diff options
Diffstat (limited to 'drivers/net/dsa')
61 files changed, 9437 insertions, 7709 deletions
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 676fb7dffe14..fe8cd5338fda 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -82,6 +82,8 @@ config NET_DSA_MV88E6060 source "drivers/net/dsa/microchip/Kconfig" +source "drivers/net/dsa/motorcomm/Kconfig" + source "drivers/net/dsa/mv88e6xxx/Kconfig" source "drivers/net/dsa/mxl862xx/Kconfig" @@ -110,10 +112,13 @@ config NET_DSA_RZN1_A5PSW config NET_DSA_KS8995 tristate "Micrel KS8995 family 5-ports 10/100 Ethernet switches" depends on SPI - select NET_DSA_TAG_NONE + select NET_DSA_MICROCHIP_KSZ_COMMON + select NET_DSA_MICROCHIP_KSZ_SPI help - This driver supports the Micrel KS8995 family of 10/100 Mbit ethernet - switches, managed over SPI. + This is a transitional option for users who had NET_DSA_KS8995 + enabled. It automatically enables the new NET_DSA_MICROCHIP_KSZ_SPI + driver which supersedes it. This option will be removed in a future + kernel release. config NET_DSA_SMSC_LAN9303 tristate @@ -168,12 +173,4 @@ config NET_DSA_VITESSE_VSC73XX_PLATFORM This enables support for the Vitesse VSC7385, VSC7388, VSC7395 and VSC7398 SparX integrated ethernet switches, connected over a CPU-attached address bus and work in memory-mapped I/O mode. - -config NET_DSA_YT921X - tristate "Motorcomm YT9215 ethernet switch chip support" - select NET_DSA_TAG_YT921X - select NET_IEEE8021Q_HELPERS if DCB - help - This enables support for the Motorcomm YT9215 ethernet switch - chip. endmenu diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 6ceb78a755d7..7e637d56b35c 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -2,7 +2,6 @@ obj-$(CONFIG_NET_DSA_BCM_SF2) += bcm-sf2.o bcm-sf2-objs := bcm_sf2.o bcm_sf2_cfp.o obj-$(CONFIG_NET_DSA_LOOP) += dsa_loop.o -obj-$(CONFIG_NET_DSA_KS8995) += ks8995.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 @@ -15,11 +14,11 @@ obj-$(CONFIG_NET_DSA_SMSC_LAN9303_MDIO) += lan9303_mdio.o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX) += vitesse-vsc73xx-core.o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_PLATFORM) += vitesse-vsc73xx-platform.o obj-$(CONFIG_NET_DSA_VITESSE_VSC73XX_SPI) += vitesse-vsc73xx-spi.o -obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o obj-y += b53/ obj-y += hirschmann/ obj-y += lantiq/ obj-y += microchip/ +obj-y += motorcomm/ obj-y += mv88e6xxx/ obj-y += mxl862xx/ obj-y += netc/ diff --git a/drivers/net/dsa/b53/b53_mdio.c b/drivers/net/dsa/b53/b53_mdio.c index 43a3b37b731b..fc1ce57a80a3 100644 --- a/drivers/net/dsa/b53/b53_mdio.c +++ b/drivers/net/dsa/b53/b53_mdio.c @@ -384,7 +384,7 @@ static const struct of_device_id b53_of_match[] = { { .compatible = "brcm,bcm5395" }, { .compatible = "brcm,bcm5397" }, { .compatible = "brcm,bcm5398" }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, b53_of_match); diff --git a/drivers/net/dsa/b53/b53_srab.c b/drivers/net/dsa/b53/b53_srab.c index b9939bbd2cd5..83a03fca142a 100644 --- a/drivers/net/dsa/b53/b53_srab.c +++ b/drivers/net/dsa/b53/b53_srab.c @@ -515,7 +515,7 @@ static const struct of_device_id b53_srab_of_match[] = { { .compatible = "brcm,cygnus-srab", .data = (void *)BCM583XX_DEVICE_ID }, { .compatible = "brcm,nsp-srab", .data = (void *)BCM58XX_DEVICE_ID }, { .compatible = "brcm,omega-srab", .data = (void *)BCM583XX_DEVICE_ID }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, b53_srab_of_match); diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index de3efa3ce9a7..9e5713015187 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1358,7 +1358,7 @@ static const struct of_device_id bcm_sf2_of_match[] = { { .compatible = "brcm,bcm7278-switch-v4.8", .data = &bcm_sf2_7278_data }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, bcm_sf2_of_match); diff --git a/drivers/net/dsa/hirschmann/hellcreek.c b/drivers/net/dsa/hirschmann/hellcreek.c index 2c6cabf61143..aec50414986e 100644 --- a/drivers/net/dsa/hirschmann/hellcreek.c +++ b/drivers/net/dsa/hirschmann/hellcreek.c @@ -2093,7 +2093,7 @@ static const struct of_device_id hellcreek_of_match[] = { .compatible = "hirschmann,hellcreek-de1soc-r1", .data = &de1soc_r1_pdata, }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, hellcreek_of_match); diff --git a/drivers/net/dsa/ks8995.c b/drivers/net/dsa/ks8995.c deleted file mode 100644 index 77d8b842693c..000000000000 --- a/drivers/net/dsa/ks8995.c +++ /dev/null @@ -1,857 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * SPI driver for Micrel/Kendin KS8995M and KSZ8864RMN ethernet switches - * - * Copyright (C) 2008 Gabor Juhos <juhosg at openwrt.org> - * Copyright (C) 2025 Linus Walleij <linus.walleij@linaro.org> - * - * This file was based on: drivers/spi/at25.c - * Copyright (C) 2006 David Brownell - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include <linux/bits.h> -#include <linux/if_bridge.h> -#include <linux/if_vlan.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/device.h> -#include <linux/gpio/consumer.h> -#include <linux/of.h> -#include <linux/spi/spi.h> -#include <net/dsa.h> - -#define DRV_VERSION "0.1.1" -#define DRV_DESC "Micrel KS8995 Ethernet switch SPI driver" - -/* ------------------------------------------------------------------------ */ - -#define KS8995_REG_ID0 0x00 /* Chip ID0 */ -#define KS8995_REG_ID1 0x01 /* Chip ID1 */ - -#define KS8995_REG_GC0 0x02 /* Global Control 0 */ - -#define KS8995_GC0_P5_PHY BIT(3) /* Port 5 PHY enabled */ - -#define KS8995_REG_GC1 0x03 /* Global Control 1 */ -#define KS8995_REG_GC2 0x04 /* Global Control 2 */ - -#define KS8995_GC2_HUGE BIT(2) /* Huge packet support */ -#define KS8995_GC2_LEGAL BIT(1) /* Legal size override */ - -#define KS8995_REG_GC3 0x05 /* Global Control 3 */ -#define KS8995_REG_GC4 0x06 /* Global Control 4 */ - -#define KS8995_GC4_10BT BIT(4) /* Force switch to 10Mbit */ -#define KS8995_GC4_MII_FLOW BIT(5) /* MII full-duplex flow control enable */ -#define KS8995_GC4_MII_HD BIT(6) /* MII half-duplex mode enable */ - -#define KS8995_REG_GC5 0x07 /* Global Control 5 */ -#define KS8995_REG_GC6 0x08 /* Global Control 6 */ -#define KS8995_REG_GC7 0x09 /* Global Control 7 */ -#define KS8995_REG_GC8 0x0a /* Global Control 8 */ -#define KS8995_REG_GC9 0x0b /* Global Control 9 */ - -#define KS8995_GC9_SPECIAL BIT(0) /* Special tagging mode (DSA) */ - -/* In DSA the ports 1-4 are numbered 0-3 and the CPU port is port 4 */ -#define KS8995_REG_PC(p, r) (0x10 + (0x10 * (p)) + (r)) /* Port Control */ -#define KS8995_REG_PS(p, r) (0x1e + (0x10 * (p)) + (r)) /* Port Status */ - -#define KS8995_REG_PC0 0x00 /* Port Control 0 */ -#define KS8995_REG_PC1 0x01 /* Port Control 1 */ -#define KS8995_REG_PC2 0x02 /* Port Control 2 */ -#define KS8995_REG_PC3 0x03 /* Port Control 3 */ -#define KS8995_REG_PC4 0x04 /* Port Control 4 */ -#define KS8995_REG_PC5 0x05 /* Port Control 5 */ -#define KS8995_REG_PC6 0x06 /* Port Control 6 */ -#define KS8995_REG_PC7 0x07 /* Port Control 7 */ -#define KS8995_REG_PC8 0x08 /* Port Control 8 */ -#define KS8995_REG_PC9 0x09 /* Port Control 9 */ -#define KS8995_REG_PC10 0x0a /* Port Control 10 */ -#define KS8995_REG_PC11 0x0b /* Port Control 11 */ -#define KS8995_REG_PC12 0x0c /* Port Control 12 */ -#define KS8995_REG_PC13 0x0d /* Port Control 13 */ - -#define KS8995_PC0_TAG_INS BIT(2) /* Enable tag insertion on port */ -#define KS8995_PC0_TAG_REM BIT(1) /* Enable tag removal on port */ -#define KS8995_PC0_PRIO_EN BIT(0) /* Enable priority handling */ - -#define KS8995_PC2_TXEN BIT(2) /* Enable TX on port */ -#define KS8995_PC2_RXEN BIT(1) /* Enable RX on port */ -#define KS8995_PC2_LEARN_DIS BIT(0) /* Disable learning on port */ - -#define KS8995_PC13_TXDIS BIT(6) /* Disable transmitter */ -#define KS8995_PC13_PWDN BIT(3) /* Power down */ - -#define KS8995_REG_TPC0 0x60 /* TOS Priority Control 0 */ -#define KS8995_REG_TPC1 0x61 /* TOS Priority Control 1 */ -#define KS8995_REG_TPC2 0x62 /* TOS Priority Control 2 */ -#define KS8995_REG_TPC3 0x63 /* TOS Priority Control 3 */ -#define KS8995_REG_TPC4 0x64 /* TOS Priority Control 4 */ -#define KS8995_REG_TPC5 0x65 /* TOS Priority Control 5 */ -#define KS8995_REG_TPC6 0x66 /* TOS Priority Control 6 */ -#define KS8995_REG_TPC7 0x67 /* TOS Priority Control 7 */ - -#define KS8995_REG_MAC0 0x68 /* MAC address 0 */ -#define KS8995_REG_MAC1 0x69 /* MAC address 1 */ -#define KS8995_REG_MAC2 0x6a /* MAC address 2 */ -#define KS8995_REG_MAC3 0x6b /* MAC address 3 */ -#define KS8995_REG_MAC4 0x6c /* MAC address 4 */ -#define KS8995_REG_MAC5 0x6d /* MAC address 5 */ - -#define KS8995_REG_IAC0 0x6e /* Indirect Access Control 0 */ -#define KS8995_REG_IAC1 0x6f /* Indirect Access Control 0 */ -#define KS8995_REG_IAD7 0x70 /* Indirect Access Data 7 */ -#define KS8995_REG_IAD6 0x71 /* Indirect Access Data 6 */ -#define KS8995_REG_IAD5 0x72 /* Indirect Access Data 5 */ -#define KS8995_REG_IAD4 0x73 /* Indirect Access Data 4 */ -#define KS8995_REG_IAD3 0x74 /* Indirect Access Data 3 */ -#define KS8995_REG_IAD2 0x75 /* Indirect Access Data 2 */ -#define KS8995_REG_IAD1 0x76 /* Indirect Access Data 1 */ -#define KS8995_REG_IAD0 0x77 /* Indirect Access Data 0 */ - -#define KSZ8864_REG_ID1 0xfe /* Chip ID in bit 7 */ - -#define KS8995_REGS_SIZE 0x80 -#define KSZ8864_REGS_SIZE 0x100 -#define KSZ8795_REGS_SIZE 0x100 - -#define ID1_CHIPID_M 0xf -#define ID1_CHIPID_S 4 -#define ID1_REVISION_M 0x7 -#define ID1_REVISION_S 1 -#define ID1_START_SW 1 /* start the switch */ - -#define FAMILY_KS8995 0x95 -#define FAMILY_KSZ8795 0x87 -#define CHIPID_M 0 -#define KS8995_CHIP_ID 0x00 -#define KSZ8864_CHIP_ID 0x01 -#define KSZ8795_CHIP_ID 0x09 - -#define KS8995_CMD_WRITE 0x02U -#define KS8995_CMD_READ 0x03U - -#define KS8995_CPU_PORT 4 -#define KS8995_NUM_PORTS 5 /* 5 ports including the CPU port */ -#define KS8995_RESET_DELAY 10 /* usec */ - -enum ks8995_chip_variant { - ks8995, - ksz8864, - ksz8795, - max_variant -}; - -struct ks8995_chip_params { - char *name; - int family_id; - int chip_id; - int regs_size; - int addr_width; - int addr_shift; -}; - -static const struct ks8995_chip_params ks8995_chip[] = { - [ks8995] = { - .name = "KS8995MA", - .family_id = FAMILY_KS8995, - .chip_id = KS8995_CHIP_ID, - .regs_size = KS8995_REGS_SIZE, - .addr_width = 8, - .addr_shift = 0, - }, - [ksz8864] = { - .name = "KSZ8864RMN", - .family_id = FAMILY_KS8995, - .chip_id = KSZ8864_CHIP_ID, - .regs_size = KSZ8864_REGS_SIZE, - .addr_width = 8, - .addr_shift = 0, - }, - [ksz8795] = { - .name = "KSZ8795CLX", - .family_id = FAMILY_KSZ8795, - .chip_id = KSZ8795_CHIP_ID, - .regs_size = KSZ8795_REGS_SIZE, - .addr_width = 12, - .addr_shift = 1, - }, -}; - -struct ks8995_switch { - struct spi_device *spi; - struct device *dev; - struct dsa_switch *ds; - struct mutex lock; - struct gpio_desc *reset_gpio; - struct bin_attribute regs_attr; - const struct ks8995_chip_params *chip; - int revision_id; - unsigned int max_mtu[KS8995_NUM_PORTS]; -}; - -static const struct spi_device_id ks8995_id[] = { - {"ks8995", ks8995}, - {"ksz8864", ksz8864}, - {"ksz8795", ksz8795}, - { } -}; -MODULE_DEVICE_TABLE(spi, ks8995_id); - -static const struct of_device_id ks8995_spi_of_match[] = { - { .compatible = "micrel,ks8995" }, - { .compatible = "micrel,ksz8864" }, - { .compatible = "micrel,ksz8795" }, - { }, -}; -MODULE_DEVICE_TABLE(of, ks8995_spi_of_match); - -static inline u8 get_chip_id(u8 val) -{ - return (val >> ID1_CHIPID_S) & ID1_CHIPID_M; -} - -static inline u8 get_chip_rev(u8 val) -{ - return (val >> ID1_REVISION_S) & ID1_REVISION_M; -} - -/* create_spi_cmd - create a chip specific SPI command header - * @ks: pointer to switch instance - * @cmd: SPI command for switch - * @address: register address for command - * - * Different chip families use different bit pattern to address the switches - * registers: - * - * KS8995: 8bit command + 8bit address - * KSZ8795: 3bit command + 12bit address + 1bit TR (?) - */ -static inline __be16 create_spi_cmd(struct ks8995_switch *ks, int cmd, - unsigned address) -{ - u16 result = cmd; - - /* make room for address (incl. address shift) */ - result <<= ks->chip->addr_width + ks->chip->addr_shift; - /* add address */ - result |= address << ks->chip->addr_shift; - /* SPI protocol needs big endian */ - return cpu_to_be16(result); -} -/* ------------------------------------------------------------------------ */ -static int ks8995_read(struct ks8995_switch *ks, char *buf, - unsigned offset, size_t count) -{ - __be16 cmd; - struct spi_transfer t[2]; - struct spi_message m; - int err; - - cmd = create_spi_cmd(ks, KS8995_CMD_READ, offset); - spi_message_init(&m); - - memset(&t, 0, sizeof(t)); - - t[0].tx_buf = &cmd; - t[0].len = sizeof(cmd); - spi_message_add_tail(&t[0], &m); - - t[1].rx_buf = buf; - t[1].len = count; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&ks->lock); - err = spi_sync(ks->spi, &m); - mutex_unlock(&ks->lock); - - return err ? err : count; -} - -static int ks8995_write(struct ks8995_switch *ks, char *buf, - unsigned offset, size_t count) -{ - __be16 cmd; - struct spi_transfer t[2]; - struct spi_message m; - int err; - - cmd = create_spi_cmd(ks, KS8995_CMD_WRITE, offset); - spi_message_init(&m); - - memset(&t, 0, sizeof(t)); - - t[0].tx_buf = &cmd; - t[0].len = sizeof(cmd); - spi_message_add_tail(&t[0], &m); - - t[1].tx_buf = buf; - t[1].len = count; - spi_message_add_tail(&t[1], &m); - - mutex_lock(&ks->lock); - err = spi_sync(ks->spi, &m); - mutex_unlock(&ks->lock); - - return err ? err : count; -} - -static inline int ks8995_read_reg(struct ks8995_switch *ks, u8 addr, u8 *buf) -{ - return ks8995_read(ks, buf, addr, 1) != 1; -} - -static inline int ks8995_write_reg(struct ks8995_switch *ks, u8 addr, u8 val) -{ - char buf = val; - - return ks8995_write(ks, &buf, addr, 1) != 1; -} - -/* ------------------------------------------------------------------------ */ - -static int ks8995_stop(struct ks8995_switch *ks) -{ - return ks8995_write_reg(ks, KS8995_REG_ID1, 0); -} - -static int ks8995_start(struct ks8995_switch *ks) -{ - return ks8995_write_reg(ks, KS8995_REG_ID1, 1); -} - -static int ks8995_reset(struct ks8995_switch *ks) -{ - int err; - - err = ks8995_stop(ks); - if (err) - return err; - - udelay(KS8995_RESET_DELAY); - - return ks8995_start(ks); -} - -/* ks8995_get_revision - get chip revision - * @ks: pointer to switch instance - * - * Verify chip family and id and get chip revision. - */ -static int ks8995_get_revision(struct ks8995_switch *ks) -{ - int err; - u8 id0, id1, ksz8864_id; - - /* read family id */ - err = ks8995_read_reg(ks, KS8995_REG_ID0, &id0); - if (err) { - err = -EIO; - goto err_out; - } - - /* verify family id */ - if (id0 != ks->chip->family_id) { - dev_err(&ks->spi->dev, "chip family id mismatch: expected 0x%02x but 0x%02x read\n", - ks->chip->family_id, id0); - err = -ENODEV; - goto err_out; - } - - switch (ks->chip->family_id) { - case FAMILY_KS8995: - /* try reading chip id at CHIP ID1 */ - err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); - if (err) { - err = -EIO; - goto err_out; - } - - /* verify chip id */ - if ((get_chip_id(id1) == CHIPID_M) && - (get_chip_id(id1) == ks->chip->chip_id)) { - /* KS8995MA */ - ks->revision_id = get_chip_rev(id1); - } else if (get_chip_id(id1) != CHIPID_M) { - /* KSZ8864RMN */ - err = ks8995_read_reg(ks, KS8995_REG_ID1, &ksz8864_id); - if (err) { - err = -EIO; - goto err_out; - } - - if ((ksz8864_id & 0x80) && - (ks->chip->chip_id == KSZ8864_CHIP_ID)) { - ks->revision_id = get_chip_rev(id1); - } - - } else { - dev_err(&ks->spi->dev, "unsupported chip id for KS8995 family: 0x%02x\n", - id1); - err = -ENODEV; - } - break; - case FAMILY_KSZ8795: - /* try reading chip id at CHIP ID1 */ - err = ks8995_read_reg(ks, KS8995_REG_ID1, &id1); - if (err) { - err = -EIO; - goto err_out; - } - - if (get_chip_id(id1) == ks->chip->chip_id) { - ks->revision_id = get_chip_rev(id1); - } else { - dev_err(&ks->spi->dev, "unsupported chip id for KSZ8795 family: 0x%02x\n", - id1); - err = -ENODEV; - } - break; - default: - dev_err(&ks->spi->dev, "unsupported family id: 0x%02x\n", id0); - err = -ENODEV; - break; - } -err_out: - return err; -} - -static int ks8995_check_config(struct ks8995_switch *ks) -{ - int ret; - u8 val; - - ret = ks8995_read_reg(ks, KS8995_REG_GC0, &val); - if (ret) { - dev_err(ks->dev, "failed to read KS8995_REG_GC0\n"); - return ret; - } - - dev_dbg(ks->dev, "port 5 PHY %senabled\n", - (val & KS8995_GC0_P5_PHY) ? "" : "not "); - - val |= KS8995_GC0_P5_PHY; - ret = ks8995_write_reg(ks, KS8995_REG_GC0, val); - if (ret) - dev_err(ks->dev, "failed to set KS8995_REG_GC0\n"); - - dev_dbg(ks->dev, "set KS8995_REG_GC0 to 0x%02x\n", val); - - return 0; -} - -static void -ks8995_mac_config(struct phylink_config *config, unsigned int mode, - const struct phylink_link_state *state) -{ -} - -static void -ks8995_mac_link_up(struct phylink_config *config, struct phy_device *phydev, - unsigned int mode, phy_interface_t interface, - int speed, int duplex, bool tx_pause, bool rx_pause) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct ks8995_switch *ks = dp->ds->priv; - int port = dp->index; - int ret; - u8 val; - - /* Allow forcing the mode on the fixed CPU port, no autonegotiation. - * We assume autonegotiation works on the PHY-facing ports. - */ - if (port != KS8995_CPU_PORT) - return; - - dev_dbg(ks->dev, "MAC link up on CPU port (%d)\n", port); - - ret = ks8995_read_reg(ks, KS8995_REG_GC4, &val); - if (ret) { - dev_err(ks->dev, "failed to read KS8995_REG_GC4\n"); - return; - } - - /* Conjure port config */ - switch (speed) { - case SPEED_10: - dev_dbg(ks->dev, "set switch MII to 100Mbit mode\n"); - val |= KS8995_GC4_10BT; - break; - case SPEED_100: - default: - dev_dbg(ks->dev, "set switch MII to 100Mbit mode\n"); - val &= ~KS8995_GC4_10BT; - break; - } - - if (duplex == DUPLEX_HALF) { - dev_dbg(ks->dev, "set switch MII to half duplex\n"); - val |= KS8995_GC4_MII_HD; - } else { - dev_dbg(ks->dev, "set switch MII to full duplex\n"); - val &= ~KS8995_GC4_MII_HD; - } - - dev_dbg(ks->dev, "set KS8995_REG_GC4 to %02x\n", val); - - /* Enable the CPU port */ - ret = ks8995_write_reg(ks, KS8995_REG_GC4, val); - if (ret) - dev_err(ks->dev, "failed to set KS8995_REG_GC4\n"); -} - -static void -ks8995_mac_link_down(struct phylink_config *config, unsigned int mode, - phy_interface_t interface) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct ks8995_switch *ks = dp->ds->priv; - int port = dp->index; - - if (port != KS8995_CPU_PORT) - return; - - dev_dbg(ks->dev, "MAC link down on CPU port (%d)\n", port); - - /* Disable the CPU port */ -} - -static const struct phylink_mac_ops ks8995_phylink_mac_ops = { - .mac_config = ks8995_mac_config, - .mac_link_up = ks8995_mac_link_up, - .mac_link_down = ks8995_mac_link_down, -}; - -static enum -dsa_tag_protocol ks8995_get_tag_protocol(struct dsa_switch *ds, - int port, - enum dsa_tag_protocol mp) -{ - /* This switch actually uses the 6 byte KS8995 protocol */ - return DSA_TAG_PROTO_NONE; -} - -static int ks8995_setup(struct dsa_switch *ds) -{ - return 0; -} - -static int ks8995_port_enable(struct dsa_switch *ds, int port, - struct phy_device *phy) -{ - struct ks8995_switch *ks = ds->priv; - - dev_dbg(ks->dev, "enable port %d\n", port); - - return 0; -} - -static void ks8995_port_disable(struct dsa_switch *ds, int port) -{ - struct ks8995_switch *ks = ds->priv; - - dev_dbg(ks->dev, "disable port %d\n", port); -} - -static int ks8995_port_pre_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) -{ - /* We support enabling/disabling learning */ - if (flags.mask & ~(BR_LEARNING)) - return -EINVAL; - - return 0; -} - -static int ks8995_port_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) -{ - struct ks8995_switch *ks = ds->priv; - int ret; - u8 val; - - if (flags.mask & BR_LEARNING) { - ret = ks8995_read_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), &val); - if (ret) { - dev_err(ks->dev, "failed to read KS8995_REG_PC2 on port %d\n", port); - return ret; - } - - if (flags.val & BR_LEARNING) - val &= ~KS8995_PC2_LEARN_DIS; - else - val |= KS8995_PC2_LEARN_DIS; - - ret = ks8995_write_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), val); - if (ret) { - dev_err(ks->dev, "failed to write KS8995_REG_PC2 on port %d\n", port); - return ret; - } - } - - return 0; -} - -static void ks8995_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) -{ - struct ks8995_switch *ks = ds->priv; - int ret; - u8 val; - - ret = ks8995_read_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), &val); - if (ret) { - dev_err(ks->dev, "failed to read KS8995_REG_PC2 on port %d\n", port); - return; - } - - /* Set the bits for the different STP states in accordance with - * the datasheet, pages 36-37 "Spanning tree support". - */ - switch (state) { - case BR_STATE_DISABLED: - case BR_STATE_BLOCKING: - case BR_STATE_LISTENING: - val &= ~KS8995_PC2_TXEN; - val &= ~KS8995_PC2_RXEN; - val |= KS8995_PC2_LEARN_DIS; - break; - case BR_STATE_LEARNING: - val &= ~KS8995_PC2_TXEN; - val &= ~KS8995_PC2_RXEN; - val &= ~KS8995_PC2_LEARN_DIS; - break; - case BR_STATE_FORWARDING: - val |= KS8995_PC2_TXEN; - val |= KS8995_PC2_RXEN; - val &= ~KS8995_PC2_LEARN_DIS; - break; - default: - dev_err(ks->dev, "unknown bridge state requested\n"); - return; - } - - ret = ks8995_write_reg(ks, KS8995_REG_PC(port, KS8995_REG_PC2), val); - if (ret) { - dev_err(ks->dev, "failed to write KS8995_REG_PC2 on port %d\n", port); - return; - } - - dev_dbg(ks->dev, "set KS8995_REG_PC2 for port %d to %02x\n", port, val); -} - -static void ks8995_phylink_get_caps(struct dsa_switch *dsa, int port, - struct phylink_config *config) -{ - unsigned long *interfaces = config->supported_interfaces; - - if (port == KS8995_CPU_PORT) - __set_bit(PHY_INTERFACE_MODE_MII, interfaces); - - if (port <= 3) { - /* Internal PHYs */ - __set_bit(PHY_INTERFACE_MODE_INTERNAL, interfaces); - /* phylib default */ - __set_bit(PHY_INTERFACE_MODE_MII, interfaces); - } - - config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100; -} - -/* Huge packet support up to 1916 byte packages "inclusive" - * which means that tags are included. If the bit is not set - * it is 1536 bytes "inclusive". We present the length without - * tags or ethernet headers. The setting affects all ports. - */ -static int ks8995_change_mtu(struct dsa_switch *ds, int port, int new_mtu) -{ - struct ks8995_switch *ks = ds->priv; - unsigned int max_mtu; - int ret; - u8 val; - int i; - - ks->max_mtu[port] = new_mtu; - - /* Roof out the MTU for the entire switch to the greatest - * common denominator: the biggest set for any one port will - * be the biggest MTU for the switch. - */ - max_mtu = ETH_DATA_LEN; - for (i = 0; i < KS8995_NUM_PORTS; i++) { - if (ks->max_mtu[i] > max_mtu) - max_mtu = ks->max_mtu[i]; - } - - /* Translate to layer 2 size. - * Add ethernet and (possible) VLAN headers, and checksum to the size. - * For ETH_DATA_LEN (1500 bytes) this will add up to 1522 bytes. - */ - max_mtu += VLAN_ETH_HLEN; - max_mtu += ETH_FCS_LEN; - - ret = ks8995_read_reg(ks, KS8995_REG_GC2, &val); - if (ret) { - dev_err(ks->dev, "failed to read KS8995_REG_GC2\n"); - return ret; - } - - if (max_mtu <= 1522) { - val &= ~KS8995_GC2_HUGE; - val &= ~KS8995_GC2_LEGAL; - } else if (max_mtu > 1522 && max_mtu <= 1536) { - /* This accepts packets up to 1536 bytes */ - val &= ~KS8995_GC2_HUGE; - val |= KS8995_GC2_LEGAL; - } else { - /* This accepts packets up to 1916 bytes */ - val |= KS8995_GC2_HUGE; - val |= KS8995_GC2_LEGAL; - } - - dev_dbg(ks->dev, "new max MTU %d bytes (inclusive)\n", max_mtu); - - ret = ks8995_write_reg(ks, KS8995_REG_GC2, val); - if (ret) - dev_err(ks->dev, "failed to set KS8995_REG_GC2\n"); - - return ret; -} - -static int ks8995_get_max_mtu(struct dsa_switch *ds, int port) -{ - return 1916 - ETH_HLEN - ETH_FCS_LEN; -} - -static const struct dsa_switch_ops ks8995_ds_ops = { - .get_tag_protocol = ks8995_get_tag_protocol, - .setup = ks8995_setup, - .port_pre_bridge_flags = ks8995_port_pre_bridge_flags, - .port_bridge_flags = ks8995_port_bridge_flags, - .port_enable = ks8995_port_enable, - .port_disable = ks8995_port_disable, - .port_stp_state_set = ks8995_port_stp_state_set, - .port_change_mtu = ks8995_change_mtu, - .port_max_mtu = ks8995_get_max_mtu, - .phylink_get_caps = ks8995_phylink_get_caps, -}; - -/* ------------------------------------------------------------------------ */ -static int ks8995_probe(struct spi_device *spi) -{ - struct ks8995_switch *ks; - int err; - int variant = spi_get_device_id(spi)->driver_data; - - if (variant >= max_variant) { - dev_err(&spi->dev, "bad chip variant %d\n", variant); - return -ENODEV; - } - - ks = devm_kzalloc(&spi->dev, sizeof(*ks), GFP_KERNEL); - if (!ks) - return -ENOMEM; - - mutex_init(&ks->lock); - ks->spi = spi; - ks->dev = &spi->dev; - ks->chip = &ks8995_chip[variant]; - - ks->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset", - GPIOD_OUT_HIGH); - err = PTR_ERR_OR_ZERO(ks->reset_gpio); - if (err) { - dev_err(&spi->dev, - "failed to get reset gpio: %d\n", err); - return err; - } - - err = gpiod_set_consumer_name(ks->reset_gpio, "switch-reset"); - if (err) - return err; - - if (ks->reset_gpio) { - /* - * If a reset line was obtained, wait for 100us after - * de-asserting RESET before accessing any registers, see - * the KS8995MA datasheet, page 44. - */ - gpiod_set_value_cansleep(ks->reset_gpio, 0); - udelay(100); - } - - spi_set_drvdata(spi, ks); - - spi->mode = SPI_MODE_0; - spi->bits_per_word = 8; - err = spi_setup(spi); - if (err) { - dev_err(&spi->dev, "spi_setup failed, err=%d\n", err); - return err; - } - - err = ks8995_get_revision(ks); - if (err) - return err; - - err = ks8995_reset(ks); - if (err) - return err; - - dev_info(&spi->dev, "%s device found, Chip ID:%x, Revision:%x\n", - ks->chip->name, ks->chip->chip_id, ks->revision_id); - - err = ks8995_check_config(ks); - if (err) - return err; - - ks->ds = devm_kzalloc(&spi->dev, sizeof(*ks->ds), GFP_KERNEL); - if (!ks->ds) - return -ENOMEM; - - ks->ds->dev = &spi->dev; - ks->ds->num_ports = KS8995_NUM_PORTS; - ks->ds->ops = &ks8995_ds_ops; - ks->ds->phylink_mac_ops = &ks8995_phylink_mac_ops; - ks->ds->priv = ks; - - err = dsa_register_switch(ks->ds); - if (err) - return dev_err_probe(&spi->dev, err, - "unable to register DSA switch\n"); - - return 0; -} - -static void ks8995_remove(struct spi_device *spi) -{ - struct ks8995_switch *ks = spi_get_drvdata(spi); - - dsa_unregister_switch(ks->ds); - /* assert reset */ - gpiod_set_value_cansleep(ks->reset_gpio, 1); -} - -/* ------------------------------------------------------------------------ */ -static struct spi_driver ks8995_driver = { - .driver = { - .name = "spi-ks8995", - .of_match_table = ks8995_spi_of_match, - }, - .probe = ks8995_probe, - .remove = ks8995_remove, - .id_table = ks8995_id, -}; - -module_spi_driver(ks8995_driver); - -MODULE_DESCRIPTION(DRV_DESC); -MODULE_VERSION(DRV_VERSION); -MODULE_AUTHOR("Gabor Juhos <juhosg at openwrt.org>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/dsa/lan9303_i2c.c b/drivers/net/dsa/lan9303_i2c.c index 3a09f3907f44..7b70e22371fc 100644 --- a/drivers/net/dsa/lan9303_i2c.c +++ b/drivers/net/dsa/lan9303_i2c.c @@ -96,7 +96,7 @@ MODULE_DEVICE_TABLE(i2c, lan9303_i2c_id); static const struct of_device_id lan9303_i2c_of_match[] = { { .compatible = "smsc,lan9303-i2c", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lan9303_i2c_of_match); diff --git a/drivers/net/dsa/lan9303_mdio.c b/drivers/net/dsa/lan9303_mdio.c index 0ac4857e5ee8..d3b5062905b1 100644 --- a/drivers/net/dsa/lan9303_mdio.c +++ b/drivers/net/dsa/lan9303_mdio.c @@ -157,7 +157,7 @@ static void lan9303_mdio_shutdown(struct mdio_device *mdiodev) static const struct of_device_id lan9303_mdio_of_match[] = { { .compatible = "smsc,lan9303-mdio" }, { .compatible = "microchip,lan9354-mdio" }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, lan9303_mdio_of_match); diff --git a/drivers/net/dsa/lantiq/lantiq_gswip.h b/drivers/net/dsa/lantiq/lantiq_gswip.h index 0b75be14dc10..4f95877c59d1 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip.h +++ b/drivers/net/dsa/lantiq/lantiq_gswip.h @@ -261,6 +261,8 @@ struct gswip_hw_info { const struct gswip_pce_microcode (*pce_microcode)[]; size_t pce_microcode_size; enum dsa_tag_protocol tag_protocol; + + int (*setup)(struct dsa_switch *ds); void (*phylink_get_caps)(struct dsa_switch *ds, int port, struct phylink_config *config); struct phylink_pcs *(*mac_select_pcs)(struct phylink_config *config, diff --git a/drivers/net/dsa/lantiq/lantiq_gswip_common.c b/drivers/net/dsa/lantiq/lantiq_gswip_common.c index 0e8eedf64d3a..5bb5f4ab4cbf 100644 --- a/drivers/net/dsa/lantiq/lantiq_gswip_common.c +++ b/drivers/net/dsa/lantiq/lantiq_gswip_common.c @@ -689,6 +689,16 @@ static int gswip_setup(struct dsa_switch *ds) */ regmap_write(priv->mdio, GSWIP_MDIO_MDC_CFG0, 0x0); + /* GSW1xx will wake up the PHYs here, so it makes sense that it happens + * after the auto-polling deactivation above, but before the MDIO bus + * registration below + */ + if (priv->hw_info->setup) { + err = priv->hw_info->setup(ds); + if (err) + return err; + } + /* Configure the MDIO Clock 2.5 MHz */ regmap_write_bits(priv->mdio, GSWIP_MDIO_MDC_CFG1, 0xff, 0x09); @@ -1279,12 +1289,8 @@ static int gswip_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) /* Enable MLEN for ports with non-standard MTUs, including the special * header on the CPU port added above. */ - if (new_mtu != ETH_DATA_LEN) - regmap_set_bits(priv->gswip, GSWIP_MAC_CTRL_2p(port), - GSWIP_MAC_CTRL_2_MLEN); - else - regmap_clear_bits(priv->gswip, GSWIP_MAC_CTRL_2p(port), - GSWIP_MAC_CTRL_2_MLEN); + regmap_assign_bits(priv->gswip, GSWIP_MAC_CTRL_2p(port), + GSWIP_MAC_CTRL_2_MLEN, new_mtu != ETH_DATA_LEN); return 0; } @@ -1339,6 +1345,7 @@ static void gswip_port_set_speed(struct gswip_priv *priv, int port, int speed, break; case SPEED_1000: + case SPEED_2500: mdio_phy = GSWIP_MDIO_PHY_SPEED_G1; mii_cfg = GSWIP_MII_CFG_RATE_M125; diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.c b/drivers/net/dsa/lantiq/mxl-gsw1xx.c index a1104b2f92a9..a26f78e1a44c 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.c +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.c @@ -352,8 +352,22 @@ static int gsw1xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, /* mark PCS configuration as incomplete */ priv->tbi_interface = PHY_INTERFACE_MODE_NA; - if (!reconf) + if (!reconf) { + /* setup SerDes clock speed */ + if (interface == PHY_INTERFACE_MODE_2500BASEX) + nco_ctrl = GSW1XX_SGMII_2G5 | GSW1XX_SGMII_2G5_NCO2; + else + nco_ctrl = GSW1XX_SGMII_1G | GSW1XX_SGMII_1G_NCO1; + + ret = regmap_update_bits(priv->clk, GSW1XX_CLK_NCO_CTRL, + GSW1XX_SGMII_HSP_MASK | + GSW1XX_SGMII_SEL, + nco_ctrl); + if (ret) + return ret; + ret = gsw1xx_pcs_reset(priv, interface); + } if (ret) return ret; @@ -423,19 +437,6 @@ static int gsw1xx_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, return ret; if (!reconf) { - /* setup SerDes clock speed */ - if (interface == PHY_INTERFACE_MODE_2500BASEX) - nco_ctrl = GSW1XX_SGMII_2G5 | GSW1XX_SGMII_2G5_NCO2; - else - nco_ctrl = GSW1XX_SGMII_1G | GSW1XX_SGMII_1G_NCO1; - - ret = regmap_update_bits(priv->clk, GSW1XX_CLK_NCO_CTRL, - GSW1XX_SGMII_HSP_MASK | - GSW1XX_SGMII_SEL, - nco_ctrl); - if (ret) - return ret; - ret = gsw1xx_pcs_phy_xaui_write(priv, 0x30, 0x80); if (ret) return ret; @@ -588,6 +589,37 @@ static void gsw150_phylink_get_caps(struct dsa_switch *ds, int port, gsw1xx_phylink_get_lpi_caps(config); } +static int gsw1xx_setup(struct dsa_switch *ds) +{ + struct gsw1xx_priv *priv = container_of(ds->priv, struct gsw1xx_priv, gswip); + struct gswip_priv *gswip_priv = ds->priv; + u32 active_mask = 0; + u32 phy_mask = 0; + int port, ret; + + for (port = 0; port < gswip_priv->hw_info->max_ports; port++) { + struct phylink_config cfg = {}; + + gswip_priv->hw_info->phylink_get_caps(ds, port, &cfg); + if (!test_bit(PHY_INTERFACE_MODE_INTERNAL, cfg.supported_interfaces)) + break; + + phy_mask |= GSW1XX_RST_REQ_PHY(port); + + if (!dsa_is_unused_port(ds, port)) + active_mask |= GSW1XX_RST_REQ_PHY(port); + } + + ret = regmap_update_bits(priv->shell, GSW1XX_SHELL_RST_REQ, phy_mask, ~active_mask); + if (ret) + return ret; + + if (active_mask) + msleep(300); + + return 0; +} + static struct phylink_pcs *gsw1xx_phylink_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { @@ -829,6 +861,7 @@ static const struct gswip_hw_info gsw12x_data = { [GSW1XX_MII_PORT] = GSWIP_MII_PCDU0, [GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1, }, + .setup = gsw1xx_setup, .mac_select_pcs = gsw1xx_phylink_mac_select_pcs, .phylink_get_caps = &gsw1xx_phylink_get_caps, .supports_2500m = true, @@ -851,6 +884,7 @@ static const struct gswip_hw_info gsw140_data = { [GSW1XX_MII_PORT] = GSWIP_MII_PCDU0, [GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1, }, + .setup = gsw1xx_setup, .mac_select_pcs = gsw1xx_phylink_mac_select_pcs, .phylink_get_caps = &gsw1xx_phylink_get_caps, .supports_2500m = true, @@ -873,6 +907,7 @@ static const struct gswip_hw_info gsw141_data = { [GSW1XX_MII_PORT] = GSWIP_MII_PCDU0, [GSW1XX_MII_PORT + 1 ... GSWIP_MAX_PORTS - 1] = -1, }, + .setup = gsw1xx_setup, .mac_select_pcs = gsw1xx_phylink_mac_select_pcs, .phylink_get_caps = gsw1xx_phylink_get_caps, .port_setup = gsw1xx_port_setup, @@ -894,6 +929,7 @@ static const struct gswip_hw_info gsw150_data = { [5] = 1, [6] = 11, }, + .setup = gsw1xx_setup, .phylink_get_caps = gsw150_phylink_get_caps, /* There is only a single RGMII_SLEW_CFG register in GSW150 and it is * unknown if RGMII slew configuration affects both RGMII ports @@ -918,7 +954,7 @@ static const struct of_device_id gsw1xx_of_match[] = { { .compatible = "maxlinear,gsw140", .data = &gsw140_data }, { .compatible = "maxlinear,gsw141", .data = &gsw141_data }, { .compatible = "maxlinear,gsw145", .data = &gsw140_data }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, gsw1xx_of_match); diff --git a/drivers/net/dsa/lantiq/mxl-gsw1xx.h b/drivers/net/dsa/lantiq/mxl-gsw1xx.h index caa8f1008587..81bc00eabbfb 100644 --- a/drivers/net/dsa/lantiq/mxl-gsw1xx.h +++ b/drivers/net/dsa/lantiq/mxl-gsw1xx.h @@ -110,6 +110,8 @@ #define GSW1XX_SHELL_BASE 0xfa00 #define GSW1XX_SHELL_RST_REQ 0x01 #define GSW1XX_RST_REQ_SGMII_SHELL BIT(5) +/* Valid only for internal-PHY ports (vary per switch type) */ +#define GSW1XX_RST_REQ_PHY(p) BIT(p) #define GSW1XX_SHELL_MANU_ID 0x10 #define GSW1XX_SHELL_MANU_ID_PNUML GENMASK(15, 12) #define GSW1XX_SHELL_MANU_ID_MANID GENMASK(11, 1) diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index c71d3fd5dfeb..75c9b2114afd 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -2,6 +2,7 @@ menuconfig NET_DSA_MICROCHIP_KSZ_COMMON tristate "Microchip KSZ8XXX/KSZ9XXX/LAN937X series switch support" depends on NET_DSA + select NET_DSA_TAG_KS8995 select NET_DSA_TAG_KSZ select NET_DSA_TAG_NONE select NET_IEEE8021Q_HELPERS diff --git a/drivers/net/dsa/microchip/ksz8.c b/drivers/net/dsa/microchip/ksz8.c index 941ae9f66f70..3c96dd4581cf 100644 --- a/drivers/net/dsa/microchip/ksz8.c +++ b/drivers/net/dsa/microchip/ksz8.c @@ -3,6 +3,7 @@ * Microchip KSZ8XXX series switch driver * * It supports the following switches: + * - KSZ8995XA (the oldest ancestor) * - KSZ8463 * - KSZ8863, KSZ8873 aka KSZ88X3 * - KSZ8895, KSZ8864 aka KSZ8895 family @@ -20,6 +21,7 @@ #include <linux/export.h> #include <linux/gpio/consumer.h> #include <linux/if_vlan.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_data/microchip-ksz.h> @@ -253,7 +255,35 @@ static int ksz8463_reset_switch(struct ksz_device *dev) static int ksz8_reset_switch(struct ksz_device *dev) { - if (ksz_is_ksz88x3(dev)) { + if (ksz_is_ksz8995xa(dev)) { + unsigned int port, reg, val; + int ret; + + /* The KSZ8995XA switch itself cannot be reset by software, but + * often boot loaders have fun with ports, so reset them. This + * reset bit is self-clearing. + */ + for (port = 0; port < dev->info->port_cnt; port++) { + ret = ksz_prmw8(dev, port, REG_PORT_STATUS_3, + PORT_PHY_SOFT_RESET, + PORT_PHY_SOFT_RESET); + if (ret) + return ret; + } + + /* IEEE 802.3 allows up to 500 ms for a PHY reset. */ + for (port = 0; port < dev->info->port_cnt; port++) { + reg = dev->dev_ops->get_port_addr(port, REG_PORT_STATUS_3); + ret = regmap_read_poll_timeout(ksz_regmap_8(dev), reg, val, + !(val & PORT_PHY_SOFT_RESET), + 1000, 600000); + if (ret) + return ret; + } + + /* Some PHYs need additional settling time after reset. */ + usleep_range(1000, 2000); + } else if (ksz_is_ksz88x3(dev)) { /* reset switch */ ksz_cfg(dev, KSZ8863_REG_SW_RESET, KSZ8863_GLOBAL_SOFTWARE_RESET | KSZ8863_PCS_RESET, true); @@ -269,6 +299,31 @@ static int ksz8_reset_switch(struct ksz_device *dev) return 0; } +static int ksz8995xa_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ + struct ksz_device *dev = ds->priv; + int frame_size; + u8 ctrl2 = 0; + + if (!dsa_is_cpu_port(dev->ds, port)) + return 0; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + /* The legal packet size bit polarity documented in the KSZ8995XA + * manual appears to be wrong. Practical tests show that the same + * semantics as the other switches are correct; using the documented + * polarity makes the switch hang on big packets. + */ + if (frame_size <= KSZ8995XA_LEGAL_PACKET_SIZE) + ctrl2 |= KSZ8863_LEGAL_PACKET_ENABLE; + else if (frame_size > KSZ8863_NORMAL_PACKET_SIZE) + ctrl2 |= KSZ8863_HUGE_PACKET_ENABLE; + + return ksz_rmw8(dev, REG_SW_CTRL_2, KSZ8863_LEGAL_PACKET_ENABLE | + KSZ8863_HUGE_PACKET_ENABLE, ctrl2); +} + static int ksz88xx_change_mtu(struct dsa_switch *ds, int port, int mtu) { struct ksz_device *dev = ds->priv; @@ -325,8 +380,8 @@ static int ksz88xx_max_mtu(struct dsa_switch *ds, int port) static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) { - u8 mask_4q, mask_2q; - u8 reg_4q, reg_2q; + u8 mask_4q = 0, mask_2q; + u8 reg_4q = 0, reg_2q; u8 data_4q = 0; u8 data_2q = 0; int ret; @@ -352,6 +407,10 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) mask_2q = KSZ8873_PORT_2QUEUE_SPLIT_EN; reg_4q = P1CR1; reg_2q = P1CR1 + 1; + } else if (ksz_is_ksz8995xa(dev)) { + /* This switch has no 4way split support */ + mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN; + reg_2q = REG_PORT_CTRL_0; } else { mask_4q = KSZ8795_PORT_4QUEUE_SPLIT_EN; mask_2q = KSZ8795_PORT_2QUEUE_SPLIT_EN; @@ -373,9 +432,11 @@ static int ksz8_port_queue_split(struct ksz_device *dev, int port, int queues) else if (queues == 2) data_2q = mask_2q; - ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q); - if (ret) - return ret; + if (mask_4q) { + ret = ksz_prmw8(dev, port, reg_4q, mask_4q, data_4q); + if (ret) + return ret; + } return ksz_prmw8(dev, port, reg_2q, mask_2q, data_2q); } @@ -970,6 +1031,10 @@ static int ksz8_r_phy_ctrl(struct ksz_device *dev, int port, u16 *val) if (reg_val & PORT_MDIX_STATUS) *val |= KSZ886X_CTRL_MDIX_STAT; + /* KSZ8995XA has no fancy features in register 0xA */ + if (ksz_is_ksz8995xa(dev)) + return 0; + ret = ksz_pread8(dev, port, REG_PORT_LINK_MD_CTRL, ®_val); if (ret < 0) return ret; @@ -1068,8 +1133,10 @@ static int ksz8_r_phy_bmcr(struct ksz_device *dev, u16 port, u16 *val) if (ctrl & PORT_FORCE_FULL_DUPLEX) *val |= BMCR_FULLDPLX; - if (speed & PORT_HP_MDIX) - *val |= KSZ886X_BMCR_HP_MDIX; + if (!ksz_is_ksz8995xa(dev)) { + if (speed & PORT_HP_MDIX) + *val |= KSZ886X_BMCR_HP_MDIX; + } if (restart & PORT_FORCE_MDIX) *val |= KSZ886X_BMCR_FORCE_MDI; @@ -1164,6 +1231,9 @@ static int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data |= LPA_LPACK; break; case PHY_REG_LINK_MD: + if (ksz_is_ksz8995xa(dev)) + return -EOPNOTSUPP; + ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1); if (ret) return ret; @@ -1307,13 +1377,15 @@ static int ksz8_w_phy_bmcr(struct ksz_device *dev, u16 port, u16 val) if (val & BMCR_RESET) return 0; - speed = 0; - if (val & KSZ886X_BMCR_HP_MDIX) - speed |= PORT_HP_MDIX; + if (!ksz_is_ksz8995xa(dev)) { + speed = 0; + if (val & KSZ886X_BMCR_HP_MDIX) + speed |= PORT_HP_MDIX; - ret = ksz_prmw8(dev, port, regs[P_SPEED_STATUS], PORT_HP_MDIX, speed); - if (ret) - return ret; + ret = ksz_prmw8(dev, port, regs[P_SPEED_STATUS], PORT_HP_MDIX, speed); + if (ret) + return ret; + } ctrl = 0; if (ksz_is_ksz88x3(dev)) { @@ -1423,11 +1495,17 @@ static int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) } break; case PHY_REG_LINK_MD: + if (ksz_is_ksz8995xa(dev)) + return -EOPNOTSUPP; + if (val & PHY_START_CABLE_DIAG) ksz_port_cfg(dev, p, REG_PORT_LINK_MD_CTRL, PORT_START_CABLE_DIAG, true); break; case PHY_REG_PHY_CTRL: + if (ksz_is_ksz8995xa(dev)) + return -EOPNOTSUPP; + ret = ksz8_w_phy_ctrl(dev, p, val); if (ret) return ret; @@ -1527,8 +1605,18 @@ static void ksz8_flush_dyn_mac_table(struct dsa_switch *ds, int port) if (ksz_is_ksz8463(dev)) { reg = KSZ8463_FLUSH_TABLE_CTRL; mask = KSZ8463_FLUSH_DYN_MAC_TABLE; + } else if (ksz_is_ksz8995xa(dev)) { + reg = REG_SW_CTRL_1; + mask = SW_FAST_AGING; } ksz_cfg(dev, reg, mask, true); + if (ksz_is_ksz8995xa(dev)) { + /* Allow the 800 us fast-age cycle to finish before restoring + * normal ageing and port learning. + */ + usleep_range(1000, 2000); + ksz_cfg(dev, reg, mask, false); + } for (index = first; index < cnt; index++) { if (!(learn[index] & PORT_LEARN_DISABLE)) ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]); @@ -2043,12 +2131,14 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz8_port_queue_split(dev, port, dev->info->num_tx_queues); - /* replace priority */ - offset = P_802_1P_CTRL; - if (ksz_is_ksz8463(dev)) - offset = P1CR2; - ksz_port_cfg(dev, port, offset, - masks[PORT_802_1P_REMAPPING], false); + if (!ksz_is_ksz8995xa(dev)) { + /* replace priority */ + offset = P_802_1P_CTRL; + if (ksz_is_ksz8463(dev)) + offset = P1CR2; + ksz_port_cfg(dev, port, offset, + masks[PORT_802_1P_REMAPPING], false); + } if (cpu_port) member = dsa_user_ports(ds); @@ -2057,6 +2147,20 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz8_cfg_port_member(dev, port, member); + if (ksz_is_ksz8995xa(dev)) { + /* Use VID 0 to identify untagged frames in the special tag */ + ksz_pwrite16(dev, port, REG_PORT_CTRL_VID, 0); + + /* The KSZ8995XA has a special tag format in the front of the frame + * that need to be inserted by the CPU and then removed by each + * port. PORT_REMOVE_TAG simply means "remove tags coming from the + * CPU port" it does not affect ingress packets. + */ + ksz_prmw8(dev, port, REG_PORT_CTRL_0, + PORT_INSERT_TAG | PORT_REMOVE_TAG, + cpu_port ? PORT_INSERT_TAG : PORT_REMOVE_TAG); + } + /* Disable all WoL options by default. Otherwise * ksz_switch_macaddr_get/put logic will not work properly. * CPU port 4 has no WoL functionality. @@ -2073,6 +2177,7 @@ static int ksz8_dsa_port_setup(struct dsa_switch *ds, int port) return 0; ksz8_port_setup(dev, port, false); + return ksz_dcb_init_port(dev, port); } @@ -2147,7 +2252,9 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds) masks = dev->info->masks; regs = dev->info->regs; - ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true); + /* KSZ8995XA uses a tag in the header instead of the tail */ + if (!ksz_is_ksz8995xa(dev)) + ksz_cfg(dev, regs[S_TAIL_TAG_CTRL], masks[SW_TAIL_TAG_ENABLE], true); ksz8_port_setup(dev, dev->cpu_port, true); @@ -2323,6 +2430,10 @@ static int ksz8_enable_stp_addr(struct ksz_device *dev) { struct alu_struct alu; + /* KSZ8995XA lacks STP */ + if (ksz_is_ksz8995xa(dev)) + return 0; + /* Setup STP address for STP operation. */ memset(&alu, 0, sizeof(alu)); ether_addr_copy(alu.mac, eth_stp_addr); @@ -2478,6 +2589,7 @@ static int ksz8463_setup(struct dsa_switch *ds) if (ret) goto free_girq; + ksz8463_ptp_set_caps(ds); ret = ksz_ptp_clock_register(ds); if (ret) { dev_err(dev->dev, "Failed to register PTP clock: %d\n", @@ -2650,13 +2762,17 @@ static int ksz8_setup(struct dsa_switch *ds) struct ksz_port *p; const u16 *regs; int i, ret; + u8 val; regs = dev->info->regs; - dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->info->num_vlans, GFP_KERNEL); - if (!dev->vlan_cache) - return -ENOMEM; + /* KSZ8995XA has no SW controlled VLAN handling */ + if (!ksz_is_ksz8995xa(dev)) { + dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), + dev->info->num_vlans, GFP_KERNEL); + if (!dev->vlan_cache) + return -ENOMEM; + } ret = ksz8_reset_switch(dev); if (ret) { @@ -2685,19 +2801,22 @@ static int ksz8_setup(struct dsa_switch *ds) ksz_init_mib_timer(dev); - ds->configure_vlan_while_not_filtering = false; ds->dscp_prio_mapping_is_global = true; ds->mtu_enforcement_ingress = true; - /* We rely on software untagging on the CPU port, so that we - * can support both tagged and untagged VLANs - */ - ds->untag_bridge_pvid = true; + if (!ksz_is_ksz8995xa(dev)) { + ds->configure_vlan_while_not_filtering = false; - /* VLAN filtering is partly controlled by the global VLAN - * Enable flag - */ - ds->vlan_filtering_is_global = true; + /* We rely on software untagging on the CPU port, so that we + * can support both tagged and untagged VLANs + */ + ds->untag_bridge_pvid = true; + + /* VLAN filtering is partly controlled by the global VLAN + * Enable flag + */ + ds->vlan_filtering_is_global = true; + } /* Enable automatic fast aging when link changed detected. */ ksz_cfg(dev, S_LINK_AGING_CTRL, SW_LINK_AUTO_AGING, true); @@ -2711,9 +2830,10 @@ static int ksz8_setup(struct dsa_switch *ds) * Make sure unicast VLAN boundary is set as default and * enable no excessive collision drop. */ - ret = ksz_rmw8(dev, REG_SW_CTRL_2, - UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP, - UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP); + val = NO_EXC_COLLISION_DROP; + if (!ksz_is_ksz8995xa(dev)) + val |= UNICAST_VLAN_BOUNDARY; + ret = ksz_rmw8(dev, REG_SW_CTRL_2, val, val); if (ret) return ret; @@ -2721,11 +2841,15 @@ static int ksz8_setup(struct dsa_switch *ds) ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false); - if (!ksz_is_ksz88x3(dev)) + if (ksz_is_ksz8995xa(dev)) + ksz_cfg(dev, REG_SW_CTRL_9, SW_SPECIAL_TAG, true); + else if (!ksz_is_ksz88x3(dev)) ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true); - for (i = 0; i < (dev->info->num_vlans / 4); i++) - ksz8_r_vlan_entries(dev, i); + if (!ksz_is_ksz8995xa(dev)) { + for (i = 0; i < (dev->info->num_vlans / 4); i++) + ksz8_r_vlan_entries(dev, i); + } /* Make sure PME (WoL) is not enabled. If requested, it will * be enabled by ksz_wol_pre_shutdown(). Otherwise, some PMICs @@ -2775,6 +2899,7 @@ static int ksz8_setup(struct dsa_switch *ds) } if (dev->info->ptp_capable) { + ksz_ptp_set_caps(ds); ret = ksz_ptp_clock_register(ds); if (ret) { dev_err(dev->dev, "Failed to register PTP clock: %d\n", @@ -2976,6 +3101,13 @@ static int ksz8_switch_init(struct ksz_device *dev) return 0; } +static enum dsa_tag_protocol ksz8995xa_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) +{ + return DSA_TAG_PROTO_KS8995; +} + static enum dsa_tag_protocol ksz8463_get_tag_protocol(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp) @@ -3072,6 +3204,16 @@ const struct phylink_mac_ops ksz8_phylink_mac_ops = { .mac_enable_tx_lpi = ksz_phylink_mac_enable_tx_lpi, }; +/* + * The KS(Z)8995XA has no indirect access, meaning no MIB counters, + * no FDB access, and no VLAN handling. + */ +const struct ksz_dev_ops ksz8995xa_dev_ops = { + .get_port_addr = ksz8_get_port_addr, + .cfg_port_member = ksz8_cfg_port_member, + .init = ksz8_switch_init, +}; + const struct ksz_dev_ops ksz8463_dev_ops = { .get_port_addr = ksz8463_get_port_addr, .cfg_port_member = ksz8_cfg_port_member, @@ -3111,6 +3253,38 @@ const struct ksz_dev_ops ksz88xx_dev_ops = { .pme_pwrite8 = ksz8_pme_pwrite8, }; +/* + * Restricted operations for KSZ8995XA, so many things are not supported + * by this old switch that we need diet DSA operations. + */ +const struct dsa_switch_ops ksz8995xa_switch_ops = { + .get_tag_protocol = ksz8995xa_get_tag_protocol, + .setup = ksz8_setup, + .teardown = ksz_teardown, + .phy_read = ksz8_phy_read16, + .phy_write = ksz8_phy_write16, + .phylink_get_caps = ksz8_phylink_get_caps, + .port_setup = ksz8_dsa_port_setup, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .port_set_mac_address = ksz_port_set_mac_address, + .port_stp_state_set = ksz_port_stp_state_set, + .port_pre_bridge_flags = ksz_port_pre_bridge_flags, + .port_bridge_flags = ksz_port_bridge_flags, + .port_fast_age = ksz8_flush_dyn_mac_table, + .port_mirror_add = ksz8_port_mirror_add, + .port_mirror_del = ksz8_port_mirror_del, + .port_change_mtu = ksz8995xa_change_mtu, + .port_max_mtu = ksz88xx_max_mtu, + .port_get_default_prio = ksz_port_get_default_prio, + .port_set_default_prio = ksz_port_set_default_prio, + .port_get_dscp_prio = ksz_port_get_dscp_prio, + .port_add_dscp_prio = ksz_port_add_dscp_prio, + .port_del_dscp_prio = ksz_port_del_dscp_prio, + .port_get_apptrust = ksz_port_get_apptrust, + .port_set_apptrust = ksz_port_set_apptrust, +}; + const struct dsa_switch_ops ksz8463_switch_ops = { .get_tag_protocol = ksz8463_get_tag_protocol, .connect_tag_protocol = ksz8463_connect_tag_protocol, diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index bc371cc26c6f..9b37c56c9617 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -12,11 +12,13 @@ #include <net/dsa.h> #include "ksz_common.h" +extern const struct ksz_dev_ops ksz8995xa_dev_ops; extern const struct ksz_dev_ops ksz8463_dev_ops; extern const struct ksz_dev_ops ksz87xx_dev_ops; extern const struct ksz_dev_ops ksz88xx_dev_ops; extern const struct phylink_mac_ops ksz88x3_phylink_mac_ops; extern const struct phylink_mac_ops ksz8_phylink_mac_ops; +extern const struct dsa_switch_ops ksz8995xa_switch_ops; extern const struct dsa_switch_ops ksz8463_switch_ops; extern const struct dsa_switch_ops ksz87xx_switch_ops; extern const struct dsa_switch_ops ksz88xx_switch_ops; diff --git a/drivers/net/dsa/microchip/ksz8_reg.h b/drivers/net/dsa/microchip/ksz8_reg.h index 6bc511da1f7d..f858bce84bfd 100644 --- a/drivers/net/dsa/microchip/ksz8_reg.h +++ b/drivers/net/dsa/microchip/ksz8_reg.h @@ -95,6 +95,8 @@ #define SW_LED_LINK_ACT_DUPLEX 2 #define SW_LED_LINK_DUPLEX 3 +#define SW_SPECIAL_TAG BIT(0) /* KSZ8995XA only */ + #define REG_SW_CTRL_10 0x0C #define SW_PASS_PAUSE BIT(0) diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 3ee995545c57..72528a53b67d 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -1781,6 +1781,7 @@ static int ksz9477_setup(struct dsa_switch *ds) } if (dev->info->ptp_capable) { + ksz_ptp_set_caps(ds); ret = ksz_ptp_clock_register(ds); if (ret) { dev_err(dev->dev, "Failed to register PTP clock: %d\n", diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 1a9d6f83a023..767c8fe8e819 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -446,6 +446,20 @@ static const u8 ksz8895_shifts[] = { [DYNAMIC_MAC_SRC_PORT] = 24, }; +static const u16 ksz8995xa_regs[] = { + [REG_SW_MAC_ADDR] = 0x68, + [P_FORCE_CTRL] = 0x0C, + [P_LINK_STATUS] = 0x0E, + [P_LOCAL_CTRL] = 0x0C, + [P_NEG_RESTART_CTRL] = 0x0D, + [P_REMOTE_STATUS] = 0x0E, + [P_SPEED_STATUS] = 0x09, + [P_STP_CTRL] = 0x02, + [S_START_CTRL] = 0x01, + [S_BROADCAST_CTRL] = 0x06, + [S_MULTICAST_CTRL] = 0x04, +}; + static const u16 ksz9477_regs[] = { [REG_SW_MAC_ADDR] = 0x0302, [P_STP_CTRL] = 0x0B04, @@ -1161,6 +1175,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .supports_mii = {false, false, true}, .supports_rmii = {false, false, true}, .internal_phy = {true, true, false}, + .n_pins = 12, + .n_per_out = 12, }, [KSZ8563] = { @@ -1194,6 +1210,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .ptp_capable = true, .wr_table = &ksz8563_register_set, .rd_table = &ksz8563_register_set, + .n_pins = 2, + .n_per_out = 3, }, [KSZ8795] = { @@ -1377,6 +1395,21 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, true, true, false}, }, + [KSZ8995XA] = { + .chip_id = KSZ8995XA_CHIP_ID, /* Also known as KS8995XA */ + .dev_name = "KSZ8995XA", + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .num_tx_queues = 2, /* low/hi priority queues, no more */ + .num_ipms = 2, + .ops = &ksz8995xa_dev_ops, + .switch_ops = &ksz8995xa_switch_ops, + .phylink_mac_ops = &ksz88x3_phylink_mac_ops, + .regs = ksz8995xa_regs, + .supports_mii = {true, true, true, true, true}, + .internal_phy = {true, true, true, true, false}, + }, + [KSZ9477] = { .chip_id = KSZ9477_CHIP_ID, .dev_name = "KSZ9477", @@ -1414,6 +1447,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .sgmii_port = 7, .wr_table = &ksz9477_register_set, .rd_table = &ksz9477_register_set, + .n_pins = 2, + .n_per_out = 3, }, [KSZ9896] = { @@ -1486,6 +1521,41 @@ const struct ksz_chip_data ksz_switch_chips[] = { .gbit_capable = {true, true, true, true, true, true, true}, }, + [KSZ9897S] = { + .chip_id = KSZ9897S_CHIP_ID, + .dev_name = "KSZ9897S", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x7F, /* can be configured as cpu port */ + .port_cnt = 7, /* total physical port count */ + .port_nirqs = 2, + .num_tx_queues = 4, + .num_ipms = 8, + .ops = &ksz9477_dev_ops, + .switch_ops = &ksz9477_switch_ops, + .phylink_mac_ops = &ksz9477_phylink_mac_ops, + .phy_errata_9477 = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = ksz9477_masks, + .shifts = ksz9477_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + false, true, false}, + .supports_rmii = {false, false, false, false, + false, true, false}, + .supports_rgmii = {false, false, false, false, + false, true, false}, + .internal_phy = {true, true, true, true, + true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, + .sgmii_port = 7, + }, + [KSZ9893] = { .chip_id = KSZ9893_CHIP_ID, .dev_name = "KSZ9893", @@ -1544,6 +1614,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, false}, .gbit_capable = {true, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [KSZ8567] = { @@ -1581,6 +1653,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .gbit_capable = {false, false, false, false, false, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [KSZ9567] = { @@ -1615,6 +1689,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { true, false, false}, .gbit_capable = {true, true, true, true, true, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9370] = { @@ -1646,6 +1722,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .supports_rgmii = {false, false, false, false, true}, .internal_phy = {true, true, true, true, false}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9371] = { @@ -1677,6 +1755,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .supports_rgmii = {false, false, false, false, true, true}, .internal_phy = {true, true, true, true, false, false}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9372] = { @@ -1712,6 +1792,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, true, true, false, false, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9373] = { @@ -1747,6 +1829,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, true, false, false, false, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9374] = { @@ -1782,6 +1866,8 @@ const struct ksz_chip_data ksz_switch_chips[] = { .internal_phy = {true, true, true, true, false, false, true, true}, .ptp_capable = true, + .n_pins = 2, + .n_per_out = 3, }, [LAN9646] = { @@ -2649,6 +2735,10 @@ void ksz_init_mib_timer(struct ksz_device *dev) { int i; + /* KSZ8995XA lacks MiB features */ + if (ksz_is_ksz8995xa(dev)) + return; + INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); for (i = 0; i < dev->info->port_cnt; i++) { @@ -2944,11 +3034,15 @@ static int ksz_switch_detect(struct ksz_device *dev) return -ENODEV; break; case KSZ8895_FAMILY_ID: - if (id2 == KSZ8895_CHIP_ID_95 || - id2 == KSZ8895_CHIP_ID_95R) + if (id2 == KSZ8895_CHIP_ID_95XA) { + dev->chip_id = KSZ8995XA_CHIP_ID; + break; + } else if (id2 == KSZ8895_CHIP_ID_95 || + id2 == KSZ8895_CHIP_ID_95R) { dev->chip_id = KSZ8895_CHIP_ID; - else + } else { return -ENODEV; + } ret = ksz_read8(dev, REG_KSZ8864_CHIP_ID, &id4); if (ret) return ret; @@ -3897,6 +3991,7 @@ int ksz_switch_register(struct ksz_device *dev) struct device_node *ports; phy_interface_t interface; unsigned int port_num; + u32 lookup_chip_id; int ret; int i; @@ -3933,7 +4028,30 @@ int ksz_switch_register(struct ksz_device *dev) if (ret) return ret; - info = ksz_lookup_info(dev->chip_id); + lookup_chip_id = dev->chip_id; + + /* The KSZ9897S and the KSZ9897R report the same chip ID and differ + * only in chip_data: only the S has the SGMII port 7, the R has a + * second RGMII port instead. Bit 7 of the port 7 XMII control 0 + * register is read-only and tells them apart; it reads one on the S. + * Compare the KSZ9897S data sheet DS00002394C section 5.2.4.1 with + * the KSZ9897R data sheet DS00002330D section 5.2.3.1. + * + * Only the chip_data entry differs, so dev->chip_id keeps the shared + * chip ID and nothing else has to know about the variant. + */ + if (dev->chip_id == KSZ9897_CHIP_ID) { + u8 val; + + ret = ksz_read8(dev, KSZ9897_REG_PORT7_XMII_CTRL_0, &val); + if (ret) + return ret; + + if (val & KSZ9897_PORT7_SGMII_SEL) + lookup_chip_id = KSZ9897S_CHIP_ID; + } + + info = ksz_lookup_info(lookup_chip_id); if (!info) return -ENODEV; @@ -4029,11 +4147,13 @@ int ksz_switch_register(struct ksz_device *dev) if (ret) return ret; - /* Read MIB counters every 30 seconds to avoid overflow. */ - dev->mib_read_interval = msecs_to_jiffies(5000); + if (!ksz_is_ksz8995xa(dev)) { + /* Read MIB counters every 30 seconds to avoid overflow. */ + dev->mib_read_interval = msecs_to_jiffies(5000); - /* Start the MIB timer. */ - schedule_delayed_work(&dev->mib_read, 0); + /* Start the MIB timer. */ + schedule_delayed_work(&dev->mib_read, 0); + } return ret; } diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index f6104f714a8d..8819670cbb05 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -139,6 +139,8 @@ struct ksz_chip_data { u8 sgmii_port; const struct regmap_access_table *wr_table; const struct regmap_access_table *rd_table; + const u8 n_pins; + const u8 n_per_out; }; struct ksz_irq { @@ -281,9 +283,11 @@ enum ksz_model { KSZ88X3, KSZ8864, KSZ8895, + KSZ8995XA, KSZ9477, KSZ9896, KSZ9897, + KSZ9897S, KSZ9893, KSZ9563, KSZ9567, @@ -789,7 +793,13 @@ static inline bool ksz_is_ksz88x3(struct ksz_device *dev) static inline bool ksz_is_8895_family(struct ksz_device *dev) { return dev->chip_id == KSZ8895_CHIP_ID || - dev->chip_id == KSZ8864_CHIP_ID; + dev->chip_id == KSZ8864_CHIP_ID || + dev->chip_id == KSZ8995XA_CHIP_ID; +} + +static inline bool ksz_is_ksz8995xa(struct ksz_device *dev) +{ + return dev->chip_id == KSZ8995XA_CHIP_ID; } static inline bool is_ksz8(struct ksz_device *dev) @@ -860,6 +870,7 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port) #define KSZ87_CHIP_ID_94 0x6 #define KSZ87_CHIP_ID_95 0x9 #define KSZ88_CHIP_ID_63 0x3 +#define KSZ8895_CHIP_ID_95XA 0x0 #define KSZ8895_CHIP_ID_95 0x4 #define KSZ8895_CHIP_ID_95R 0x6 @@ -874,6 +885,10 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port) #define SKU_ID_KSZ8563 0x3c #define SKU_ID_KSZ9563 0x1c +/* KSZ9897 specific register: port 7 XMII control 0 */ +#define KSZ9897_REG_PORT7_XMII_CTRL_0 0x7300 +#define KSZ9897_PORT7_SGMII_SEL BIT(7) + /* Driver set switch broadcast storm protection at 10% rate. */ #define BROADCAST_STORM_PROT_RATE 10 @@ -923,6 +938,7 @@ static inline bool ksz_is_sgmii_port(struct ksz_device *dev, int port) #define KSZ8863_HUGE_PACKET_SIZE 1916 #define KSZ8863_NORMAL_PACKET_SIZE 1536 #define KSZ8_LEGAL_PACKET_SIZE 1518 +#define KSZ8995XA_LEGAL_PACKET_SIZE 1522 #define KSZ9477_MAX_FRAME_SIZE 9000 #define KSZ8873_REG_GLOBAL_CTRL_12 0x0e diff --git a/drivers/net/dsa/microchip/ksz_dcb.c b/drivers/net/dsa/microchip/ksz_dcb.c index 7131c5caac54..aa0260f07252 100644 --- a/drivers/net/dsa/microchip/ksz_dcb.c +++ b/drivers/net/dsa/microchip/ksz_dcb.c @@ -20,10 +20,12 @@ #define KSZ8_PORT_DIFFSERV_ENABLE BIT(6) #define KSZ8_PORT_802_1P_ENABLE BIT(5) #define KSZ8_PORT_BASED_PRIO_M GENMASK(4, 3) +#define KSZ8995XA_PORT_BASED_PRIO BIT(4) #define KSZ8463_REG_TOS_DSCP_CTRL 0x16 #define KSZ88X3_REG_TOS_DSCP_CTRL 0x60 #define KSZ8765_REG_TOS_DSCP_CTRL 0x90 +#define KSZ8995XA_REG_TOS_DSCP_CTRL_7 0x67 #define KSZ9477_REG_SW_MAC_TOS_CTRL 0x033e #define KSZ9477_SW_TOS_DSCP_REMAP BIT(0) @@ -98,8 +100,11 @@ static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg, { if (is_ksz8(dev)) { *reg = KSZ8_REG_PORT_1_CTRL_0; - *mask = KSZ8_PORT_BASED_PRIO_M; - *shift = __bf_shf(KSZ8_PORT_BASED_PRIO_M); + if (ksz_is_ksz8995xa(dev)) + *mask = KSZ8995XA_PORT_BASED_PRIO; + else + *mask = KSZ8_PORT_BASED_PRIO_M; + *shift = __bf_shf(*mask); if (ksz_is_ksz8463(dev)) *reg = KSZ8463_REG_PORT_1_CTRL_0; } else { @@ -112,31 +117,45 @@ static void ksz_get_default_port_prio_reg(struct ksz_device *dev, int *reg, /** * ksz_get_dscp_prio_reg - Retrieves the DSCP-to-priority-mapping register * @dev: Pointer to the KSZ switch device structure + * @dscp: DSCP value for which to retrieve the register * @reg: Pointer to the register address to be set - * @per_reg: Pointer to the number of DSCP values per register * @mask: Pointer to the mask to be set + * @shift: Pointer to the bit shift to be set * - * This function retrieves the DSCP to priority mapping register, the number of - * DSCP values per register, and the mask to be set. + * This function retrieves the register, mask and shift for a DSCP to priority + * mapping entry. */ -static void ksz_get_dscp_prio_reg(struct ksz_device *dev, int *reg, - int *per_reg, u8 *mask) +static void ksz_get_dscp_prio_reg(struct ksz_device *dev, u8 dscp, int *reg, + u8 *mask, int *shift) { + int per_reg; + + if (ksz_is_ksz8995xa(dev)) { + /* KSZ8995XA stores DSCP groups in descending register order. */ + *reg = KSZ8995XA_REG_TOS_DSCP_CTRL_7 - dscp / 8; + *mask = BIT(0); + *shift = dscp % 8; + return; + } + if (ksz_is_ksz87xx(dev) || ksz_is_8895_family(dev)) { *reg = KSZ8765_REG_TOS_DSCP_CTRL; - *per_reg = 4; + per_reg = 4; *mask = GENMASK(1, 0); } else if (ksz_is_ksz88x3(dev) || ksz_is_ksz8463(dev)) { *reg = KSZ88X3_REG_TOS_DSCP_CTRL; - *per_reg = 4; + per_reg = 4; *mask = GENMASK(1, 0); if (ksz_is_ksz8463(dev)) *reg = KSZ8463_REG_TOS_DSCP_CTRL; } else { *reg = KSZ9477_REG_DIFFSERV_PRIO_MAP; - *per_reg = 2; + per_reg = 2; *mask = GENMASK(2, 0); } + + *reg += dscp / per_reg; + *shift = (dscp % per_reg) * (8 / per_reg); } /** @@ -236,10 +255,10 @@ int ksz_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) { struct ksz_device *dev = ds->priv; - int reg, per_reg, ret, shift; + int reg, ret, shift; u8 data, mask; - ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask); + ksz_get_dscp_prio_reg(dev, dscp, ®, &mask, &shift); /* If DSCP remapping is disabled, DSCP bits 3-5 are used as Internal * Priority Map (IPM) @@ -260,13 +279,10 @@ int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) /* In case DSCP remapping is enabled, we need to write the DSCP to * priority mapping table. */ - reg += dscp / per_reg; ret = ksz_read8(dev, reg, &data); if (ret) return ret; - shift = (dscp % per_reg) * (8 / per_reg); - return (data >> shift) & mask; } @@ -283,15 +299,12 @@ int ksz_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) */ static int ksz_set_global_dscp_entry(struct ksz_device *dev, u8 dscp, u8 ipm) { - int reg, per_reg, shift; + int reg, shift; u8 mask; - ksz_get_dscp_prio_reg(dev, ®, &per_reg, &mask); + ksz_get_dscp_prio_reg(dev, dscp, ®, &mask, &shift); - shift = (dscp % per_reg) * (8 / per_reg); - - return ksz_rmw8(dev, reg + (dscp / per_reg), mask << shift, - ipm << shift); + return ksz_rmw8(dev, reg, mask << shift, ipm << shift); } /** @@ -441,6 +454,9 @@ static void ksz_apptrust_error(struct ksz_device *dev) * 2. IEEE_8021QAZ_APP_SEL_DSCP - Differentiated Services Code Point selector * (lowest priority) * + * KSZ8995XA instead ORs the classification results, so it supports only + * one trusted selector at a time. + * * Return: 0 on success, or a negative error code on failure */ static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port, @@ -449,6 +465,12 @@ static int ksz_port_set_apptrust_validate(struct ksz_device *dev, int port, int i, j, found; int j_prev = 0; + if (ksz_is_ksz8995xa(dev) && nsel > 1) { + dev_err(dev->dev, + "KSZ8995XA supports only one apptrust selector at a time\n"); + return -EOPNOTSUPP; + } + /* Iterate through the requested selectors */ for (i = 0; i < nsel; i++) { found = 0; @@ -552,6 +574,10 @@ int ksz_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, int *nsel) if (ret) return ret; + /* XA's combined classification cannot be reported as a trust order. */ + if (ksz_is_ksz8995xa(dev) && (data & mask) == mask) + return -EOPNOTSUPP; + *nsel = 0; for (i = 0; i < ARRAY_SIZE(ksz_supported_apptrust); i++) { if (data & map[i].bit) diff --git a/drivers/net/dsa/microchip/ksz_ptp.c b/drivers/net/dsa/microchip/ksz_ptp.c index 39cc70d65900..1c59428b159b 100644 --- a/drivers/net/dsa/microchip/ksz_ptp.c +++ b/drivers/net/dsa/microchip/ksz_ptp.c @@ -26,6 +26,7 @@ */ #define KSZ_MAX_DRIFT_CORR 6249999 #define KSZ_MAX_PULSE_WIDTH 125000000LL +#define KSZ8463_MAX_PULSE_WIDTH 500000LL #define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */ #define KSZ_PTP_SUBNS_BITS 32 @@ -63,6 +64,17 @@ static int ksz_ptp_tou_gpio(struct ksz_device *dev) LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2); } +static int ksz8463_ptp_tou_reset(struct ksz_device *dev, u8 unit) +{ + int ret; + + ret = ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), BIT(unit)); + if (ret) + return ret; + + return ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), 0); +} + static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit) { u32 data; @@ -86,7 +98,7 @@ static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit) 0); } -static int ksz_ptp_tou_pulse_verify(u64 pulse_ns) +static int ksz_ptp_tou_pulse_verify(u64 pulse_ns, u32 mask) { u32 data; @@ -94,7 +106,7 @@ static int ksz_ptp_tou_pulse_verify(u64 pulse_ns) return -EINVAL; data = (pulse_ns / 8); - if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data)) + if ((mask & data) != data) return -ERANGE; return 0; @@ -120,6 +132,28 @@ static int ksz_ptp_tou_target_time_set(struct ksz_device *dev, return 0; } +static int ksz8463_ptp_tou_start(struct ksz_device *dev, u8 unit) +{ + u16 data; + int ret; + + ret = ksz_rmw16(dev, KSZ8463_TOU_EN, BIT(unit), BIT(unit)); + if (ret) + return ret; + + ret = ksz_read16(dev, KSZ8463_TOU_ACTIVE, &data); + if (ret) + return ret; + + if (!(data & BIT(unit))) { + dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__, + unit); + return -EIO; + } + + return 0; +} + static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit) { u32 data; @@ -147,6 +181,56 @@ static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit) return 0; } +static int ksz8463_ptp_configure_perout(struct ksz_device *dev, + struct ptp_perout_request const *request, + u32 cycle_width_ns, u32 pulse_width_ns, + u8 index) +{ + struct ptp_pin_desc *pin = &dev->ptp_data.pin_config[index]; + u16 cfg_base = KSZ8463_TRIG1_CFG + KSZ8463_TRIGN_CFG_SIZE * pin->chan; + u16 data; + int ret; + + /* Hardware has only 32 bit for the second field */ + if ((request->start.sec & 0xffffffff) != request->start.sec) + return -EINVAL; + + data = KSZ8463_NOTIFY_BIT | + FIELD_PREP(KSZ8463_PATTERN_M, TRIG_POS_PERIOD) | + pin->index; + ret = ksz_write16(dev, cfg_base + KSZ8463_PATTERN_OFF, data); + if (ret) + return ret; + + ret = ksz_write32(dev, cfg_base + KSZ8463_CYCLE_WIDTH_OFF, + cycle_width_ns); + if (ret) + return ret; + + /* Set cycle count 0 - Infinite */ + ret = ksz_write16(dev, cfg_base + KSZ8463_CYCLE_CNT_OFF, 0); + if (ret) + return ret; + + /* KSZ8463 uses a 8 ns unit value to compute the pulse width */ + data = (pulse_width_ns / 8); + ret = ksz_write16(dev, cfg_base + KSZ8463_PULSE_WIDTH_OFF, data); + if (ret) + return ret; + + ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_NSEC, + request->start.nsec); + if (ret) + return ret; + + ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_SEC, + request->start.sec); + if (ret) + return ret; + + return 0; +} + static int ksz_ptp_configure_perout(struct ksz_device *dev, u32 cycle_width_ns, u32 pulse_width_ns, struct timespec64 const *target_time, @@ -183,17 +267,11 @@ static int ksz_ptp_configure_perout(struct ksz_device *dev, return 0; } -static int ksz_ptp_enable_perout(struct ksz_device *dev, - struct ptp_perout_request const *request, - int on) +static int ksz_ptp_get_pin(struct ksz_device *dev, + struct ptp_perout_request const *request) { struct ksz_ptp_data *ptp_data = &dev->ptp_data; - u64 req_pulse_width_ns; - u64 cycle_width_ns; - u64 pulse_width_ns; - int pin = 0; - u32 data32; - int ret; + int pin; if (request->flags & ~PTP_PEROUT_DUTY_CYCLE) return -EOPNOTSUPP; @@ -206,6 +284,119 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev, if (pin < 0) return -EINVAL; + return pin; +} + +static int ksz_ptp_compute_perout_cycle(struct ksz_device *dev, + struct ptp_perout_request const *request, + u64 max_pulse_width, + u64 *cycle_width_ns, + u64 *pulse_width_ns) +{ + struct timespec64 tmp; + + if (request->period.sec < 0) + return -EINVAL; + + if (!request->period.sec && !request->period.nsec) + return -EINVAL; + + tmp.tv_sec = request->period.sec; + tmp.tv_nsec = request->period.nsec; + *cycle_width_ns = timespec64_to_ns(&tmp); + if ((*cycle_width_ns & TRIG_CYCLE_WIDTH_M) != *cycle_width_ns) { + *cycle_width_ns = 0; + *pulse_width_ns = 0; + return -EINVAL; + } + + if (request->flags & PTP_PEROUT_DUTY_CYCLE) { + *pulse_width_ns = request->on.sec * NSEC_PER_SEC + + request->on.nsec; + return 0; + } + + /* Use a duty cycle of 50%. Maximum pulse width supported by the + * hardware is a little bit more than 125 ms. + */ + *pulse_width_ns = (request->period.sec * NSEC_PER_SEC + + request->period.nsec) / 2; + *pulse_width_ns = min_t(u64, *pulse_width_ns, max_pulse_width); + + return 0; +} + +static int ksz8463_ptp_enable_perout(struct ksz_device *dev, + struct ptp_perout_request const *request, + int on) +{ + struct ksz_ptp_data *ptp_data = &dev->ptp_data; + u64 cycle_width_ns; + u64 pulse_width_ns; + int pin; + int ret; + + pin = ksz_ptp_get_pin(dev, request); + if (pin < 0) + return pin; + + ret = ksz8463_ptp_tou_reset(dev, request->index); + if (ret) + return ret; + + if (!on) { + ptp_data->tou_mode = KSZ_PTP_TOU_IDLE; + return 0; + } + + ret = ksz_ptp_compute_perout_cycle(dev, request, + KSZ8463_MAX_PULSE_WIDTH, + &cycle_width_ns, &pulse_width_ns); + if (ret) + return ret; + + ret = ksz_ptp_tou_pulse_verify(pulse_width_ns, + KSZ8463_TRIG_PULSE_WIDTH_M); + if (ret) + return ret; + + ret = ksz8463_ptp_configure_perout(dev, request, + cycle_width_ns, pulse_width_ns, + pin); + if (ret) + return ret; + + ret = ksz_ptp_tou_gpio(dev); + if (ret) + return ret; + + ret = ksz8463_ptp_tou_start(dev, request->index); + if (ret) + return ret; + + memcpy(&ptp_data->perout_request, request, + sizeof(struct ptp_perout_request)); + ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT; + + return 0; +} + +static int ksz_ptp_enable_perout(struct ksz_device *dev, + struct ptp_perout_request const *request, + int on) +{ + struct ksz_ptp_data *ptp_data = &dev->ptp_data; + struct timespec64 tmp; + u64 cycle_width_ns; + u64 pulse_width_ns; + int pin = 0; + u32 data32; + int ret; + + pin = ksz_ptp_get_pin(dev, request); + if (pin < 0) + return pin; + data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) | FIELD_PREP(PTP_TOU_INDEX, request->index); ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4, @@ -222,36 +413,19 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev, return 0; } - ptp_data->perout_target_time_first.tv_sec = request->start.sec; - ptp_data->perout_target_time_first.tv_nsec = request->start.nsec; - - ptp_data->perout_period.tv_sec = request->period.sec; - ptp_data->perout_period.tv_nsec = request->period.nsec; - - cycle_width_ns = timespec64_to_ns(&ptp_data->perout_period); - if ((cycle_width_ns & TRIG_CYCLE_WIDTH_M) != cycle_width_ns) - return -EINVAL; - - if (request->flags & PTP_PEROUT_DUTY_CYCLE) { - pulse_width_ns = request->on.sec * NSEC_PER_SEC + - request->on.nsec; - } else { - /* Use a duty cycle of 50%. Maximum pulse width supported by the - * hardware is a little bit more than 125 ms. - */ - req_pulse_width_ns = (request->period.sec * NSEC_PER_SEC + - request->period.nsec) / 2; - pulse_width_ns = min_t(u64, req_pulse_width_ns, - KSZ_MAX_PULSE_WIDTH); - } + ret = ksz_ptp_compute_perout_cycle(dev, request, KSZ_MAX_PULSE_WIDTH, + &cycle_width_ns, &pulse_width_ns); + if (ret) + return ret; - ret = ksz_ptp_tou_pulse_verify(pulse_width_ns); + ret = ksz_ptp_tou_pulse_verify(pulse_width_ns, TRIG_PULSE_WIDTH_M); if (ret) return ret; + tmp.tv_sec = request->start.sec; + tmp.tv_nsec = request->start.nsec; ret = ksz_ptp_configure_perout(dev, cycle_width_ns, pulse_width_ns, - &ptp_data->perout_target_time_first, - pin); + &tmp, pin); if (ret) return ret; @@ -263,6 +437,8 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev, if (ret) return ret; + memcpy(&ptp_data->perout_request, request, + sizeof(struct ptp_perout_request)); ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT; return 0; @@ -756,13 +932,14 @@ static int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) return ret; } -static int ksz_ptp_restart_perout(struct ksz_device *dev) +static int ksz_ptp_prepare_restart_perout(struct ksz_device *dev, + struct ptp_perout_request *request) { struct ksz_ptp_data *ptp_data = &dev->ptp_data; s64 now_ns, first_ns, period_ns, next_ns; - struct ptp_perout_request request; struct timespec64 next; struct timespec64 now; + struct timespec64 tmp; unsigned int count; int ret; @@ -773,10 +950,14 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev) return ret; now_ns = timespec64_to_ns(&now); - first_ns = timespec64_to_ns(&ptp_data->perout_target_time_first); + tmp.tv_sec = ptp_data->perout_request.start.sec; + tmp.tv_nsec = ptp_data->perout_request.start.nsec; + first_ns = timespec64_to_ns(&tmp); /* Calculate next perout event based on start time and period */ - period_ns = timespec64_to_ns(&ptp_data->perout_period); + tmp.tv_sec = ptp_data->perout_request.period.sec; + tmp.tv_nsec = ptp_data->perout_request.period.nsec; + period_ns = timespec64_to_ns(&tmp); if (first_ns < now_ns) { count = div_u64(now_ns - first_ns, period_ns); @@ -791,41 +972,110 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev) /* Restart periodic output signal */ next = ns_to_timespec64(next_ns); - request.start.sec = next.tv_sec; - request.start.nsec = next.tv_nsec; - request.period.sec = ptp_data->perout_period.tv_sec; - request.period.nsec = ptp_data->perout_period.tv_nsec; - request.index = 0; - request.flags = 0; + memcpy(request, &ptp_data->perout_request, + sizeof(struct ptp_perout_request)); + request->start.sec = next.tv_sec; + request->start.nsec = next.tv_nsec; + + return 0; +} + +static int ksz_ptp_restart_perout(struct ksz_device *dev) +{ + struct ptp_perout_request request; + int ret; + + ret = ksz_ptp_prepare_restart_perout(dev, &request); + if (ret) + return ret; return ksz_ptp_enable_perout(dev, &request, 1); } -static int ksz_ptp_settime(struct ptp_clock_info *ptp, - const struct timespec64 *ts) +static int ksz8463_ptp_restart_perout(struct ksz_device *dev) { - struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); - struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); - const u16 *regs = dev->info->regs; + struct ptp_perout_request request; int ret; - mutex_lock(&ptp_data->lock); + ret = ksz_ptp_prepare_restart_perout(dev, &request); + if (ret) + return ret; + + return ksz8463_ptp_enable_perout(dev, &request, 1); +} + +static int __ksz_ptp_settime(struct ksz_device *dev, + const struct timespec64 *ts) +{ + const u16 *regs = dev->info->regs; + int ret; /* Write to shadow registers and Load PTP clock */ ret = ksz_write16(dev, regs[PTP_RTC_SUB_NANOSEC], PTP_RTC_0NS); if (ret) - goto unlock; + return ret; ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], ts->tv_nsec); if (ret) - goto unlock; + return ret; ret = ksz_write32(dev, regs[PTP_RTC_SEC], ts->tv_sec); if (ret) - goto unlock; + return ret; ret = ksz_rmw16(dev, regs[PTP_CLK_CTRL], PTP_LOAD_TIME, PTP_LOAD_TIME); if (ret) + return ret; + + return 0; +} + +static int ksz8463_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); + int ret; + + mutex_lock(&ptp_data->lock); + + ret = __ksz_ptp_settime(dev, ts); + if (ret) + goto unlock; + + switch (ptp_data->tou_mode) { + case KSZ_PTP_TOU_IDLE: + break; + + case KSZ_PTP_TOU_PEROUT: + ret = ksz8463_ptp_restart_perout(dev); + if (ret) + goto unlock; + + break; + } + + spin_lock_bh(&ptp_data->clock_lock); + ptp_data->clock_time = *ts; + spin_unlock_bh(&ptp_data->clock_lock); + +unlock: + mutex_unlock(&ptp_data->lock); + + return ret; +} + +static int ksz_ptp_settime(struct ptp_clock_info *ptp, + const struct timespec64 *ts) +{ + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); + int ret; + + mutex_lock(&ptp_data->lock); + + ret = __ksz_ptp_settime(dev, ts); + if (ret) goto unlock; switch (ptp_data->tou_mode) { @@ -890,18 +1140,13 @@ unlock: return ret; } -static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +static int __ksz_ptp_adjtime(struct ksz_device *dev, s64 delta) { - struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); - struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); - struct timespec64 delta64 = ns_to_timespec64(delta); const u16 *regs = dev->info->regs; s32 sec, nsec; u16 data16; int ret; - mutex_lock(&ptp_data->lock); - /* do not use ns_to_timespec64(), * both sec and nsec are subtracted by hw */ @@ -909,15 +1154,15 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], abs(nsec)); if (ret) - goto unlock; + return ret; ret = ksz_write32(dev, regs[PTP_RTC_SEC], abs(sec)); if (ret) - goto unlock; + return ret; ret = ksz_read16(dev, regs[PTP_CLK_CTRL], &data16); if (ret) - goto unlock; + return ret; data16 |= PTP_STEP_ADJ; @@ -929,6 +1174,56 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) ret = ksz_write16(dev, regs[PTP_CLK_CTRL], data16); if (ret) + return ret; + + return 0; +} + +static int ksz8463_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); + struct timespec64 delta64 = ns_to_timespec64(delta); + int ret; + + mutex_lock(&ptp_data->lock); + + ret = __ksz_ptp_adjtime(dev, delta); + if (ret) + goto unlock; + + switch (ptp_data->tou_mode) { + case KSZ_PTP_TOU_IDLE: + break; + + case KSZ_PTP_TOU_PEROUT: + ret = ksz8463_ptp_restart_perout(dev); + if (ret) + goto unlock; + + break; + } + + spin_lock_bh(&ptp_data->clock_lock); + ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64); + spin_unlock_bh(&ptp_data->clock_lock); + +unlock: + mutex_unlock(&ptp_data->lock); + return ret; +} + +static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); + struct timespec64 delta64 = ns_to_timespec64(delta); + int ret; + + mutex_lock(&ptp_data->lock); + + ret = __ksz_ptp_adjtime(dev, delta); + if (ret) goto unlock; switch (ptp_data->tou_mode) { @@ -952,6 +1247,26 @@ unlock: return ret; } +static int ksz8463_ptp_enable(struct ptp_clock_info *ptp, + struct ptp_clock_request *req, int on) +{ + struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp); + struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data); + int ret; + + switch (req->type) { + case PTP_CLK_REQ_PEROUT: + mutex_lock(&ptp_data->lock); + ret = ksz8463_ptp_enable_perout(dev, &req->perout, on); + mutex_unlock(&ptp_data->lock); + break; + default: + return -EOPNOTSUPP; + } + + return ret; +} + static int ksz_ptp_enable(struct ptp_clock_info *ptp, struct ptp_clock_request *req, int on) { @@ -1028,17 +1343,33 @@ static int ksz_ptp_start_clock(struct ksz_device *dev) return 0; } -int ksz_ptp_clock_register(struct dsa_switch *ds) +void ksz8463_ptp_set_caps(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + struct ksz_ptp_data *ptp_data; + + ptp_data = &dev->ptp_data; + + ptp_data->caps.owner = THIS_MODULE; + snprintf(ptp_data->caps.name, 16, "Microchip Clock"); + ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR; + ptp_data->caps.gettime64 = ksz_ptp_gettime; + ptp_data->caps.settime64 = ksz8463_ptp_settime; + ptp_data->caps.adjfine = ksz_ptp_adjfine; + ptp_data->caps.adjtime = ksz8463_ptp_adjtime; + ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work; + ptp_data->caps.enable = ksz8463_ptp_enable; + ptp_data->caps.verify = ksz_ptp_verify_pin; + ptp_data->caps.n_pins = dev->info->n_pins; + ptp_data->caps.n_per_out = dev->info->n_per_out; +} + +void ksz_ptp_set_caps(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - const u16 *regs = dev->info->regs; struct ksz_ptp_data *ptp_data; - int ret; - u8 i; ptp_data = &dev->ptp_data; - mutex_init(&ptp_data->lock); - spin_lock_init(&ptp_data->clock_lock); ptp_data->caps.owner = THIS_MODULE; snprintf(ptp_data->caps.name, 16, "Microchip Clock"); @@ -1050,14 +1381,27 @@ int ksz_ptp_clock_register(struct dsa_switch *ds) ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work; ptp_data->caps.enable = ksz_ptp_enable; ptp_data->caps.verify = ksz_ptp_verify_pin; - ptp_data->caps.n_pins = KSZ_PTP_N_GPIO; - ptp_data->caps.n_per_out = 3; + ptp_data->caps.n_pins = dev->info->n_pins; + ptp_data->caps.n_per_out = dev->info->n_per_out; +} + +int ksz_ptp_clock_register(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + const u16 *regs = dev->info->regs; + struct ksz_ptp_data *ptp_data; + int ret; + u8 i; + + ptp_data = &dev->ptp_data; + mutex_init(&ptp_data->lock); + spin_lock_init(&ptp_data->clock_lock); ret = ksz_ptp_start_clock(dev); if (ret) return ret; - for (i = 0; i < KSZ_PTP_N_GPIO; i++) { + for (i = 0; i < dev->info->n_pins; i++) { struct ptp_pin_desc *ptp_pin = &ptp_data->pin_config[i]; snprintf(ptp_pin->name, diff --git a/drivers/net/dsa/microchip/ksz_ptp.h b/drivers/net/dsa/microchip/ksz_ptp.h index 7067ec9bd1e6..22898a22723c 100644 --- a/drivers/net/dsa/microchip/ksz_ptp.h +++ b/drivers/net/dsa/microchip/ksz_ptp.h @@ -12,7 +12,7 @@ #include <linux/ptp_clock_kernel.h> -#define KSZ_PTP_N_GPIO 2 +#define KSZ_PTP_N_GPIO 12 enum ksz_ptp_tou_mode { KSZ_PTP_TOU_IDLE, @@ -29,10 +29,11 @@ struct ksz_ptp_data { spinlock_t clock_lock; struct timespec64 clock_time; enum ksz_ptp_tou_mode tou_mode; - struct timespec64 perout_target_time_first; /* start of first pulse */ - struct timespec64 perout_period; + struct ptp_perout_request perout_request; }; +void ksz_ptp_set_caps(struct dsa_switch *ds); +void ksz8463_ptp_set_caps(struct dsa_switch *ds); int ksz_ptp_clock_register(struct dsa_switch *ds); void ksz_ptp_clock_unregister(struct dsa_switch *ds); @@ -65,6 +66,8 @@ struct ksz_ptp_data { struct mutex lock; }; +static inline void ksz_ptp_set_caps(struct dsa_switch *ds) { } +static inline void ksz8463_ptp_set_caps(struct dsa_switch *ds) { } static inline int ksz_ptp_clock_register(struct dsa_switch *ds) { return 0; diff --git a/drivers/net/dsa/microchip/ksz_ptp_reg.h b/drivers/net/dsa/microchip/ksz_ptp_reg.h index 65ea8577af75..16f117fa5d49 100644 --- a/drivers/net/dsa/microchip/ksz_ptp_reg.h +++ b/drivers/net/dsa/microchip/ksz_ptp_reg.h @@ -51,6 +51,22 @@ #define REG_PTP_UNIT_INDEX__4 0x0520 +#define KSZ8463_TOU_ACTIVE 0x202 +#define KSZ8463_TOU_EN 0x206 +#define KSZ8463_TOU_SW_RST 0x208 + +#define KSZ8463_TRIGN_CFG_SIZE 0x20 +#define KSZ8463_TRIG1_CFG 0x220 +#define KSZ8463_TARGET_NSEC 0x000 +#define KSZ8463_TARGET_SEC 0x004 +#define KSZ8463_PATTERN_OFF 0x008 +#define KSZ8463_NOTIFY_BIT BIT(8) +#define KSZ8463_PATTERN_M GENMASK(6, 4) +#define KSZ8463_PULSE_WIDTH_OFF 0x00A +#define KSZ8463_TRIG_PULSE_WIDTH_M GENMASK(15, 0) +#define KSZ8463_CYCLE_WIDTH_OFF 0x00C +#define KSZ8463_CYCLE_CNT_OFF 0x010 + #define PTP_GPIO_INDEX GENMASK(19, 16) #define PTP_TSI_INDEX BIT(8) #define PTP_TOU_INDEX GENMASK(1, 0) diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c index 373e9054947c..e3bb9e604186 100644 --- a/drivers/net/dsa/microchip/ksz_spi.c +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -161,7 +161,8 @@ static int ksz_spi_probe(struct spi_device *spi) chip->chip_id == KSZ8794_CHIP_ID || chip->chip_id == KSZ8765_CHIP_ID) regmap_config = ksz8795_regmap_config; - else if (chip->chip_id == KSZ8895_CHIP_ID || + else if (chip->chip_id == KSZ8995XA_CHIP_ID || + chip->chip_id == KSZ8895_CHIP_ID || chip->chip_id == KSZ8864_CHIP_ID) regmap_config = ksz8863_regmap_config; else @@ -185,7 +186,10 @@ static int ksz_spi_probe(struct spi_device *spi) dev->pdata = spi->dev.platform_data; /* setup spi */ - spi->mode = SPI_MODE_3; + if (chip->chip_id == KSZ8995XA_CHIP_ID) + spi->mode = SPI_MODE_0; + else + spi->mode = SPI_MODE_3; ret = spi_setup(spi); if (ret) return ret; @@ -224,6 +228,25 @@ static void ksz_spi_shutdown(struct spi_device *spi) } static const struct of_device_id ksz_dt_ids[] = { + /* + * Legacy Micrel bindings. In 2015 Microchip acquired + * Micrel which is the originator of the KSZ series, and + * devices branded for Micrel already existed, as well as + * some device tree bindings. These two products are identical + * to the same Microchip products. + */ + { + .compatible = "micrel,ksz8864", + .data = &ksz_switch_chips[KSZ8864] + }, + { + .compatible = "micrel,ksz8795", + .data = &ksz_switch_chips[KSZ8795] + }, + { + .compatible = "micrel,ks8995", + .data = &ksz_switch_chips[KSZ8995XA] + }, { .compatible = "microchip,ksz8463", .data = &ksz_switch_chips[KSZ8463] @@ -257,6 +280,10 @@ static const struct of_device_id ksz_dt_ids[] = { .data = &ksz_switch_chips[KSZ8895] }, { + .compatible = "microchip,ksz8995xa", + .data = &ksz_switch_chips[KSZ8995XA] + }, + { .compatible = "microchip,ksz9477", .data = &ksz_switch_chips[KSZ9477] }, @@ -317,6 +344,7 @@ static const struct of_device_id ksz_dt_ids[] = { MODULE_DEVICE_TABLE(of, ksz_dt_ids); static const struct spi_device_id ksz_spi_ids[] = { + { "ks8995" }, { "ksz8463" }, { "ksz8765" }, { "ksz8794" }, @@ -325,6 +353,7 @@ static const struct spi_device_id ksz_spi_ids[] = { { "ksz8864" }, { "ksz8873" }, { "ksz8895" }, + { "ksz8995xa" }, { "ksz9477" }, { "ksz9896" }, { "ksz9897" }, diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c index 86ce3a86705f..3a209122fc7d 100644 --- a/drivers/net/dsa/microchip/lan937x_main.c +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -867,6 +867,7 @@ static int lan937x_setup(struct dsa_switch *ds) } } + ksz_ptp_set_caps(ds); ret = ksz_ptp_clock_register(ds); if (ret) { dev_err(dev->dev, "Failed to register PTP clock: %d\n", diff --git a/drivers/net/dsa/motorcomm/Kconfig b/drivers/net/dsa/motorcomm/Kconfig new file mode 100644 index 000000000000..79cdd79a1fd2 --- /dev/null +++ b/drivers/net/dsa/motorcomm/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_YT921X + tristate "Motorcomm YT9215 ethernet switch chip support" + select NET_DSA_TAG_YT921X + select NET_IEEE8021Q_HELPERS if DCB + help + This enables support for the Motorcomm YT9215 ethernet switch + chip. + +config NET_DSA_YT921X_LEDS + bool "LED support for Motorcomm YT9215" + default y + depends on NET_DSA_YT921X + depends on LEDS_CLASS=y || LEDS_CLASS=NET_DSA_YT921X + help + This enables support for controlling the LEDs attached to the + Motorcomm YT9215 switch chips. diff --git a/drivers/net/dsa/motorcomm/Makefile b/drivers/net/dsa/motorcomm/Makefile new file mode 100644 index 000000000000..9eaa59f699e8 --- /dev/null +++ b/drivers/net/dsa/motorcomm/Makefile @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_YT921X) += yt921x.o +yt921x-objs := chip.o +yt921x-$(CONFIG_NET_DSA_YT921X_LEDS) += leds.o +yt921x-objs += mdio_bus.o +yt921x-objs += mib.o +yt921x-objs += pcs-921x.o +yt921x-objs += smi.o +yt921x-objs += tc.o diff --git a/drivers/net/dsa/motorcomm/chip.c b/drivers/net/dsa/motorcomm/chip.c new file mode 100644 index 000000000000..d4fc20c52a58 --- /dev/null +++ b/drivers/net/dsa/motorcomm/chip.c @@ -0,0 +1,2680 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Motorcomm YT921x Switch + * + * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII, + * be sure to do your own checks before porting to another chip. + * + * Copyright (c) 2025 David Yang + */ + +#include <linux/dcbnl.h> +#include <linux/etherdevice.h> +#include <linux/if_bridge.h> +#include <linux/if_hsr.h> +#include <linux/if_vlan.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_mdio.h> +#include <linux/of_net.h> +#include <linux/sort.h> +#include <linux/u64_stats_sync.h> + +#include <net/dsa.h> +#include <net/dscp.h> +#include <net/ieee8021q.h> + +#include "chip.h" +#include "leds.h" +#include "mdio_bus.h" +#include "mib.h" +#include "pcs.h" +#include "smi.h" +#include "tc.h" + +static const struct yt921x_info yt921x_infos[] = { + { + "YT9215SC", YT9215_MAJOR, 1, 0, + GENMASK(4, 0), + BIT(9), + BIT(8) | BIT(9), + }, + { + "YT9215S", YT9215_MAJOR, 2, 0, + GENMASK(4, 0), + BIT(9), + BIT(8), + }, + { + "YT9215RB", YT9215_MAJOR, 3, 0, + GENMASK(4, 0), + BIT(8) | BIT(9), + 0, + }, + { + "YT9214NB", YT9215_MAJOR, 3, 2, + BIT(1) | BIT(3), + BIT(9), + BIT(8), + }, + { + "YT9213NB", YT9215_MAJOR, 3, 3, + BIT(1) | BIT(3), + BIT(9), + BIT(9), + }, + { + "YT9218N", YT9218_MAJOR, 0, 0, + GENMASK(7, 0), + 0, + 0, + }, + { + "YT9218MB", YT9218_MAJOR, 1, 0, + GENMASK(7, 0), + BIT(8) | BIT(9), + BIT(8) | BIT(9), + }, + {} +}; + +#define YT921X_VID_UNWARE 4095 + +struct yt921x_reg_mdio { + struct mii_bus *bus; + int addr; + /* SWITCH_ID_1 / SWITCH_ID_0 of the device + * + * This is a way to multiplex multiple devices on the same MII phyaddr + * and should be configurable in DT. However, MDIO core simply doesn't + * allow multiple devices over one reg addr, so this is a fixed value + * for now until a solution is found. + * + * Keep this because we need switchid to form MII regaddrs anyway. + */ + unsigned char switchid; +}; + +/* TODO: SPI/I2C */ + +static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) +{ + struct yt921x_reg_mdio *mdio = context; + struct mii_bus *bus = mdio->bus; + int addr = mdio->addr; + u32 reg_addr; + u32 reg_data; + u32 val; + int res; + + /* Hold the mdio bus lock to avoid (un)locking for 4 times */ + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | + YT921X_SMI_READ; + res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); + if (res) + goto end; + res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); + if (res) + goto end; + + reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | + YT921X_SMI_READ; + res = __mdiobus_read(bus, addr, reg_data); + if (res < 0) + goto end; + val = (u16)res; + res = __mdiobus_read(bus, addr, reg_data); + if (res < 0) + goto end; + val = (val << 16) | (u16)res; + + *valp = val; + res = 0; + +end: + mutex_unlock(&bus->mdio_lock); + return res; +} + +static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val) +{ + struct yt921x_reg_mdio *mdio = context; + struct mii_bus *bus = mdio->bus; + int addr = mdio->addr; + u32 reg_addr; + u32 reg_data; + int res; + + mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); + + reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | + YT921X_SMI_WRITE; + res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); + if (res) + goto end; + res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); + if (res) + goto end; + + reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | + YT921X_SMI_WRITE; + res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16)); + if (res) + goto end; + res = __mdiobus_write(bus, addr, reg_data, (u16)val); + if (res) + goto end; + + res = 0; + +end: + mutex_unlock(&bus->mdio_lock); + return res; +} + +static const struct yt921x_reg_ops yt921x_reg_ops_mdio = { + .read = yt921x_reg_mdio_read, + .write = yt921x_reg_mdio_write, +}; + +/* TODO: SPI/I2C */ + +static int +yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e) +{ + /* Poor datasheet for EEE operations; don't ask if you are confused */ + + bool enable = e->eee_enabled; + u16 new_mask; + int res; + + /* Enable / disable global EEE */ + new_mask = priv->eee_ports_mask; + new_mask &= ~BIT(port); + new_mask |= !enable ? 0 : BIT(port); + + if (!!new_mask != !!priv->eee_ports_mask) { + res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC, + YT921X_PON_STRAP_EEE, !!new_mask); + if (res) + return res; + res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL, + YT921X_PON_STRAP_EEE, !!new_mask); + if (res) + return res; + } + + priv->eee_ports_mask = new_mask; + + /* Enable / disable port EEE */ + res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL, + YT921X_EEE_CTRL_ENn(port), enable); + if (res) + return res; + res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port), + YT921X_EEE_VAL_DATA, enable); + if (res) + return res; + + return 0; +} + +static int +yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_set_eee(priv, port, e); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) +{ + /* Only serves as packet filter, since the frame size is always set to + * maximum after reset + */ + + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct dsa_port *dp = dsa_to_port(ds, port); + int frame_size; + int res; + + frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN; + if (dsa_port_is_cpu(dp)) + frame_size += YT921X_TAG_LEN; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port), + YT921X_MAC_FRAME_SIZE_M, + YT921X_MAC_FRAME_SIZE(frame_size)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) +{ + /* Only called for user ports, exclude tag len here */ + return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; +} + +static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag, + struct netlink_ext_ack *extack) +{ + u32 val; + int res; + + /* Hash Mode is global. Make sure the same Hash Mode is set to all the + * 2 possible lags. + * If we are the unique LAG we can set whatever hash mode we want. + * To change hash mode it's needed to remove all LAG and change the mode + * with the latest. + */ + if (unique_lag) { + res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl); + if (res) + return res; + } else { + res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val); + if (res) + return res; + + if (val != ctrl) { + NL_SET_ERR_MSG_MOD(extack, + "Mismatched Hash Mode across different lags is not supported"); + return -EOPNOTSUPP; + } + } + + return 0; +} + +static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask) +{ + unsigned long targets_mask = ports_mask; + unsigned int cnt; + u32 ctrl; + int port; + int res; + + cnt = 0; + for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { + ctrl = YT921X_LAG_MEMBER_PORT(port); + res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt), + ctrl); + if (res) + return res; + + cnt++; + } + + ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) | + YT921X_LAG_GROUP_MEMBER_NUM(cnt); + return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl); +} + +static int +yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct dsa_port *dp; + u32 ctrl; + int res; + + if (!lag.id) + return -EINVAL; + + ctrl = 0; + dsa_lag_foreach_port(dp, ds->dst, &lag) + ctrl |= BIT(dp->index); + + mutex_lock(&priv->reg_lock); + res = yt921x_lag_set(priv, lag.id - 1, ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag, + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) +{ + unsigned int members; + struct dsa_port *dp; + + if (!lag.id) + return -EINVAL; + + members = 0; + dsa_lag_foreach_port(dp, ds->dst, &lag) + /* Includes the port joining the LAG */ + members++; + + if (members > YT921X_LAG_PORT_NUM) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot offload more than 4 LAG ports"); + return -EOPNOTSUPP; + } + + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { + NL_SET_ERR_MSG_MOD(extack, + "Can only offload LAG using hash TX type"); + return -EOPNOTSUPP; + } + + if (info->hash_type != NETDEV_LAG_HASH_L2 && + info->hash_type != NETDEV_LAG_HASH_L23 && + info->hash_type != NETDEV_LAG_HASH_L34) { + NL_SET_ERR_MSG_MOD(extack, + "Can only offload L2 or L2+L3 or L3+L4 TX hash"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int +yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag, + struct netdev_lag_upper_info *info, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct dsa_port *dp; + bool unique_lag; + unsigned int i; + u32 ctrl; + int res; + + res = yt921x_dsa_port_lag_check(ds, lag, info, extack); + if (res) + return res; + + ctrl = 0; + switch (info->hash_type) { + case NETDEV_LAG_HASH_L34: + ctrl |= YT921X_LAG_HASH_IP_DST; + ctrl |= YT921X_LAG_HASH_IP_SRC; + ctrl |= YT921X_LAG_HASH_IP_PROTO; + + ctrl |= YT921X_LAG_HASH_L4_DPORT; + ctrl |= YT921X_LAG_HASH_L4_SPORT; + break; + case NETDEV_LAG_HASH_L23: + ctrl |= YT921X_LAG_HASH_MAC_DA; + ctrl |= YT921X_LAG_HASH_MAC_SA; + + ctrl |= YT921X_LAG_HASH_IP_DST; + ctrl |= YT921X_LAG_HASH_IP_SRC; + ctrl |= YT921X_LAG_HASH_IP_PROTO; + break; + case NETDEV_LAG_HASH_L2: + ctrl |= YT921X_LAG_HASH_MAC_DA; + ctrl |= YT921X_LAG_HASH_MAC_SA; + break; + default: + return -EOPNOTSUPP; + } + + /* Check if we are the unique configured LAG */ + unique_lag = true; + dsa_lags_foreach_id(i, ds->dst) + if (i != lag.id && dsa_lag_by_id(ds->dst, i)) { + unique_lag = false; + break; + } + + mutex_lock(&priv->reg_lock); + do { + res = yt921x_lag_hash(priv, ctrl, unique_lag, extack); + if (res) + break; + + ctrl = 0; + dsa_lag_foreach_port(dp, ds->dst, &lag) + ctrl |= BIT(dp->index); + res = yt921x_lag_set(priv, lag.id - 1, ctrl); + } while (0); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp) +{ + struct device *dev = yt921x_priv_to_device(priv); + u32 val = YT921X_FDB_RESULT_DONE; + int res; + + res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE, + &val); + if (res) { + dev_err(dev, "FDB probably stuck\n"); + return res; + } + + *valp = val; + return 0; +} + +static int +yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, + u16 vid, u32 ctrl1) +{ + u32 ctrl; + int res; + + ctrl = ethaddr_hi4_to_u32(addr); + res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); + if (res) + return res; + + ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr); + return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); +} + +static int +yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, + u16 *indexp) +{ + u32 ctrl; + u32 val; + int res; + + res = yt921x_fdb_in01(priv, addr, vid, 0); + if (res) + return res; + + ctrl = 0; + res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res = yt921x_fdb_wait(priv, &val); + if (res) + return res; + if (val & YT921X_FDB_RESULT_NOTFOUND) { + *indexp = YT921X_FDB_NUM; + return 0; + } + + *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); + return 0; +} + +static int +yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp, + u16 *ports_maskp, u16 *indexp, u8 *statusp) +{ + struct device *dev = yt921x_priv_to_device(priv); + u16 index; + u32 data0; + u32 data1; + u32 data2; + u32 val; + int res; + + res = yt921x_fdb_wait(priv, &val); + if (res) + return res; + if (val & YT921X_FDB_RESULT_NOTFOUND) { + *ports_maskp = 0; + return 0; + } + index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); + + res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1); + if (res) + return res; + if ((data1 & YT921X_FDB_IO1_STATUS_M) == + YT921X_FDB_IO1_STATUS_INVALID) { + *ports_maskp = 0; + return 0; + } + + res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2); + if (res) + return res; + + addr[0] = data0 >> 24; + addr[1] = data0 >> 16; + addr[2] = data0 >> 8; + addr[3] = data0; + addr[4] = data1 >> 8; + addr[5] = data1; + *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1); + *indexp = index; + *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2); + *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1); + + dev_dbg(dev, + "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n", + __func__, *indexp, addr[0], addr[1], addr[2], addr[3], + addr[4], addr[5], *vidp, *ports_maskp, *statusp); + return 0; +} + +static int +yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask, + dsa_fdb_dump_cb_t *cb, void *data) +{ + unsigned char addr[ETH_ALEN]; + u8 status; + u16 pmask; + u16 index; + u32 ctrl; + u16 vid; + int res; + + ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status); + if (res) + return res; + if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { + res = cb(addr, vid, + status == YT921X_FDB_ENTRY_STATUS_STATIC, data); + if (res) + return res; + } + + ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + index = 0; + do { + ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT | + YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, + &status); + if (res) + return res; + if (!pmask) + break; + + if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { + res = cb(addr, vid, + status == YT921X_FDB_ENTRY_STATUS_STATIC, + data); + if (res) + return res; + } + + /* Never call GET_NEXT with 4095, otherwise it will hang + * forever until a reset! + */ + } while (index < YT921X_FDB_NUM - 1); + + return 0; +} + +static int +yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid, + bool flush_static) +{ + u32 ctrl; + u32 val; + int res; + + if (vid < 4096) { + ctrl = YT921X_FDB_IO1_FID(vid); + res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); + if (res) + return res; + } + + ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START; + if (vid >= 4096) + ctrl |= YT921X_FDB_OP_FLUSH_PORT; + else + ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID; + if (flush_static) + ctrl |= YT921X_FDB_OP_FLUSH_STATIC; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + res = yt921x_fdb_wait(priv, &val); + if (res) + return res; + + return 0; +} + +static int +yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static) +{ + return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static); +} + +static int +yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1, + u16 ctrl2) +{ + u32 ctrl; + u32 val; + int res; + + res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2); + if (res) + return res; + + ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | + YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); +} + +static int +yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, + u16 ports_mask) +{ + u32 ctrl; + u32 val; + int res; + + ctrl = YT921X_FDB_IO1_STATUS_STATIC; + res = yt921x_fdb_in01(priv, addr, vid, ctrl); + if (res) + return res; + + ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); + res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); + if (res) + return res; + + ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); +} + +static int +yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr, + u16 vid, u16 ports_mask) +{ + u16 index; + u32 ctrl1; + u32 ctrl2; + u32 ctrl; + u32 val2; + u32 val; + int res; + + /* Check for presence */ + res = yt921x_fdb_has(priv, addr, vid, &index); + if (res) + return res; + if (index >= YT921X_FDB_NUM) + return 0; + + /* Check if action required */ + res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); + if (res) + return res; + + ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask); + if (ctrl2 == val2) + return 0; + if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) { + ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START; + res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); + if (res) + return res; + + return yt921x_fdb_wait(priv, &val); + } + + res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1); + if (res) + return res; + + return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); +} + +static int +yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, + u16 ports_mask) +{ + u16 index; + u32 ctrl1; + u32 ctrl2; + u32 val1; + u32 val2; + int res; + + /* Check for presence */ + res = yt921x_fdb_has(priv, addr, vid, &index); + if (res) + return res; + if (index >= YT921X_FDB_NUM) + return yt921x_fdb_add(priv, addr, vid, ports_mask); + + /* Check if action required */ + res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); + if (res) + return res; + + ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M; + ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC; + ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask); + if (ctrl1 == val1 && ctrl2 == val2) + return 0; + + return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); +} + +static int +yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" + * only wants to see unicast + */ + res = yt921x_fdb_dump(priv, BIT(port), cb, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_fdb_flush_port(priv, port, false); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for", + port, res); +} + +static int +yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 ctrl; + int res; + + /* AGEING reg is set in 5s step */ + ctrl = clamp(msecs / 5000, 1, U16_MAX); + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_write(priv, YT921X_AGEING, ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_fdb_join(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + const unsigned char *addr = mdb->addr; + u16 vid = mdb->vid; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + const unsigned char *addr = mdb->addr; + u16 vid = mdb->vid; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_fdb_join(priv, addr, vid, BIT(port)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware) +{ + u32 ctrl; + + /* Abuse SVLAN for PCP parsing without polluting the FDB - it just works + * despite YT921X_VLAN_CTRL_SVLAN_EN never being set + */ + if (!vlan_aware) + ctrl = YT921X_PORT_IGR_TPIDn_STAG(0); + else + ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0); + return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl); +} + +static int +yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid) +{ + u32 mask; + u32 ctrl; + + mask = YT921X_PORT_VLAN_CTRL_CVID_M; + ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid); + return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port), + mask, ctrl); +} + +static int +yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering) +{ + struct dsa_port *dp = dsa_to_port(&priv->ds, port); + struct net_device *bdev; + u16 pvid; + u32 mask; + u32 ctrl; + int res; + + bdev = dsa_port_bridge_dev_get(dp); + + if (!bdev || !vlan_filtering) + pvid = YT921X_VID_UNWARE; + else + br_vlan_get_pvid(bdev, &pvid); + res = yt921x_port_set_pvid(priv, port, pvid); + if (res) + return res; + + mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED | + YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + ctrl = 0; + /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */ + if (vlan_filtering && !pvid) + ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), + mask, ctrl); + if (res) + return res; + + res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER, + YT921X_VLAN_IGR_FILTER_PORTn(port), + vlan_filtering); + if (res) + return res; + + res = yt921x_vlan_aware_set(priv, port, vlan_filtering); + if (res) + return res; + + return 0; +} + +static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) +{ + u32 masks[2]; + + masks[0] = YT921X_VLAN_CTRLa_PORTn(port); + masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); + + return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks); +} + +static int +yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) +{ + u32 masks[2]; + u32 ctrls[2]; + + masks[0] = YT921X_VLAN_CTRLa_PORTn(port) | + YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask); + ctrls[0] = masks[0]; + + masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); + ctrls[1] = untagged ? masks[1] : 0; + + return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), + masks, ctrls); +} + +static int +yt921x_pvid_clear(struct yt921x_priv *priv, int port) +{ + struct dsa_port *dp = dsa_to_port(&priv->ds, port); + bool vlan_filtering; + u32 mask; + int res; + + vlan_filtering = dsa_port_is_vlan_filtering(dp); + + res = yt921x_port_set_pvid(priv, port, + vlan_filtering ? 0 : YT921X_VID_UNWARE); + if (res) + return res; + + if (vlan_filtering) { + mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), + mask); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid) +{ + struct dsa_port *dp = dsa_to_port(&priv->ds, port); + bool vlan_filtering; + u32 mask; + int res; + + vlan_filtering = dsa_port_is_vlan_filtering(dp); + + if (vlan_filtering) { + res = yt921x_port_set_pvid(priv, port, vid); + if (res) + return res; + } + + mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; + res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask); + if (res) + return res; + + return 0; +} + +static int +yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + res = yt921x_vlan_filtering(priv, port, vlan_filtering); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u16 vid = vlan->vid; + u16 pvid; + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + do { + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *bdev; + + res = yt921x_vlan_del(priv, port, vid); + if (res) + break; + + bdev = dsa_port_bridge_dev_get(dp); + if (bdev) { + br_vlan_get_pvid(bdev, &pvid); + if (pvid == vid) + res = yt921x_pvid_clear(priv, port); + } + } while (0); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u16 vid = vlan->vid; + u16 pvid; + int res; + + /* CPU port is supposed to be a member of every VLAN; see + * yt921x_vlan_add() and yt921x_port_setup() + */ + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + do { + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *bdev; + + res = yt921x_vlan_add(priv, port, vid, + vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); + if (res) + break; + + bdev = dsa_port_bridge_dev_get(dp); + if (bdev) { + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { + res = yt921x_pvid_set(priv, port, vid); + } else { + br_vlan_get_pvid(bdev, &pvid); + if (pvid == vid) + res = yt921x_pvid_clear(priv, port); + } + } + } while (0); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_userport_standalone(struct yt921x_priv *priv, int port) +{ + u32 mask; + u32 ctrl; + int res; + + ctrl = ~priv->cpu_ports_mask; + res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl); + if (res) + return res; + + /* Turn off FDB learning to prevent FDB pollution */ + mask = YT921X_PORT_LEARN_DIS; + res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask); + if (res) + return res; + + /* Turn off VLAN awareness */ + res = yt921x_vlan_aware_set(priv, port, false); + if (res) + return res; + + /* Unrelated since learning is off and all packets are trapped; + * set it anyway + */ + res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE); + if (res) + return res; + + return 0; +} + +static int yt921x_userport_bridge(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + mask = YT921X_PORT_LEARN_DIS; + res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask); + if (res) + return res; + + return 0; +} + +static int yt921x_isolate(struct yt921x_priv *priv, int port) +{ + u32 mask; + int res; + + mask = BIT(port); + for (int i = 0; i < YT921X_PORT_NUM; i++) { + if ((BIT(i) & priv->cpu_ports_mask) || i == port) + continue; + + res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i), + mask); + if (res) + return res; + } + + return 0; +} + +/* Make sure to include the CPU port in ports_mask, or your bridge will + * not have it. + */ +static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask) +{ + unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask; + u32 isolated_mask; + u32 ctrl; + int port; + int res; + + isolated_mask = 0; + for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { + struct yt921x_port *pp = &priv->ports[port]; + + if (pp->isolated) + isolated_mask |= BIT(port); + } + + /* Block from non-cpu bridge ports ... */ + for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { + struct yt921x_port *pp = &priv->ports[port]; + + /* to non-bridge ports */ + ctrl = ~ports_mask; + /* to isolated ports when isolated */ + if (pp->isolated) + ctrl |= isolated_mask; + /* to itself when non-hairpin */ + if (!pp->hairpin) + ctrl |= BIT(port); + else + ctrl &= ~BIT(port); + + res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), + ctrl); + if (res) + return res; + } + + return 0; +} + +static int yt921x_bridge_leave(struct yt921x_priv *priv, int port) +{ + int res; + + res = yt921x_userport_standalone(priv, port); + if (res) + return res; + + res = yt921x_isolate(priv, port); + if (res) + return res; + + return 0; +} + +static int +yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask) +{ + int res; + + res = yt921x_userport_bridge(priv, port); + if (res) + return res; + + res = yt921x_bridge(priv, ports_mask); + if (res) + return res; + + return 0; +} + +static int +yt921x_bridge_flags(struct yt921x_priv *priv, int port, + struct switchdev_brport_flags flags) +{ + struct yt921x_port *pp = &priv->ports[port]; + bool do_flush; + u32 mask; + int res; + + if (flags.mask & BR_LEARNING) { + bool learning = flags.val & BR_LEARNING; + + mask = YT921X_PORT_LEARN_DIS; + res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port), + mask, !learning); + if (res) + return res; + } + + /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP + * is set + */ + + /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */ + + do_flush = false; + if (flags.mask & BR_HAIRPIN_MODE) { + pp->hairpin = flags.val & BR_HAIRPIN_MODE; + do_flush = true; + } + if (flags.mask & BR_ISOLATED) { + pp->isolated = flags.val & BR_ISOLATED; + do_flush = true; + } + if (do_flush) { + struct dsa_switch *ds = &priv->ds; + struct dsa_port *dp = dsa_to_port(ds, port); + struct net_device *bdev; + + bdev = dsa_port_bridge_dev_get(dp); + if (bdev) { + u32 ports_mask; + + ports_mask = dsa_bridge_ports(ds, bdev); + ports_mask |= priv->cpu_ports_mask; + res = yt921x_bridge(priv, ports_mask); + if (res) + return res; + } + } + + return 0; +} + +static int +yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD | + BR_MCAST_FLOOD | BR_ISOLATED)) + return -EINVAL; + return 0; +} + +static int +yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + mutex_lock(&priv->reg_lock); + res = yt921x_bridge_flags(priv, port, flags); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static void +yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + int res; + + if (dsa_is_cpu_port(ds, port)) + return; + + mutex_lock(&priv->reg_lock); + res = yt921x_bridge_leave(priv, port); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", + port, res); +} + +static int +yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, bool *tx_fwd_offload, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u16 ports_mask; + int res; + + if (dsa_is_cpu_port(ds, port)) + return 0; + + ports_mask = dsa_bridge_ports(ds, bridge.dev); + ports_mask |= priv->cpu_ports_mask; + + mutex_lock(&priv->reg_lock); + res = yt921x_bridge_join(priv, port, ports_mask); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port, + const struct switchdev_mst_state *st) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 mask; + u32 ctrl; + int res; + + mask = YT921X_STP_PORTn_M(port); + switch (st->state) { + case BR_STATE_DISABLED: + ctrl = YT921X_STP_PORTn_DISABLED(port); + break; + case BR_STATE_LISTENING: + case BR_STATE_LEARNING: + ctrl = YT921X_STP_PORTn_LEARNING(port); + break; + case BR_STATE_FORWARDING: + default: + ctrl = YT921X_STP_PORTn_FORWARD(port); + break; + case BR_STATE_BLOCKING: + ctrl = YT921X_STP_PORTn_BLOCKING(port); + break; + } + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, + const struct switchdev_vlan_msti *msti) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 masks[2]; + u32 ctrls[2]; + int res; + + if (!msti->vid) + return -EINVAL; + if (!msti->msti || msti->msti >= YT921X_MSTI_NUM) + return -EINVAL; + + masks[0] = 0; + ctrls[0] = 0; + masks[1] = YT921X_VLAN_CTRLb_STP_ID_M; + ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti); + + mutex_lock(&priv->reg_lock); + res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid), + masks, ctrls); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static void +yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + struct dsa_port *dp = dsa_to_port(ds, port); + bool learning; + u32 mask; + u32 ctrl; + int res; + + mask = YT921X_STP_PORTn_M(port); + learning = false; + switch (state) { + case BR_STATE_DISABLED: + ctrl = YT921X_STP_PORTn_DISABLED(port); + break; + case BR_STATE_LISTENING: + ctrl = YT921X_STP_PORTn_LEARNING(port); + break; + case BR_STATE_LEARNING: + ctrl = YT921X_STP_PORTn_LEARNING(port); + learning = dp->learning; + break; + case BR_STATE_FORWARDING: + default: + ctrl = YT921X_STP_PORTn_FORWARD(port); + learning = dp->learning; + break; + case BR_STATE_BLOCKING: + ctrl = YT921X_STP_PORTn_BLOCKING(port); + break; + } + + mutex_lock(&priv->reg_lock); + do { + res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl); + if (res) + break; + + mask = YT921X_PORT_LEARN_DIS; + ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), + mask, ctrl); + } while (0); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for", + port, res); +} + +static int __maybe_unused +yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 val; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val); +} + +static int __maybe_unused +yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 mask; + u32 ctrl; + int res; + + if (prio >= YT921X_PRIO_NUM) + return -EINVAL; + + mutex_lock(&priv->reg_lock); + mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN; + ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int __maybe_unused appprios_cmp(const void *a, const void *b) +{ + return ((const u8 *)b)[1] - ((const u8 *)a)[1]; +} + +static int __maybe_unused +yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, + int *nselp) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u8 appprios[2][2] = {}; + int nsel; + u32 val; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP; + appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7; + appprios[1][0] = DCB_APP_SEL_PCP; + appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7; + sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp, + NULL); + + nsel = 0; + for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) { + sel[nsel] = appprios[i][0]; + nsel++; + } + *nselp = nsel; + + return 0; +} + +static int __maybe_unused +yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, + int nsel) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + u32 ctrl; + int res; + + if (nsel > YT921X_APP_SEL_NUM) + return -EINVAL; + + ctrl = 0; + for (int i = 0; i < nsel; i++) { + switch (sel[i]) { + case IEEE_8021QAZ_APP_SEL_DSCP: + ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP, + 7 - i); + break; + case DCB_APP_SEL_PCP: + ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP, + 7 - i); + ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP, + 7 - i); + break; + default: + dev_err(dev, + "Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n", + i + 1); + return -EOPNOTSUPP; + } + } + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_port_down(struct yt921x_priv *priv, int port) +{ + const struct yt921x_info *info = priv->info; + u32 mask; + int res; + + mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_RX_MAC_EN | + YT921X_PORT_TX_MAC_EN; + res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); + if (res) + return res; + + if (BIT(port) & info->serdes_mask) { + mask = YT921X_SERDES_LINK; + res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); + if (res) + return res; + } + if (BIT(port) & info->xmii_mask) { + mask = YT921X_XMII_LINK; + res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + const struct yt921x_info *info = priv->info; + struct yt921x_port *pp = &priv->ports[port]; + int ps = ethtool_speed_to_yt921x(speed); + u32 mask; + u32 ctrl; + int res; + + if (ps == YT921X_SPEED_INVALID) + return -EINVAL; + + mask = YT921X_PORT_SPEED_M | YT921X_PORT_TX_MAC_EN | + YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_PAUSE | + YT921X_PORT_RX_PAUSE | YT921X_PORT_DUPLEX_FULL | + YT921X_PORT_CTRL_LINK_AN; + ctrl = YT921X_PORT_SPEED(ps); + if (duplex == DUPLEX_FULL) + ctrl |= YT921X_PORT_DUPLEX_FULL; + if (tx_pause) + ctrl |= YT921X_PORT_TX_PAUSE; + if (rx_pause) + ctrl |= YT921X_PORT_RX_PAUSE; + ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; + if (pp->serdes && pp->inband) + ctrl |= YT921X_PORT_CTRL_LINK_AN; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); + if (res) + return res; + + if (!(BIT(port) & (info->serdes_mask | info->xmii_mask))) + return 0; + + if (pp->serdes) { + mask = YT921X_SERDES_SPEED_M; + ctrl = YT921X_SERDES_SPEED(ps); + mask |= YT921X_SERDES_DUPLEX_FULL; + if (duplex == DUPLEX_FULL) + ctrl |= YT921X_SERDES_DUPLEX_FULL; + mask |= YT921X_SERDES_TX_PAUSE; + if (tx_pause) + ctrl |= YT921X_SERDES_TX_PAUSE; + mask |= YT921X_SERDES_RX_PAUSE; + if (rx_pause) + ctrl |= YT921X_SERDES_RX_PAUSE; + mask |= YT921X_SERDES_LINK; + if (!pp->inband) + ctrl |= YT921X_SERDES_LINK; + res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), + mask, ctrl); + if (res) + return res; + } else { + mask = YT921X_XMII_LINK; + res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); + if (res) + return res; + + ctrl = YT921X_MDIO_POLLING_SPEED(ps); + if (duplex == DUPLEX_FULL) + ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL; + ctrl |= YT921X_MDIO_POLLING_LINK; + res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, + phy_interface_t interface) +{ + struct device *dev = yt921x_priv_to_device(priv); + const struct yt921x_info *info = priv->info; + struct yt921x_port *pp = &priv->ports[port]; + u32 mask; + int res; + + if (BIT(port) & info->internal_mask) { + if (interface != PHY_INTERFACE_MODE_INTERNAL) + goto err; + return 0; + } + + switch (interface) { + /* SERDES */ + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + if (!(BIT(port) & info->serdes_mask)) + goto err; + + mask = YT921X_SERDES_CTRL_PORTn(port); + res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); + if (res) + return res; + + mask = YT921X_XMII_CTRL_PORTn(port); + res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask); + if (res) + return res; + + /* The order is quite arbitrary - we can't return to a safe + * state on IO errors. + */ + pp->serdes = true; + + break; + /* add XMII support here */ + default: + goto err; + } + + return 0; + +err: + dev_err(dev, "Wrong mode %d on port %d\n", interface, port); + return -EINVAL; +} + +static struct phylink_pcs * +yt921x_phylink_mac_select_pcs(struct phylink_config *config, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + const struct yt921x_info *info = priv->info; + int port = dp->index; + + if (!(BIT(port) & info->serdes_mask)) + return NULL; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_100BASEX: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: + return &priv->ports[port].pcs; + default: + return NULL; + } +} + +static void +yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + struct yt921x_mib *pm; + int port = dp->index; + int res; + + /* No need to sync; port control block is hold until device remove */ + pm = priv->ports[port].mib; + if (pm) + cancel_delayed_work(&pm->work); + + mutex_lock(&priv->reg_lock); + res = yt921x_port_down(priv, port); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", + port, res); +} + +static void +yt921x_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, unsigned int mode, + phy_interface_t interface, int speed, int duplex, + bool tx_pause, bool rx_pause) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + struct yt921x_mib *pm; + int port = dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_port_up(priv, port, mode, interface, speed, duplex, + tx_pause, rx_pause); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", + port, res); + + pm = priv->ports[port].mib; + if (pm) + schedule_delayed_work(&pm->work, 0); +} + +static void +yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode, + const struct phylink_link_state *state) +{ + struct dsa_port *dp = dsa_phylink_to_port(config); + struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); + int port = dp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_port_config(priv, port, mode, state->interface); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", + port, res); +} + +static void +yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + const struct yt921x_info *info = priv->info; + + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_10 | MAC_100 | MAC_1000; + + if (info->internal_mask & BIT(port)) { + /* Port 10 for MCU should probably go here too. But since that + * is untested yet, turn it down for the moment by letting it + * fall to the default branch. + */ + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + return; + } + + if (BIT(port) & info->serdes_mask) { + __set_bit(PHY_INTERFACE_MODE_SGMII, + config->supported_interfaces); + /* REVSGMII (SGMII in PHY role) should go here, once + * PHY_INTERFACE_MODE_REVSGMII is introduced. + */ + __set_bit(PHY_INTERFACE_MODE_100BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + config->supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + config->supported_interfaces); + config->mac_capabilities |= MAC_2500FD; + } + if (BIT(port) & info->xmii_mask) { + /* Not tested. To add support for XMII: + * - Add proper interface modes below + * - Handle them in yt921x_port_config() + */ + } + /* no such port: empty supported_interfaces causes phylink to turn it + * down + */ +} + +static int yt921x_port_setup(struct yt921x_priv *priv, int port) +{ + struct dsa_switch *ds = &priv->ds; + u32 ctrl; + int res; + + res = yt921x_userport_standalone(priv, port); + if (res) + return res; + + /* Clear prio order (even if DCB is not enabled) to avoid unsolicited + * priorities + */ + res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0); + if (res) + return res; + + if (dsa_is_cpu_port(ds, port)) { + /* Egress of CPU port is supposed to be completely controlled + * via tagging, so set to oneway isolated (drop all packets + * without tag). + */ + ctrl = ~(u32)0; + res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), + ctrl); + if (res) + return res; + + /* To simplify FDB "isolation" simulation, we also disable + * learning on the CPU port, and let software identify packets + * towarding CPU (either trapped or a static FDB entry is + * matched, no matter which bridge that entry is for), which is + * already done by yt921x_userport_standalone(). As a result, + * VLAN-awareness becomes unrelated on the CPU port (set to + * VLAN-unaware by the way). + */ + } + + return 0; +} + +static enum dsa_tag_protocol +yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_YT921X; +} + +static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + + /* get_stats64() might not be synchronized with port_teardown(), hold + * the MIB block till the device teardown + */ + if (pm) + disable_delayed_work_sync(&pm->work); +} + +static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + const struct yt921x_info *info = priv->info; + struct device *dev = yt921x_priv_to_device(priv); + struct yt921x_mib *pm = pp->mib; + int res; + + if (!pm && (BIT(port) & yt921x_info_ports_mask(info))) { + pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL); + if (!pm) + return -ENOMEM; + pp->mib = pm; + + pm->port = pp; + u64_stats_init(&pm->syncp); + INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll); + /* Start disabled, so that the enable below always pairs with + * either this disable or the one from a previous teardown + */ + disable_delayed_work_sync(&pm->work); + } + + mutex_lock(&priv->reg_lock); + res = yt921x_port_setup(priv, port); + mutex_unlock(&priv->reg_lock); + if (res) + return res; + + if (pm) + enable_delayed_work(&pm->work); + + return 0; +} + +/* Not "port" - DSCP mapping is global */ +static int __maybe_unused +yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 val; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + return FIELD_GET(YT921X_IPM_PRIO_M, val); +} + +static int __maybe_unused +yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + u32 val; + int res; + + mutex_lock(&priv->reg_lock); + /* During a "dcb app replace" command, the new app table entry will be + * added first, then the old one will be deleted. But the hardware only + * supports one QoS class per DSCP value (duh), so if we blindly delete + * the app table entry for this DSCP value, we end up deleting the + * entry with the new priority. Avoid that by checking whether user + * space wants to delete the priority which is currently configured, or + * something else which is no longer current. + */ + res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); + if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio) + res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), + YT921X_IPM_PRIO(IEEE8021Q_TT_BK)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int __maybe_unused +yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + if (prio >= YT921X_PRIO_NUM) + return -EINVAL; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), + YT921X_IPM_PRIO(prio)); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp) +{ + u32 val = YT921X_EDATA_DATA_IDLE; + int res; + + res = yt921x_reg_wait(priv, YT921X_EDATA_DATA, + YT921X_EDATA_DATA_STATUS_M, &val); + if (res) + return res; + + *valp = val; + return 0; +} + +static int +yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp) +{ + u32 ctrl; + u32 val; + int res; + + ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ; + res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl); + if (res) + return res; + res = yt921x_edata_wait(priv, &val); + if (res) + return res; + + *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val); + return 0; +} + +static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp) +{ + u32 val; + int res; + + res = yt921x_edata_wait(priv, &val); + if (res) + return res; + return yt921x_edata_read_cont(priv, addr, valp); +} + +static void yt921x_dsa_teardown(struct dsa_switch *ds) +{ +#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + + yt921x_leds_remove(priv); +#endif +} + +static int yt921x_chip_detect(struct yt921x_priv *priv) +{ + struct device *dev = yt921x_priv_to_device(priv); + const struct yt921x_info *info; + u8 extmode; + u32 chipid; + u32 major; + u32 mode; + u32 val; + int res; + + res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); + if (res) + return res; + + major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); + + for (info = yt921x_infos; info->name; info++) + if (info->major == major) + break; + if (!info->name) { + dev_err(dev, "Unexpected chipid 0x%x\n", chipid); + return -ENODEV; + } + + res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); + if (res) + return res; + res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode); + if (res) + return res; + + for (; info->name; info++) + if (info->major == major && info->mode == mode && + info->extmode == extmode) + break; + if (!info->name) { + dev_err(dev, + "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n", + chipid, mode, extmode); + return -ENODEV; + } + + res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val); + if (res) + return res; + switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) { + case 0: + priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6; + break; + case YT921X_SYS_CLK_143M: + priv->cycle_ns = 7; + break; + default: + priv->cycle_ns = 8; + } + + /* Print chipid here since we are interested in lower 16 bits */ + dev_info(dev, + "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n", + info->name, chipid, mode, extmode); + + priv->info = info; + + return 0; +} + +static int yt921x_chip_reset(struct yt921x_priv *priv) +{ + struct device *dev = yt921x_priv_to_device(priv); + u16 eth_p_tag; + u32 val; + int res; + + res = yt921x_chip_detect(priv); + if (res) + return res; + + /* Reset */ + res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW); + if (res) + return res; + + /* RST_HW is almost same as GPIO hard reset, so we need this delay. */ + fsleep(YT921X_RST_DELAY_US); + + val = 0; + res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val); + if (res) + return res; + + /* Check for tag EtherType; do it after reset in case you messed it up + * before. + */ + res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val); + if (res) + return res; + eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val); + if (eth_p_tag != ETH_P_YT921X) { + dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag, + ETH_P_YT921X); + /* Despite being possible, we choose not to set CPU_TAG_TPID, + * since there is no way it can be different unless you have the + * wrong chip. + */ + return -EINVAL; + } + + return 0; +} + +static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + unsigned long cpu_ports_mask; + u32 ctrls[2]; + u32 ctrl; + int port; + int res; + + /* Enable DSA */ + priv->cpu_ports_mask = dsa_cpu_ports(ds); + + ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | + YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); + res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); + if (res) + return res; + + /* Setup software switch */ + ctrl = YT921X_CPU_COPY_TO_EXT_CPU; + res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl); + if (res) + return res; + + ctrl = GENMASK(10, 0); + res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl); + if (res) + return res; + + /* YT921x does not support native DSA port bridging, so we use port + * isolation to emulate it. However, be especially careful that port + * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from + * another bridge) is matched and the destination port (in another + * bridge) is blocked, the packet will be dropped instead of flooding to + * the "bridged" ports, thus we need to trap and handle those packets by + * software. + * + * If there is no more than one bridge, we might be able to drop them + * directly given some conditions are met, but we trap them in all cases + * for now. + */ + ctrl = 0; + for (int i = 0; i < YT921X_PORT_NUM; i++) + ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i); + /* Except for CPU ports, if any packets are sent via CPU ports without + * tag, they should be dropped. + */ + cpu_ports_mask = priv->cpu_ports_mask; + for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) { + ctrl &= ~YT921X_ACT_UNK_ACTn_M(port); + ctrl |= YT921X_ACT_UNK_ACTn_DROP(port); + } + res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl); + if (res) + return res; + + /* Tagged VID 0 should be treated as untagged, which confuses the + * hardware a lot + */ + ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M; + ctrls[1] = 0; + res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls); + if (res) + return res; + + return 0; +} + +static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv) +{ + u32 ctrl; + int res; + + /* DSCP to internal priorities */ + for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) { + int prio = ietf_dscp_to_ieee8021q_tt(dscp); + + if (prio < 0) + return prio; + + res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), + YT921X_IPM_PRIO(prio)); + if (res) + return res; + } + + /* 802.1Q QoS to internal priorities */ + for (u8 pcp = 0; pcp < 8; pcp++) + for (u8 dei = 0; dei < 2; dei++) { + ctrl = YT921X_IPM_PRIO(pcp); + if (dei) + /* "Red" almost means drop, so it's not that + * useful. Note that tc police does not support + * Three-Color very well + */ + ctrl |= YT921X_IPM_COLOR_YELLOW; + + for (u8 svlan = 0; svlan < 2; svlan++) { + u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp); + + res = yt921x_reg_write(priv, reg, ctrl); + if (res) + return res; + } + } + + return 0; +} + +static int yt921x_chip_setup(struct yt921x_priv *priv) +{ + u32 ctrl; + int res; + + ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER; + res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl); + if (res) + return res; + + res = yt921x_chip_setup_dsa(priv); + if (res) + return res; + + res = yt921x_chip_setup_tc(priv); + if (res) + return res; + + res = yt921x_chip_setup_acl(priv); + if (res) + return res; + +#if IS_ENABLED(CONFIG_DCB) + res = yt921x_chip_setup_qos(priv); + if (res) + return res; +#endif + + /* Miscellaneous */ + res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP); + if (res) + return res; + + res = yt921x_reg_clear_bits(priv, YT921X_SERDES_CTRL, + YT921X_SERDES_CTRL_TEST); + if (res) + return res; + + return 0; +} + +static int yt921x_dsa_setup(struct dsa_switch *ds) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + struct device_node *np = dev->of_node; + struct device_node *child; + unsigned long mask; + int port; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_chip_reset(priv); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + + /* Register the internal mdio bus. Nodes for internal ports should have + * proper phy-handle pointing to their PHYs. Not enabling the internal + * bus is possible, though pretty wired, if internal ports are not used. + */ + child = of_get_child_by_name(np, "mdio"); + if (child) { + res = yt921x_mbus_int_init(priv, child); + of_node_put(child); + if (res) + return res; + } + + /* External mdio bus is optional */ + child = of_get_child_by_name(np, "mdio-external"); + if (child) { + res = yt921x_mbus_ext_init(priv, child); + of_node_put(child); + if (res) + return res; + + dev_err(dev, "Untested external mdio bus\n"); + return -ENODEV; + } + + mask = priv->info->serdes_mask; + for_each_set_bit(port, &mask, YT921X_PORT_NUM) { + struct yt921x_port *pp = &priv->ports[port]; + + pp->pcs.ops = &yt921x_phylink_pcs_ops; + pp->pcs.poll = true; + + __set_bit(PHY_INTERFACE_MODE_SGMII, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_100BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_1000BASEX, + pp->pcs.supported_interfaces); + __set_bit(PHY_INTERFACE_MODE_2500BASEX, + pp->pcs.supported_interfaces); + } + + mutex_lock(&priv->reg_lock); + res = yt921x_chip_setup(priv); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + +#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) + res = yt921x_leds_setup(priv); + if (res) + dev_warn(dev, "Failed to setup LEDs: %d\n", res); +#endif + + return 0; +} + +static const struct phylink_mac_ops yt921x_phylink_mac_ops = { + .mac_select_pcs = yt921x_phylink_mac_select_pcs, + .mac_link_down = yt921x_phylink_mac_link_down, + .mac_link_up = yt921x_phylink_mac_link_up, + .mac_config = yt921x_phylink_mac_config, +}; + +static const struct dsa_switch_ops yt921x_dsa_switch_ops = { + /* mib */ + .get_strings = yt921x_dsa_get_strings, + .get_ethtool_stats = yt921x_dsa_get_ethtool_stats, + .get_sset_count = yt921x_dsa_get_sset_count, + .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats, + .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats, + .get_rmon_stats = yt921x_dsa_get_rmon_stats, + .get_stats64 = yt921x_dsa_get_stats64, + .get_pause_stats = yt921x_dsa_get_pause_stats, + /* eee */ + .support_eee = dsa_supports_eee, + .set_mac_eee = yt921x_dsa_set_mac_eee, + /* mtu */ + .port_change_mtu = yt921x_dsa_port_change_mtu, + .port_max_mtu = yt921x_dsa_port_max_mtu, + /* rate */ + .port_policer_del = yt921x_dsa_port_policer_del, + .port_policer_add = yt921x_dsa_port_policer_add, + .port_setup_tc = yt921x_dsa_port_setup_tc, + /* acl */ + .cls_flower_del = yt921x_dsa_cls_flower_del, + .cls_flower_add = yt921x_dsa_cls_flower_add, + /* hsr */ + .port_hsr_leave = dsa_port_simple_hsr_leave, + .port_hsr_join = dsa_port_simple_hsr_join, + /* mirror */ + .port_mirror_del = yt921x_dsa_port_mirror_del, + .port_mirror_add = yt921x_dsa_port_mirror_add, + /* lag */ + .port_lag_leave = yt921x_dsa_port_lag_leave, + .port_lag_join = yt921x_dsa_port_lag_join, + /* fdb */ + .port_fdb_dump = yt921x_dsa_port_fdb_dump, + .port_fast_age = yt921x_dsa_port_fast_age, + .set_ageing_time = yt921x_dsa_set_ageing_time, + .port_fdb_del = yt921x_dsa_port_fdb_del, + .port_fdb_add = yt921x_dsa_port_fdb_add, + .port_mdb_del = yt921x_dsa_port_mdb_del, + .port_mdb_add = yt921x_dsa_port_mdb_add, + /* vlan */ + .port_vlan_filtering = yt921x_dsa_port_vlan_filtering, + .port_vlan_del = yt921x_dsa_port_vlan_del, + .port_vlan_add = yt921x_dsa_port_vlan_add, + /* bridge */ + .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags, + .port_bridge_flags = yt921x_dsa_port_bridge_flags, + .port_bridge_leave = yt921x_dsa_port_bridge_leave, + .port_bridge_join = yt921x_dsa_port_bridge_join, + /* mst */ + .port_mst_state_set = yt921x_dsa_port_mst_state_set, + .vlan_msti_set = yt921x_dsa_vlan_msti_set, + .port_stp_state_set = yt921x_dsa_port_stp_state_set, +#if IS_ENABLED(CONFIG_DCB) + /* dcb */ + .port_get_default_prio = yt921x_dsa_port_get_default_prio, + .port_set_default_prio = yt921x_dsa_port_set_default_prio, + .port_get_apptrust = yt921x_dsa_port_get_apptrust, + .port_set_apptrust = yt921x_dsa_port_set_apptrust, +#endif + /* port */ + .get_tag_protocol = yt921x_dsa_get_tag_protocol, + .phylink_get_caps = yt921x_dsa_phylink_get_caps, + .port_teardown = yt921x_dsa_port_teardown, + .port_setup = yt921x_dsa_port_setup, +#if IS_ENABLED(CONFIG_DCB) + /* dscp */ + .port_get_dscp_prio = yt921x_dsa_port_get_dscp_prio, + .port_del_dscp_prio = yt921x_dsa_port_del_dscp_prio, + .port_add_dscp_prio = yt921x_dsa_port_add_dscp_prio, +#endif + /* chip */ + .teardown = yt921x_dsa_teardown, + .setup = yt921x_dsa_setup, +}; + +static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) +{ + struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); + + if (!priv) + return; + + dsa_switch_shutdown(&priv->ds); +} + +static void yt921x_mdio_remove(struct mdio_device *mdiodev) +{ + struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); + + if (!priv) + return; + + dsa_unregister_switch(&priv->ds); + + for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) { + struct yt921x_acl_blk *aclblk = priv->acl_blks[i]; + + if (!aclblk) + continue; + for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) { + struct yt921x_acl_rule *aclrule = aclblk->rules[j]; + + if (!aclrule) + continue; + kvfree(aclrule); + } + kvfree(aclblk); + } + mutex_destroy(&priv->reg_lock); +} + +static int yt921x_mdio_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct yt921x_reg_mdio *mdio; + struct yt921x_priv *priv; + struct dsa_switch *ds; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL); + if (!mdio) + return -ENOMEM; + + mdio->bus = mdiodev->bus; + mdio->addr = mdiodev->addr; + mdio->switchid = 0; + + mutex_init(&priv->reg_lock); + + priv->reg_ops = &yt921x_reg_ops_mdio; + priv->reg_ctx = mdio; + + for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) { + struct yt921x_port *pp = &priv->ports[i]; + + pp->index = i; + } + + ds = &priv->ds; + ds->dev = dev; + ds->assisted_learning_on_cpu_port = true; + ds->dscp_prio_mapping_is_global = true; + ds->priv = priv; + ds->ops = &yt921x_dsa_switch_ops; + ds->ageing_time_min = 1 * 5000; + ds->ageing_time_max = U16_MAX * 5000; + ds->phylink_mac_ops = &yt921x_phylink_mac_ops; + ds->num_lag_ids = YT921X_LAG_NUM; + ds->num_ports = YT921X_PORT_NUM; + + mdiodev_set_drvdata(mdiodev, priv); + + return dsa_register_switch(ds); +} + +static const struct of_device_id yt921x_of_match[] = { + { .compatible = "motorcomm,yt9215" }, + {} +}; +MODULE_DEVICE_TABLE(of, yt921x_of_match); + +static struct mdio_driver yt921x_mdio_driver = { + .probe = yt921x_mdio_probe, + .remove = yt921x_mdio_remove, + .shutdown = yt921x_mdio_shutdown, + .mdiodrv.driver = { + .name = YT921X_NAME, + .of_match_table = yt921x_of_match, + }, +}; + +mdio_module_driver(yt921x_mdio_driver); + +MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>"); +MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/motorcomm/chip.h b/drivers/net/dsa/motorcomm/chip.h new file mode 100644 index 000000000000..fc315f1d8f87 --- /dev/null +++ b/drivers/net/dsa/motorcomm/chip.h @@ -0,0 +1,568 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2025 David Yang + */ + +#ifndef __YT921X_H +#define __YT921X_H + +#include <net/dsa.h> + +#define YT921X_SMI_SWITCHID_M GENMASK(3, 2) +#define YT921X_SMI_SWITCHID(x) FIELD_PREP(YT921X_SMI_SWITCHID_M, (x)) +#define YT921X_SMI_AD BIT(1) +#define YT921X_SMI_ADDR 0 +#define YT921X_SMI_DATA YT921X_SMI_AD +#define YT921X_SMI_RW BIT(0) +#define YT921X_SMI_WRITE 0 +#define YT921X_SMI_READ YT921X_SMI_RW + +#define YT921X_SWITCHID_NUM 4 + +#define YT921X_RST 0x80000 +#define YT921X_RST_HW BIT(31) +#define YT921X_RST_SW BIT(1) +#define YT921X_FUNC 0x80004 +#define YT921X_FUNC_METER BIT(4) +#define YT921X_FUNC_ACL BIT(2) +#define YT921X_FUNC_MIB BIT(1) +#define YT921X_CHIP_ID 0x80008 +#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16) +#define YT921X_EXT_CPU_PORT 0x8000c +#define YT921X_EXT_CPU_PORT_TAG_EN BIT(15) +#define YT921X_EXT_CPU_PORT_PORT_EN BIT(14) +#define YT921X_EXT_CPU_PORT_PORT_M GENMASK(3, 0) +#define YT921X_EXT_CPU_PORT_PORT(x) FIELD_PREP(YT921X_EXT_CPU_PORT_PORT_M, (x)) +#define YT921X_CPU_TAG_TPID 0x80010 +#define YT921X_CPU_TAG_TPID_TPID_M GENMASK(15, 0) +/* Same as ETH_P_YT921X, but this represents the true HW default, while the + * former is a local convention chosen by us. + */ +#define YT921X_CPU_TAG_TPID_TPID_DEFAULT 0x9988 +#define YT921X_PVID_SEL 0x80014 +#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port) +#define YT921X_SERDES_CTRL 0x80028 +#define YT921X_SERDES_CTRL_TEST BIT(6) +#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8) +#define YT921X_IO_LEVEL 0x80030 +#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4) +#define YT9215_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M, (x)) +#define YT9215_IO_LEVEL_NORMAL_3V3 YT9215_IO_LEVEL_NORMAL(0) +#define YT9215_IO_LEVEL_NORMAL_1V8 YT9215_IO_LEVEL_NORMAL(3) +#define YT9215_IO_LEVEL_RGMII1_M GENMASK(3, 2) +#define YT9215_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M, (x)) +#define YT9215_IO_LEVEL_RGMII1_3V3 YT9215_IO_LEVEL_RGMII1(0) +#define YT9215_IO_LEVEL_RGMII1_2V5 YT9215_IO_LEVEL_RGMII1(1) +#define YT9215_IO_LEVEL_RGMII1_1V8 YT9215_IO_LEVEL_RGMII1(2) +#define YT9215_IO_LEVEL_RGMII0_M GENMASK(1, 0) +#define YT9215_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M, (x)) +#define YT9215_IO_LEVEL_RGMII0_3V3 YT9215_IO_LEVEL_RGMII0(0) +#define YT9215_IO_LEVEL_RGMII0_2V5 YT9215_IO_LEVEL_RGMII0(1) +#define YT9215_IO_LEVEL_RGMII0_1V8 YT9215_IO_LEVEL_RGMII0(2) +#define YT9218_IO_LEVEL_RGMII1_M GENMASK(5, 4) +#define YT9218_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M, (x)) +#define YT9218_IO_LEVEL_RGMII1_3V3 YT9218_IO_LEVEL_RGMII1(0) +#define YT9218_IO_LEVEL_RGMII1_2V5 YT9218_IO_LEVEL_RGMII1(1) +#define YT9218_IO_LEVEL_RGMII1_1V8 YT9218_IO_LEVEL_RGMII1(2) +#define YT9218_IO_LEVEL_RGMII0_M GENMASK(3, 2) +#define YT9218_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M, (x)) +#define YT9218_IO_LEVEL_RGMII0_3V3 YT9218_IO_LEVEL_RGMII0(0) +#define YT9218_IO_LEVEL_RGMII0_2V5 YT9218_IO_LEVEL_RGMII0(1) +#define YT9218_IO_LEVEL_RGMII0_1V8 YT9218_IO_LEVEL_RGMII0(2) +#define YT9218_IO_LEVEL_NORMAL_M GENMASK(1, 0) +#define YT9218_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M, (x)) +#define YT9218_IO_LEVEL_NORMAL_3V3 YT9218_IO_LEVEL_NORMAL(0) +#define YT9218_IO_LEVEL_NORMAL_1V8 YT9218_IO_LEVEL_NORMAL(3) +#define YT921X_MAC_ADDR_HI2 0x80080 +#define YT921X_MAC_ADDR_LO4 0x80084 +#define YT921X_SERDESn(port) (0x8008c + 4 * ((port) - 8)) +#define YT921X_SERDES_MODE_M GENMASK(9, 7) +#define YT921X_SERDES_MODE(x) FIELD_PREP(YT921X_SERDES_MODE_M, (x)) +#define YT921X_SERDES_MODE_SGMII YT921X_SERDES_MODE(0) +#define YT921X_SERDES_MODE_REVSGMII YT921X_SERDES_MODE(1) +#define YT921X_SERDES_MODE_1000BASEX YT921X_SERDES_MODE(2) +#define YT921X_SERDES_MODE_100BASEX YT921X_SERDES_MODE(3) +#define YT921X_SERDES_MODE_2500BASEX YT921X_SERDES_MODE(4) +#define YT921X_SERDES_RX_PAUSE BIT(6) +#define YT921X_SERDES_TX_PAUSE BIT(5) +#define YT921X_SERDES_LINK BIT(4) /* force link */ +#define YT921X_SERDES_DUPLEX_FULL BIT(3) +#define YT921X_SERDES_SPEED_M GENMASK(2, 0) +#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x)) +#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) /* Bit 7-0 shared with PORT_STATUS */ +#define YT921X_PORT_CTRL_PAUSE_AN BIT(10) +#define YT921X_PORT_CTRL_LINK_AN BIT(9) +#define YT921X_PORT_CTRL_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */ +#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port)) +#define YT921X_PORT_LINK BIT(8) +#define YT921X_PORT_DUPLEX_FULL BIT(7) +#define YT921X_PORT_RX_PAUSE BIT(6) +#define YT921X_PORT_TX_PAUSE BIT(5) +#define YT921X_PORT_RX_MAC_EN BIT(4) +#define YT921X_PORT_TX_MAC_EN BIT(3) +#define YT921X_PORT_SPEED_M GENMASK(2, 0) +#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x)) +#define YT921X_PON_STRAP_FUNC 0x80320 +#define YT921X_PON_STRAP_VAL 0x80324 +#define YT921X_PON_STRAP_CAP 0x80328 +#define YT921X_PON_STRAP_EEE BIT(16) +#define YT921X_PON_STRAP_LOOP_DETECT BIT(7) +#define YT921X_MDIO_POLLINGn(port) (0x80364 + 4 * ((port) - 8)) +#define YT921X_MDIO_POLLING_DUPLEX_FULL BIT(4) +#define YT921X_MDIO_POLLING_LINK BIT(3) +#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0) +#define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x)) + +enum yt921x_speed { + YT921X_SPEED_10, + YT921X_SPEED_100, + YT921X_SPEED_1000, + YT921X_SPEED_10000, + YT921X_SPEED_2500, + YT921X_SPEED_INVALID +}; + +#define YT921X_SENSOR 0x8036c +#define YT921X_SENSOR_TEMP BIT(18) +#define YT921X_TEMP 0x80374 +#define YT921X_CHIP_MODE 0x80388 +#define YT921X_CHIP_MODE_MODE GENMASK(1, 0) +#define YT921X_XMII_CTRL 0x80394 +#define YT921X_XMII_CTRL_PORTn(port) BIT(9 - (port)) /* Yes, it's reversed */ +#define YT921X_XMIIn(port) (0x80400 + 8 * ((port) - 8)) +#define YT921X_XMII_MODE_M GENMASK(31, 29) +#define YT921X_XMII_MODE(x) FIELD_PREP(YT921X_XMII_MODE_M, (x)) +#define YT921X_XMII_MODE_MII YT921X_XMII_MODE(0) +#define YT921X_XMII_MODE_REVMII YT921X_XMII_MODE(1) +#define YT921X_XMII_MODE_RMII YT921X_XMII_MODE(2) +#define YT921X_XMII_MODE_REVRMII YT921X_XMII_MODE(3) +#define YT921X_XMII_MODE_RGMII YT921X_XMII_MODE(4) +#define YT921X_XMII_MODE_DISABLE YT921X_XMII_MODE(5) +#define YT921X_XMII_LINK BIT(19) /* force link */ +#define YT921X_XMII_EN BIT(18) +#define YT921X_XMII_SOFT_RST BIT(17) +#define YT921X_XMII_RGMII_TX_DELAY_150PS_M GENMASK(16, 13) +#define YT921X_XMII_RGMII_TX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_TX_DELAY_150PS_M, (x)) +#define YT921X_XMII_TX_CLK_IN BIT(11) +#define YT921X_XMII_RX_CLK_IN BIT(10) +#define YT921X_XMII_RGMII_TX_DELAY_2NS BIT(8) +#define YT921X_XMII_RGMII_TX_CLK_OUT BIT(7) +#define YT921X_XMII_RGMII_RX_DELAY_150PS_M GENMASK(6, 3) +#define YT921X_XMII_RGMII_RX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_RX_DELAY_150PS_M, (x)) +#define YT921X_XMII_RMII_PHY_TX_CLK_OUT BIT(2) +#define YT921X_XMII_REVMII_TX_CLK_OUT BIT(1) +#define YT921X_XMII_REVMII_RX_CLK_OUT BIT(0) + +#define YT921X_MACn_FRAME(port) (0x81008 + 0x1000 * (port)) +#define YT921X_MAC_FRAME_SIZE_M GENMASK(21, 8) +#define YT921X_MAC_FRAME_SIZE(x) FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (x)) + +#define YT921X_EEEn_VAL(port) (0xa0000 + 0x40 * (port)) +#define YT921X_EEE_VAL_DATA BIT(1) + +#define YT921X_EEE_CTRL 0xb0000 +#define YT921X_EEE_CTRL_ENn(port) BIT(port) + +#define YT921X_EDATA_CTRL 0xe0000 +#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8) +#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x)) +#define YT921X_EDATA_CTRL_OP_M GENMASK(3, 0) +#define YT921X_EDATA_CTRL_OP(x) FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x)) +#define YT921X_EDATA_CTRL_READ YT921X_EDATA_CTRL_OP(5) +#define YT921X_EDATA_DATA 0xe0004 +#define YT921X_EDATA_DATA_DATA_M GENMASK(31, 24) +#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0) +#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x)) +#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3) +#define YT921X_SYS_CLK 0xe0040 +#define YT921X_SYS_CLK_SEL_M GENMASK(1, 0) /* unknown: 167M */ +#define YT9215_SYS_CLK_125M 0 +#define YT9218_SYS_CLK_167M 0 +#define YT921X_SYS_CLK_143M 1 + +#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port)) +#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4) +#define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x)) +#define YT921X_PORT_EGR_TPID_STAG_M GENMASK(3, 2) +#define YT921X_PORT_EGR_TPID_STAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_STAG_M, (x)) +#define YT921X_TPID_EGRn(x) (0x100300 + 4 * (x)) /* [0, 3] */ +#define YT921X_TPID_EGR_TPID_M GENMASK(15, 0) + +#define YT921X_IPM_DSCPn(n) (0x180000 + 4 * (n)) /* Internal Priority Map */ +#define YT921X_IPM_PCPn(map, dei, pcp) (0x180100 + 4 * (16 * (map) + 8 * (dei) + (pcp))) +#define YT921X_IPM_PRIO_M GENMASK(4, 2) +#define YT921X_IPM_PRIO(x) FIELD_PREP(YT921X_IPM_PRIO_M, (x)) +#define YT921X_IPM_COLOR_M GENMASK(1, 0) +#define YT921X_IPM_COLOR(x) FIELD_PREP(YT921X_IPM_COLOR_M, (x)) +#define YT921X_IPM_COLOR_GREEN YT921X_IPM_COLOR(0) +#define YT921X_IPM_COLOR_YELLOW YT921X_IPM_COLOR(1) +#define YT921X_IPM_COLOR_RED YT921X_IPM_COLOR(2) +#define YT921X_PORTn_QOS(port) (0x180180 + 4 * (port)) +#define YT921X_PORT_QOS_CVLAN_PRIO_MAP_ID BIT(5) +#define YT921X_PORT_QOS_SVLAN_PRIO_MAP_ID BIT(4) +#define YT921X_PORT_QOS_PRIO_M GENMASK(3, 1) +#define YT921X_PORT_QOS_PRIO(x) FIELD_PREP(YT921X_PORT_QOS_PRIO_M, (x)) +#define YT921X_PORT_QOS_PRIO_EN BIT(0) +#define YT921X_PORTn_PRIO_ORD(port) (0x180200 + 4 * (port)) +#define YT921X_PORT_PRIO_ORD_APPm_M(m) GENMASK(3 * (m) + 2, 3 * (m)) +#define YT921X_PORT_PRIO_ORD_APPm(m, x) ((x) << (3 * (m))) /* 0: disabled, except PORT_QOS_PRIO */ + +enum yt921x_app_selector { + YT921X_APP_SEL_MAC_SA, + YT921X_APP_SEL_MAC_DA, + YT921X_APP_SEL_VID, + YT921X_APP_SEL_ACL, + YT921X_APP_SEL_DSCP, + YT921X_APP_SEL_CVLAN_PCP, + YT921X_APP_SEL_SVLAN_PCP, + /* The physical port, i.e. YT921X_PORT_QOS_PRIO */ + YT921X_APP_SEL_PORT, + YT921X_APP_SEL_NUM +}; + +#define YT921X_VLAN_IGR_FILTER 0x180280 +#define YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port) BIT((port) + 11) +#define YT921X_VLAN_IGR_FILTER_PORTn(port) BIT(port) +#define YT921X_PORTn_ISOLATION(port) (0x180294 + 4 * (port)) +#define YT921X_PORT_ISOLATION_BLOCKn(port) BIT(port) +#define YT921X_STPn(n) (0x18038c + 4 * (n)) +#define YT921X_STP_PORTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) +#define YT921X_STP_PORTn(port, x) ((x) << (2 * (port))) +#define YT921X_STP_PORTn_DISABLED(port) YT921X_STP_PORTn(port, 0) +#define YT921X_STP_PORTn_LEARNING(port) YT921X_STP_PORTn(port, 1) +#define YT921X_STP_PORTn_BLOCKING(port) YT921X_STP_PORTn(port, 2) +#define YT921X_STP_PORTn_FORWARD(port) YT921X_STP_PORTn(port, 3) +#define YT921X_PORTn_LEARN(port) (0x1803d0 + 4 * (port)) +#define YT921X_PORT_LEARN_VID_LEARN_MULTI_EN BIT(22) +#define YT921X_PORT_LEARN_VID_LEARN_MODE BIT(21) +#define YT921X_PORT_LEARN_VID_LEARN_EN BIT(20) +#define YT921X_PORT_LEARN_SUSPEND_COPY_EN BIT(19) +#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18) +#define YT921X_PORT_LEARN_DIS BIT(17) +#define YT921X_PORT_LEARN_LIMIT_EN BIT(16) +#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8) +#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x)) +#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2) +#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0) +#define YT921X_PORT_LEARN_MODE(x) FIELD_PREP(YT921X_PORT_LEARN_MODE_M, (x)) +#define YT921X_PORT_LEARN_MODE_AUTO YT921X_PORT_LEARN_MODE(0) +#define YT921X_PORT_LEARN_MODE_AUTO_AND_COPY YT921X_PORT_LEARN_MODE(1) +#define YT921X_PORT_LEARN_MODE_CPU_CONTROL YT921X_PORT_LEARN_MODE(2) +#define YT921X_AGEING 0x180440 +#define YT921X_AGEING_INTERVAL_M GENMASK(15, 0) +#define YT921X_FDB_IN0 0x180454 +#define YT921X_FDB_IN1 0x180458 +#define YT921X_FDB_IN2 0x18045c +#define YT921X_FDB_OP 0x180460 +#define YT921X_FDB_OP_INDEX_M GENMASK(22, 11) +#define YT921X_FDB_OP_INDEX(x) FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x)) +#define YT921X_FDB_OP_MODE_INDEX BIT(10) /* mac+fid / index */ +#define YT921X_FDB_OP_FLUSH_MCAST BIT(9) /* ucast / mcast */ +#define YT921X_FDB_OP_FLUSH_M GENMASK(8, 7) +#define YT921X_FDB_OP_FLUSH(x) FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x)) +#define YT921X_FDB_OP_FLUSH_ALL YT921X_FDB_OP_FLUSH(0) +#define YT921X_FDB_OP_FLUSH_PORT YT921X_FDB_OP_FLUSH(1) +#define YT921X_FDB_OP_FLUSH_PORT_VID YT921X_FDB_OP_FLUSH(2) +#define YT921X_FDB_OP_FLUSH_VID YT921X_FDB_OP_FLUSH(3) +#define YT921X_FDB_OP_FLUSH_STATIC BIT(6) +#define YT921X_FDB_OP_NEXT_TYPE_M GENMASK(5, 4) +#define YT921X_FDB_OP_NEXT_TYPE(x) FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_M, (x)) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT YT921X_FDB_OP_NEXT_TYPE(0) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST_VID YT921X_FDB_OP_NEXT_TYPE(1) +#define YT921X_FDB_OP_NEXT_TYPE_UCAST YT921X_FDB_OP_NEXT_TYPE(2) +#define YT921X_FDB_OP_NEXT_TYPE_MCAST YT921X_FDB_OP_NEXT_TYPE(3) +#define YT921X_FDB_OP_OP_M GENMASK(3, 1) +#define YT921X_FDB_OP_OP(x) FIELD_PREP(YT921X_FDB_OP_OP_M, (x)) +#define YT921X_FDB_OP_OP_ADD YT921X_FDB_OP_OP(0) +#define YT921X_FDB_OP_OP_DEL YT921X_FDB_OP_OP(1) +#define YT921X_FDB_OP_OP_GET_ONE YT921X_FDB_OP_OP(2) +#define YT921X_FDB_OP_OP_GET_NEXT YT921X_FDB_OP_OP(3) +#define YT921X_FDB_OP_OP_FLUSH YT921X_FDB_OP_OP(4) +#define YT921X_FDB_OP_START BIT(0) +#define YT921X_FDB_RESULT 0x180464 +#define YT921X_FDB_RESULT_DONE BIT(15) +#define YT921X_FDB_RESULT_NOTFOUND BIT(14) +#define YT921X_FDB_RESULT_OVERWRITED BIT(13) +#define YT921X_FDB_RESULT_INDEX_M GENMASK(11, 0) +#define YT921X_FDB_RESULT_INDEX(x) FIELD_PREP(YT921X_FDB_RESULT_INDEX_M, (x)) +#define YT921X_FDB_OUT0 0x1804b0 +#define YT921X_FDB_IO0_ADDR_HI4_M GENMASK(31, 0) +#define YT921X_FDB_OUT1 0x1804b4 +#define YT921X_FDB_IO1_EGR_PRIO_EN BIT(31) +#define YT921X_FDB_IO1_STATUS_M GENMASK(30, 28) +#define YT921X_FDB_IO1_STATUS(x) FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (x)) +#define YT921X_FDB_IO1_STATUS_INVALID YT921X_FDB_IO1_STATUS(0) +#define YT921X_FDB_IO1_STATUS_MIN_TIME YT921X_FDB_IO1_STATUS(1) +#define YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME YT921X_FDB_IO1_STATUS(3) +#define YT921X_FDB_IO1_STATUS_MAX_TIME YT921X_FDB_IO1_STATUS(5) +#define YT921X_FDB_IO1_STATUS_PENDING YT921X_FDB_IO1_STATUS(6) +#define YT921X_FDB_IO1_STATUS_STATIC YT921X_FDB_IO1_STATUS(7) +#define YT921X_FDB_IO1_FID_M GENMASK(27, 16) /* filtering ID (VID) */ +#define YT921X_FDB_IO1_FID(x) FIELD_PREP(YT921X_FDB_IO1_FID_M, (x)) +#define YT921X_FDB_IO1_ADDR_LO2_M GENMASK(15, 0) +#define YT921X_FDB_OUT2 0x1804b8 +#define YT921X_FDB_IO2_MOVE_AGING_STATUS_M GENMASK(31, 30) +#define YT921X_FDB_IO2_IGR_DROP BIT(29) +#define YT921X_FDB_IO2_EGR_PORTS_M GENMASK(28, 18) +#define YT921X_FDB_IO2_EGR_PORTS(x) FIELD_PREP(YT921X_FDB_IO2_EGR_PORTS_M, (x)) +#define YT921X_FDB_IO2_EGR_DROP BIT(17) +#define YT921X_FDB_IO2_COPY_TO_CPU BIT(16) +#define YT921X_FDB_IO2_IGR_PRIO_EN BIT(15) +#define YT921X_FDB_IO2_PRIO_M GENMASK(14, 12) +#define YT921X_FDB_IO2_PRIO(x) FIELD_PREP(YT921X_FDB_IO2_PRIO_M, (x)) +#define YT921X_FDB_IO2_NEW_VID_M GENMASK(11, 0) +#define YT921X_FDB_IO2_NEW_VID(x) FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M, (x)) +#define YT921X_FILTER_UNK_UCAST 0x180508 +#define YT921X_FILTER_UNK_MCAST 0x18050c +#define YT921X_FILTER_MCAST 0x180510 +#define YT921X_FILTER_BCAST 0x180514 +#define YT921X_FILTER_PORTS_M GENMASK(10, 0) +#define YT921X_FILTER_PORTS(x) FIELD_PREP(YT921X_FILTER_PORTS_M, (x)) +#define YT921X_FILTER_PORTn(port) BIT(port) +#define YT921X_VLAN_EGR_FILTER 0x180598 +#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port) +#define YT921X_LAG_GROUPn(n) (0x1805a8 + 4 * (n)) +#define YT921X_LAG_GROUP_PORTS_M GENMASK(13, 3) +#define YT921X_LAG_GROUP_PORTS(x) FIELD_PREP(YT921X_LAG_GROUP_PORTS_M, (x)) +#define YT921X_LAG_GROUP_MEMBER_NUM_M GENMASK(2, 0) +#define YT921X_LAG_GROUP_MEMBER_NUM(x) FIELD_PREP(YT921X_LAG_GROUP_MEMBER_NUM_M, (x)) +#define YT921X_LAG_MEMBERnm(n, m) (0x1805b0 + 4 * (4 * (n) + (m))) +#define YT921X_LAG_MEMBER_PORT_M GENMASK(3, 0) +#define YT921X_LAG_MEMBER_PORT(x) FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x)) +#define YT921X_CPU_COPY 0x180690 +#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2) +#define YT921X_CPU_COPY_TO_INT_CPU BIT(1) +#define YT921X_CPU_COPY_TO_EXT_CPU BIT(0) +#define YT921X_ACL_PERMIT_UNMATCH 0x1806a0 +#define YT921X_ACL_PERMIT_UNMATCH_PORTS_M GENMASK(10, 0) +#define YT921X_ACL_PERMIT_UNMATCH_PORTS(x) FIELD_PREP(YT921X_ACL_PERMIT_UNMATCH_PORTS_M, (x)) +#define YT921X_ACL_PERMIT_UNMATCH_PORTn(port) BIT(port) +#define YT921X_ACT_UNK_UCAST 0x180734 +#define YT921X_ACT_UNK_MCAST 0x180738 +#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23) +#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(22) +#define YT921X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) +#define YT921X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port))) +#define YT921X_ACT_UNK_ACTn_FORWARD(port) YT921X_ACT_UNK_ACTn(port, 0) /* flood */ +#define YT921X_ACT_UNK_ACTn_TRAP(port) YT921X_ACT_UNK_ACTn(port, 1) /* steer to CPU */ +#define YT921X_ACT_UNK_ACTn_DROP(port) YT921X_ACT_UNK_ACTn(port, 2) /* discard */ +/* NEVER use this action; see comments in the tag driver */ +#define YT921X_ACT_UNK_ACTn_COPY(port) YT921X_ACT_UNK_ACTn(port, 3) /* flood and copy */ +#define YT921X_FDB_HW_FLUSH 0x180958 +#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0) + +#define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan)) +#define YT921X_VLAN_CTRLb_UNTAG_PORTS_M GENMASK(18, 8) +#define YT921X_VLAN_CTRLb_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x)) +#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8) +#define YT921X_VLAN_CTRLb_STP_ID_M GENMASK(7, 4) +#define YT921X_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x)) +#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3) +#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23) +#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x)) +#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22) +#define YT921X_VLAN_CTRLa_PRIO_EN BIT(21) +#define YT921X_VLAN_CTRLa_PRIO_M GENMASK(20, 18) +#define YT921X_VLAN_CTRLa_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x)) +#define YT921X_VLAN_CTRLa_PORTS_M GENMASK(17, 7) +#define YT921X_VLAN_CTRLa_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x)) +#define YT921X_VLAN_CTRLa_PORTn(port) BIT((port) + 7) +#define YT921X_VLAN_CTRLa_BYPASS_1X_AC BIT(6) +#define YT921X_VLAN_CTRLa_METER_EN BIT(5) +#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0) + +#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */ +#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0) +#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port)) +#define YT921X_PORT_IGR_TPIDn_STAG_M GENMASK(7, 4) +#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4) +#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0) +#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x) +#define YT921X_LAG_HASH 0x210090 +#define YT921X_LAG_HASH_L4_SPORT BIT(7) +#define YT921X_LAG_HASH_L4_DPORT BIT(6) +#define YT921X_LAG_HASH_IP_PROTO BIT(5) +#define YT921X_LAG_HASH_IP_SRC BIT(4) +#define YT921X_LAG_HASH_IP_DST BIT(3) +#define YT921X_LAG_HASH_MAC_SA BIT(2) +#define YT921X_LAG_HASH_MAC_DA BIT(1) +#define YT921X_LAG_HASH_SRC_PORT BIT(0) + +#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port)) +#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31) +#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_EN BIT(30) +#define YT921X_PORT_VLAN_CTRL_SVID_M GENMASK(29, 18) +#define YT921X_PORT_VLAN_CTRL_SVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_SVID_M, (x)) +#define YT921X_PORT_VLAN_CTRL_CVID_M GENMASK(17, 6) +#define YT921X_PORT_VLAN_CTRL_CVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_CVID_M, (x)) +#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_M GENMASK(5, 3) +#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_M GENMASK(2, 0) +#define YT921X_PORTn_VLAN_CTRL1(port) (0x230080 + 4 * (port)) +#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN BIT(8) +#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M GENMASK(7, 4) +#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED BIT(3) +#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED BIT(2) +#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1) +#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0) + +#define YT921X_EDATA_EXTMODE 0xfb +#define YT921X_EDATA_LEN 0x100 + +#define YT921X_FDB_NUM 4096 + +enum yt921x_fdb_entry_status { + YT921X_FDB_ENTRY_STATUS_INVALID = 0, + YT921X_FDB_ENTRY_STATUS_MIN_TIME = 1, + YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME = 3, + YT921X_FDB_ENTRY_STATUS_MAX_TIME = 5, + YT921X_FDB_ENTRY_STATUS_PENDING = 6, + YT921X_FDB_ENTRY_STATUS_STATIC = 7, +}; + +#define YT921X_MSTI_NUM 16 + +/* Custom meters only, not including dedicated port meters (11) */ +#define YT921X_METER_NUM 64 + +#define YT921X_LAG_NUM 2 +#define YT921X_LAG_PORT_NUM 4 + +#define YT921X_PRIO_NUM 8 + +#define YT9215_MAJOR 0x9002 +#define YT9218_MAJOR 0x9001 + +/* required for a hard reset */ +#define YT921X_RST_DELAY_US 10000 + +#define YT921X_FRAME_SIZE_MAX 0x2400 /* 9216 */ + +#define YT921X_TAG_LEN 8 + +#define YT921X_ACL_BLK_NUM 48 +#define YT921X_UDF_NUM 16 + +#define YT921X_LED_GROUP_NUM 3 +#define YT921X_LED_PORT_NUM 10 + +/* 8 internal + 2 external + 1 mcu */ +#define YT921X_PORT_NUM 11 + +#define YT921X_NAME "yt921x" + +struct yt921x_port { + unsigned char index; + + /* SerDes in use */ + bool serdes:1; + /* Link from in-band status (PHYLINK_PCS_NEG_INBAND) */ + bool inband:1; + /* BR_HAIRPIN_MODE */ + bool hairpin:1; + /* BR_ISOLATED */ + bool isolated:1; + + unsigned short acl_cnt; + +#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) + unsigned char led_duty; + unsigned short led_cycle; + + unsigned char led_duty_mask; + unsigned char led_cycle_mask; + + struct yt921x_led *leds[YT921X_LED_GROUP_NUM]; +#endif + + struct yt921x_mib *mib; + struct phylink_pcs pcs; +}; + +#define pcs_to_yt921x_port(_pcs) container_of((_pcs), struct yt921x_port, pcs) + +struct yt921x_reg_ops { + int (*read)(void *context, u32 reg, u32 *valp); + int (*write)(void *context, u32 reg, u32 val); +}; + +struct yt921x_info { + const char *name; + u16 major; + /* Unknown, seems to be plain enumeration */ + u8 mode; + u8 extmode; + /* Ports with integral GbE PHYs, not including MCU Port 10 */ + u16 internal_mask; + /* Note: xmii_mask and serdes_mask may overlap */ + u16 xmii_mask; + u16 serdes_mask; +}; + +static inline u16 yt921x_info_ports_mask(const struct yt921x_info *info) +{ + return info->internal_mask | info->xmii_mask | info->serdes_mask; +} + +struct yt921x_priv { + struct dsa_switch ds; + + const struct yt921x_info *info; + unsigned int meter_slot_ns; + unsigned int port_shape_slot_ns; + /* cache of dsa_cpu_ports(ds) */ + u16 cpu_ports_mask; + unsigned char cycle_ns; + + /* protect the access to the switch registers */ + struct mutex reg_lock; + const struct yt921x_reg_ops *reg_ops; + void *reg_ctx; + + /* mdio master bus */ + struct mii_bus *mbus_int; + struct mii_bus *mbus_ext; + + struct yt921x_port ports[YT921X_PORT_NUM]; + + u16 eee_ports_mask; + + DECLARE_BITMAP(meters_map, YT921X_METER_NUM); + + u8 acl_masks[YT921X_ACL_BLK_NUM]; + struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM]; +}; + +#define dsa_to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds) +#define yt921x_port_to_priv(pp) \ + container_of_const((pp), struct yt921x_priv, ports[(pp)->index]) +#define yt921x_priv_to_device(priv) ((priv)->ds.dev) + +static inline int ethtool_speed_to_yt921x(int speed) +{ + switch (speed) { + case SPEED_10: + return YT921X_SPEED_10; + case SPEED_100: + return YT921X_SPEED_100; + case SPEED_1000: + return YT921X_SPEED_1000; + case SPEED_2500: + return YT921X_SPEED_2500; + case SPEED_10000: + return YT921X_SPEED_10000; + default: + return YT921X_SPEED_INVALID; + } +} + +static inline u32 ethaddr_hi4_to_u32(const unsigned char *addr) +{ + return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; +} + +static inline u32 ethaddr_lo2_to_u32(const unsigned char *addr) +{ + return (addr[4] << 8) | addr[5]; +} + +#endif diff --git a/drivers/net/dsa/motorcomm/leds.c b/drivers/net/dsa/motorcomm/leds.c new file mode 100644 index 000000000000..08fbe86c3334 --- /dev/null +++ b/drivers/net/dsa/motorcomm/leds.c @@ -0,0 +1,655 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include <linux/uleds.h> + +#include "chip.h" +#include "leds.h" +#include "smi.h" + +#define to_yt921x_led(led_cdev) \ + container_of_const((led_cdev), struct yt921x_led, cdev) +#define to_yt921x_port(led) ((led)->port) + +static u32 yt921x_led_regaddr(struct yt921x_priv *priv, int port, int group) +{ + switch (group) { + case 0: + default: + return YT921X_LED0_PORTn(port); + case 1: + return YT921X_LED1_PORTn(port); + case 2: + return YT921X_LED2_PORTn(port); + } +} + +static int +yt921x_led_force_set(struct yt921x_priv *priv, int port, int group, bool on) +{ + struct yt921x_port *pp = &priv->ports[port]; + u32 ctrl; + u32 mask; + + /* No reversion - LED is sacrificed in case of IO error */ + pp->led_duty_mask &= ~BIT(group); + pp->led_cycle_mask &= ~BIT(group); + + mask = YT921X_LED2_PORT_FORCEn_M(group); + ctrl = on ? YT921X_LED2_PORT_FORCEn_ON(group) : + YT921X_LED2_PORT_FORCEn_OFF(group); + return yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), mask, + ctrl); +} + +static int +yt921x_led_blink_select(const struct yt921x_priv *priv, unsigned long on, + unsigned long off, unsigned short *cyclep, + unsigned char *dutyp) +{ + static const unsigned char dutys[] = { + YT921X_LED_DUTY(1, 6), + YT921X_LED_DUTY(1, 4), + YT921X_LED_DUTY(1, 3), + YT921X_LED_DUTY(1, 2), + }; + unsigned int cycle_upper; + unsigned int cycle_req; + unsigned int duty_req; + unsigned int cycle; + unsigned int duty; + + cycle = YT921X_LED_BLINK_MAX; + cycle_upper = cycle * 11585 / 8192 + 1; /* M_SQRT2 * cycle */ + if (check_add_overflow(on, off, &cycle_req) || cycle_req >= cycle_upper) + return -EOPNOTSUPP; + + for (; cycle > YT921X_LED_BLINK_MIN; cycle_upper >>= 1, cycle >>= 1) + if (cycle_req >= cycle_upper >> 1) + break; + *cyclep = cycle; + + duty_req = DIV_ROUND_CLOSEST(YT921X_LED_DUTY_DENOM * + (on > off ? off : on), cycle_req); + for (unsigned int i = ARRAY_SIZE(dutys) - 1;; i--) + if (i == 0 || duty_req >= (dutys[i - 1] + dutys[i]) / 2) { + duty = dutys[i]; + break; + } + if (on > off) + duty = YT921X_LED_DUTY_DENOM - duty; + *dutyp = duty; + + return 0; +} + +static int +yt921x_led_blink_set(struct yt921x_priv *priv, int port, int group, + unsigned long *onp, unsigned long *offp) +{ + struct yt921x_port *pp = &priv->ports[port]; + unsigned short cycle; + unsigned char duty; + bool use_cycle; + u32 ctrl; + u32 mask; + u32 val; + int res; + + if (!*onp && !*offp) { + cycle = YT921X_LED_BLINK_DEF; + duty = (pp->led_duty_mask & ~BIT(group)) ? pp->led_duty : + YT921X_LED_DUTY(1, 2); + + use_cycle = false; + } else if (!*onp || !*offp) { + return yt921x_led_force_set(priv, port, group, *onp); + } else { + res = yt921x_led_blink_select(priv, *onp, *offp, &cycle, &duty); + if (res) + return res; + + use_cycle = cycle < YT921X_LED_BLINK_DEF; + + if (use_cycle && cycle != pp->led_cycle && + (pp->led_cycle_mask & ~BIT(group))) + return -EOPNOTSUPP; + if (duty != pp->led_duty && (pp->led_duty_mask & ~BIT(group))) + return -EOPNOTSUPP; + } + + pp->led_cycle_mask &= ~BIT(group); + pp->led_duty_mask &= ~BIT(group); + + /* The chip seems to jam a while if changing duty directly */ + res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val); + if (res) + return res; + + ctrl = val & ~YT921X_LED2_PORT_FORCEn_M(group); + ctrl |= YT921X_LED2_PORT_FORCEn_OFF(group); + if (val != ctrl) { + res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl); + if (res) + return res; + } + + mask = YT921X_LED1_PORT_BLINK_DUTY_M | YT921X_LED1_PORT_BLINK_DUTY_COMP; + switch (duty >= YT921X_LED_DUTY(1, 2) ? duty : + YT921X_LED_DUTY_DENOM - duty) { + default: + duty = YT921X_LED_DUTY(1, 2); + fallthrough; + case YT921X_LED_DUTY(1, 2): + ctrl = YT921X_LED1_PORT_BLINK_DUTY_1_2; + break; + case YT921X_LED_DUTY(2, 3): + ctrl = YT921X_LED1_PORT_BLINK_DUTY_2_3; + break; + case YT921X_LED_DUTY(3, 4): + ctrl = YT921X_LED1_PORT_BLINK_DUTY_3_4; + break; + case YT921X_LED_DUTY(5, 6): + ctrl = YT921X_LED1_PORT_BLINK_DUTY_5_6; + break; + } + if (duty < YT921X_LED_DUTY(1, 2)) + ctrl |= YT921X_LED1_PORT_BLINK_DUTY_COMP; + if (use_cycle) { + mask |= YT921X_LED1_PORT_OTHER_BLINK_M; + ctrl |= YT921X_LED1_PORT_OTHER_BLINK(9 - __fls(cycle)); + } + res = yt921x_reg_update_bits(priv, YT921X_LED1_PORTn(port), mask, ctrl); + if (res) + return res; + + ctrl = val & ~(YT921X_LED2_PORT_FORCEn_M(group) | + YT921X_LED2_PORT_FORCE_BLINKn_M(group)); + ctrl |= YT921X_LED2_PORT_FORCEn_BLINK(group); + if (use_cycle) + ctrl |= YT921X_LED2_PORT_FORCE_BLINKn_OTHER(group); + else + ctrl |= YT921X_LED2_PORT_FORCE_BLINKn(group, __fls(cycle) - 9); + res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), ctrl); + if (res) + return res; + + if (use_cycle) { + pp->led_cycle_mask |= BIT(group); + pp->led_cycle = cycle; + } + pp->led_duty_mask |= BIT(group); + pp->led_duty = duty; + + *onp = DIV_ROUND_CLOSEST(duty * cycle, YT921X_LED_DUTY_DENOM); + *offp = cycle - *onp; + return 0; +} + +struct yt921x_led_trigger_map { + unsigned long flags; + u32 mask; +}; + +static const struct yt921x_led_trigger_map yt921x_led_trigger_maps[] = { + {BIT(TRIGGER_NETDEV_LINK), + YT921X_LEDx_PORT_ACT_DUPLEX_HALF | YT921X_LEDx_PORT_ACT_DUPLEX_FULL}, + {BIT(TRIGGER_NETDEV_LINK_10), YT921X_LEDx_PORT_ACT_10M}, + {BIT(TRIGGER_NETDEV_LINK_100), YT921X_LEDx_PORT_ACT_100M}, + {BIT(TRIGGER_NETDEV_LINK_1000), YT921X_LEDx_PORT_ACT_1000M}, + {BIT(TRIGGER_NETDEV_HALF_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_HALF}, + {BIT(TRIGGER_NETDEV_FULL_DUPLEX), YT921X_LEDx_PORT_ACT_DUPLEX_FULL}, + {BIT(TRIGGER_NETDEV_TX), YT921X_LEDx_PORT_ACT_TX_BLINK}, + {BIT(TRIGGER_NETDEV_RX), YT921X_LEDx_PORT_ACT_RX_BLINK}, + {BIT(TRIGGER_NETDEV_TX_ERR) | BIT(TRIGGER_NETDEV_RX_ERR), + YT921X_LEDx_PORT_ACT_COLLISION_BLINK}, +}; + +static bool +yt921x_led_trigger_is_supported(const struct yt921x_priv *priv, int port, + int group, unsigned long flags) +{ + if (!flags) + return true; + + for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) { + const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i]; + + if ((flags & map->flags) == map->flags) { + flags &= ~map->flags; + if (!flags) + return true; + } + } + + return false; +} + +static int +yt921x_led_trigger_get(struct yt921x_priv *priv, int port, int group, + unsigned long *flagsp) +{ + u32 addr; + u32 val; + int res; + + res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val); + if (res) + return res; + + if ((val & YT921X_LED2_PORT_FORCEn_M(group)) != + YT921X_LED2_PORT_FORCEn_DONTCARE(group)) { + *flagsp = 0; + return 0; + } + + if (group != 2) { + addr = yt921x_led_regaddr(priv, port, group); + res = yt921x_reg_read(priv, addr, &val); + if (res) + return res; + } + + *flagsp = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) { + const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i]; + + if ((val & map->mask) == map->mask) + *flagsp |= map->flags; + } + + return 0; +} + +static int +yt921x_led_trigger_set(struct yt921x_priv *priv, int port, int group, + unsigned long flags) +{ + struct yt921x_port *pp = &priv->ports[port]; + u32 addr; + u32 ctrl; + u32 mask; + int res; + + ctrl = 0; + for (unsigned int i = 0; i < ARRAY_SIZE(yt921x_led_trigger_maps); i++) { + const struct yt921x_led_trigger_map *map = &yt921x_led_trigger_maps[i]; + + if ((flags & map->flags) == map->flags) { + flags &= ~map->flags; + ctrl |= map->mask; + if (!flags) + break; + } + } + if (flags) + return -EOPNOTSUPP; + + pp->led_duty_mask &= ~BIT(group); + pp->led_cycle_mask &= ~BIT(group); + + mask = !group ? YT921X_LED0_PORT_ACT_M : YT921X_LEDx_PORT_ACT_M; + if (group == 2) { + mask |= YT921X_LED2_PORT_FORCEn_M(group); + ctrl |= YT921X_LED2_PORT_FORCEn_DONTCARE(group); + } + addr = yt921x_led_regaddr(priv, port, group); + res = yt921x_reg_update_bits(priv, addr, mask, ctrl); + if (res) + return res; + + if (group != 2) { + mask = YT921X_LED2_PORT_FORCEn_M(group); + ctrl = YT921X_LED2_PORT_FORCEn_DONTCARE(group); + res = yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), + mask, ctrl); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_cled_brightness_set_blocking(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_led_force_set(priv, pp->index, led->group, brightness); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_cled_blink_set(struct led_classdev *led_cdev, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_led_blink_set(priv, pp->index, led->group, delay_on, + delay_off); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static struct device __maybe_unused * +yt921x_cled_hw_control_get_device(struct led_classdev *led_cdev) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct dsa_port *dp; + + dp = dsa_to_port(&priv->ds, pp->index); + if (!dp) + return NULL; + + if (dsa_port_is_user(dp)) + return !dp->user ? NULL : &dp->user->dev; + if (dsa_port_is_cpu(dp)) + return !dp->conduit ? NULL : &dp->conduit->dev; + + return NULL; +} + +static int __maybe_unused +yt921x_cled_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + + if (yt921x_led_trigger_is_supported(priv, pp->index, led->group, flags)) + return 0; + return -EOPNOTSUPP; +} + +static int __maybe_unused +yt921x_cled_hw_control_get(struct led_classdev *led_cdev, unsigned long *flagsp) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_led_trigger_get(priv, pp->index, led->group, flagsp); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int __maybe_unused +yt921x_cled_hw_control_set(struct led_classdev *led_cdev, unsigned long flags) +{ + struct yt921x_led *led = to_yt921x_led(led_cdev); + struct yt921x_port *pp = to_yt921x_port(led); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_led_trigger_set(priv, pp->index, led->group, flags); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_led_setup(struct yt921x_priv *priv, int port, + struct fwnode_handle *fwnode) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct yt921x_port *pp = &priv->ports[port]; + struct led_init_data init_data; + struct led_classdev *led_cdev; + char name[LED_MAX_NAME_SIZE]; + enum led_default_state state; + struct yt921x_led *led; + int polarity_orig; + bool force_high; + bool force_low; + u32 led2_orig; + int polarity; + u32 group; + u32 mask; + u32 ctrl; + u32 val; + bool on; + int res; + int ret; + + res = fwnode_property_read_u32(fwnode, "reg", &group); + if (res) { + dev_err(dev, "Missing LED reg for %pfw\n", fwnode); + return res; + } + if (group >= YT921X_LED_GROUP_NUM) { + dev_err(dev, "Invalid LED reg %u for port %d\n", group, port); + return -EINVAL; + } + if (pp->leds[group]) { + res = -EEXIST; + goto err; + } + + force_high = fwnode_property_read_bool(fwnode, "active-high"); + force_low = fwnode_property_read_bool(fwnode, "active-low"); + if (force_high && force_low) { + dev_err(dev, "Duplicate polarities for LED %02d:%02u\n", + port, group); + return -EINVAL; + } + polarity = force_high ? 1 : force_low ? -1 : 0; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) { + res = -ENOMEM; + goto err; + } + pp->leds[group] = led; + + led->port = pp; + led->group = group; + + state = led_init_default_state_get(fwnode); + + mutex_lock(&priv->reg_lock); + + /* Inversion is internal - force on will give low logic. + * In the rest of the file, treat LEDs as if active-low. + */ + polarity_orig = 0; + if (polarity) { + res = yt921x_reg_read(priv, YT921X_LED_PAR_INV, &val); + if (res) + goto err_inv; + + mask = YT921X_LED_PAR_INV_INVnm(group, port); + ctrl = polarity > 0 ? val | mask : val & ~mask; + if (ctrl != val) { + res = yt921x_reg_write(priv, YT921X_LED_PAR_INV, ctrl); + if (res) + goto err_inv; + + polarity_orig = -polarity; + } + } + + led2_orig = U32_MAX; + res = yt921x_reg_read(priv, YT921X_LED2_PORTn(port), &val); + if (res) + goto err_led2; + mask = YT921X_LED2_PORT_FORCEn_M(group); + + switch (state) { + case LEDS_DEFSTATE_OFF: + case LEDS_DEFSTATE_ON: + default: + on = state == LEDS_DEFSTATE_ON; + ctrl = (val & ~mask) | + (on ? YT921X_LED2_PORT_FORCEn_ON(group) : + YT921X_LED2_PORT_FORCEn_OFF(group)); + if (ctrl != val) { + res = yt921x_reg_write(priv, YT921X_LED2_PORTn(port), + ctrl); + if (res) + goto err_led2; + + led2_orig = val & mask; + } + break; + case LEDS_DEFSTATE_KEEP: + on = (val & mask) == YT921X_LED2_PORT_FORCEn_ON(group); + break; + } + + mutex_unlock(&priv->reg_lock); + + led_cdev = &led->cdev; + led_cdev->brightness = on; + led_cdev->max_brightness = 1; + led_cdev->flags = LED_RETAIN_AT_SHUTDOWN; + led_cdev->brightness_set_blocking = yt921x_cled_brightness_set_blocking; + led_cdev->blink_set = yt921x_cled_blink_set; +#ifdef CONFIG_LEDS_TRIGGERS + led_cdev->hw_control_trigger = "netdev"; + led_cdev->hw_control_get_device = yt921x_cled_hw_control_get_device; + led_cdev->hw_control_is_supported = yt921x_cled_hw_control_is_supported; + led_cdev->hw_control_get = yt921x_cled_hw_control_get; + led_cdev->hw_control_set = yt921x_cled_hw_control_set; +#endif + + snprintf(name, sizeof(name), YT921X_NAME "-%u:%02d:%02u", + priv->ds.index, port, group); + init_data = (typeof(init_data)){ + .fwnode = fwnode, + .default_label = ":port", + .devicename = name, + .devname_mandatory = true, + }; + res = devm_led_classdev_register_ext(dev, led_cdev, &init_data); + if (res) + goto revoke; + + return 0; + +revoke: + mutex_lock(&priv->reg_lock); + if (led2_orig != U32_MAX) { + ret = yt921x_reg_update_bits(priv, YT921X_LED2_PORTn(port), + YT921X_LED2_PORT_FORCEn_M(group), + led2_orig); + if (ret) + dev_err(dev, + "Failed to restore %s for LED %02d:%02u: %d\n", + "LED2_PORT", port, group, ret); + } +err_led2: + if (polarity_orig) { + ret = yt921x_reg_toggle_bits(priv, YT921X_LED_PAR_INV, + YT921X_LED_PAR_INV_INVnm(group, port), + polarity_orig > 0); + if (ret) + dev_err(dev, + "Failed to restore %s for LED %02d:%02u: %d\n", + "LED_PAR_INV", port, group, ret); + } +err_inv: + mutex_unlock(&priv->reg_lock); + + pp->leds[group] = NULL; + devm_kfree(dev, led); + +err: + dev_err(dev, "Failed to initialize LED %02d:%02u: %d\n", + port, group, res); + return res; +} + +static void yt921x_leds_remove_port(struct yt921x_priv *priv, int port) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct yt921x_port *pp = &priv->ports[port]; + + for (int group = 0; group < YT921X_LED_GROUP_NUM; group++) { + struct yt921x_led *led = pp->leds[group]; + + if (led) { + devm_led_classdev_unregister(dev, &led->cdev); + pp->leds[group] = NULL; + devm_kfree(dev, led); + } + } + + pp->led_duty_mask = 0; + pp->led_cycle_mask = 0; +} + +static int yt921x_leds_setup_port(struct yt921x_priv *priv, int port) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct dsa_switch *ds = &priv->ds; + struct device_node *leds_np; + struct dsa_port *dp; + + dp = dsa_to_port(ds, port); + leds_np = of_get_child_by_name(dp->dn, "leds"); + if (!leds_np) + return 0; + + if (port >= YT921X_LED_PORT_NUM) { + dev_err(dev, "Cannot configure LEDs for port %d\n", port); + of_node_put(leds_np); + return -EINVAL; + } + + for_each_child_of_node_scoped(leds_np, led_np) { + yt921x_led_setup(priv, port, of_fwnode_handle(led_np)); + /* Allow partial configuration: LEDs are optional */ + } + + of_node_put(leds_np); + return 0; +} + +void yt921x_leds_remove(struct yt921x_priv *priv) +{ + for (int port = 0; port < YT921X_LED_PORT_NUM; port++) + yt921x_leds_remove_port(priv, port); +} + +int yt921x_leds_setup(struct yt921x_priv *priv) +{ + struct dsa_switch *ds = &priv->ds; + struct dsa_port *dp; + + /* LEDs are always enabled. There is no way to disable them altogether + * (as far as I know). + */ + + dsa_switch_for_each_port(dp, ds) { + int port = dp->index; + + if (!dp->dn) + continue; + + yt921x_leds_setup_port(priv, port); + /* Allow partial configuration: LEDs are optional */ + } + + return 0; +} diff --git a/drivers/net/dsa/motorcomm/leds.h b/drivers/net/dsa/motorcomm/leds.h new file mode 100644 index 000000000000..11df479ee581 --- /dev/null +++ b/drivers/net/dsa/motorcomm/leds.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_LEDS_H +#define _YT_LEDS_H + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/kconfig.h> +#include <linux/leds.h> + +#define YT921X_LED_CTRL 0xd0000 +#define YT921X_LED_CTRL_UNK BIT(21) +#define YT921X_LED_CTRL_LOOPDETECT_BLINK_M GENMASK(20, 19) /* cycle = 512 * x ms */ +#define YT921X_LED_CTRL_LOOPDETECT_BLINK(x) FIELD_PREP(YT921X_LED_CTRL_LOOPDETECT_BLINK_M, (x)) +#define YT921X_LED_CTRL_PORT_NUM_M GENMASK(16, 13) +#define YT921X_LED_CTRL_PORT_NUM(x) FIELD_PREP(YT921X_LED_CTRL_PORT_NUM_M, (x)) +#define YT921X_LED_CTRL_MODE_M GENMASK(1, 0) +#define YT921X_LED_CTRL_MODE(x) FIELD_PREP(YT921X_LED_CTRL_MODE_M, (x)) +#define YT921X_LED_CTRL_MODE_PARALLEL YT921X_LED_CTRL_MODE(0) +#define YT921X_LED_CTRL_MODE_SERIAL YT921X_LED_CTRL_MODE(2) +#define YT921X_LED0_PORTn(port) (0xd0004 + 4 * (port)) +#define YT921X_LED0_PORT_ACT_M GENMASK(17, 0) +#define YT921X_LED0_PORT_ACT_LINK_TRY_DIS BIT(17) +#define YT921X_LED0_PORT_ACT_COLLISION_BLINK_INDI BIT(16) +#define YT921X_LED1_PORTn(port) (0xd0040 + 4 * (port)) +#define YT921X_LED1_PORT_OTHER_BLINK_M GENMASK(31, 30) /* cycle = 512 >> x ms */ +#define YT921X_LED1_PORT_OTHER_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_OTHER_BLINK_M, (x)) +#define YT921X_LED1_PORT_EEE_BLINK_M GENMASK(29, 28) /* cycle = 512 >> x ms */ +#define YT921X_LED1_PORT_EEE_BLINK(x) FIELD_PREP(YT921X_LED1_PORT_EEE_BLINK_M, (x)) +#define YT921X_LED1_PORT_BLINK_DUTY_COMP BIT(27) +#define YT921X_LED1_PORT_BLINK_DUTY_M GENMASK(26, 25) +#define YT921X_LED1_PORT_BLINK_DUTY(x) FIELD_PREP(YT921X_LED1_PORT_BLINK_DUTY_M, (x)) +#define YT921X_LED1_PORT_BLINK_DUTY_1_2 YT921X_LED1_PORT_BLINK_DUTY(0) +#define YT921X_LED1_PORT_BLINK_DUTY_2_3 YT921X_LED1_PORT_BLINK_DUTY(1) +#define YT921X_LED1_PORT_BLINK_DUTY_3_4 YT921X_LED1_PORT_BLINK_DUTY(2) +#define YT921X_LED1_PORT_BLINK_DUTY_5_6 YT921X_LED1_PORT_BLINK_DUTY(3) +#define YT921X_LED2_PORTn(port) (0xd0080 + 4 * (port)) +#define YT921X_LED2_PORT_FORCEn_M(grp) GENMASK(4 * (grp) + 19, 4 * (grp) + 18) +#define YT921X_LED2_PORT_FORCEn(grp, x) ((x) << (4 * (grp) + 18)) +#define YT921X_LED2_PORT_FORCEn_DONTCARE(grp) YT921X_LED2_PORT_FORCEn(grp, 0) +#define YT921X_LED2_PORT_FORCEn_BLINK(grp) YT921X_LED2_PORT_FORCEn(grp, 1) +#define YT921X_LED2_PORT_FORCEn_ON(grp) YT921X_LED2_PORT_FORCEn(grp, 2) +#define YT921X_LED2_PORT_FORCEn_OFF(grp) YT921X_LED2_PORT_FORCEn(grp, 3) +#define YT921X_LED2_PORT_FORCE_BLINKn_M(grp) GENMASK(4 * (grp) + 17, 4 * (grp) + 16) /* cycle = 512 << x ms */ +#define YT921X_LED2_PORT_FORCE_BLINKn(grp, x) ((x) << (4 * (grp) + 16)) +#define YT921X_LED2_PORT_FORCE_BLINKn_OTHER(grp) YT921X_LED2_PORT_FORCE_BLINKn(grp, 3) +#define YT921X_LEDx_PORT_ACT_M GENMASK(15, 0) +#define YT921X_LEDx_PORT_ACT_EEE_BLINK BIT(15) +#define YT921X_LEDx_PORT_ACT_LOOPDETECT_BLINK BIT(14) +#define YT921X_LEDx_PORT_ACT_ACTIVE_BLINK BIT(13) +#define YT921X_LEDx_PORT_ACT_DUPLEX_FULL BIT(12) +#define YT921X_LEDx_PORT_ACT_DUPLEX_HALF BIT(11) +#define YT921X_LEDx_PORT_ACT_TX_BLINK BIT(10) +#define YT921X_LEDx_PORT_ACT_RX_BLINK BIT(9) +#define YT921X_LEDx_PORT_ACT_TX BIT(8) +#define YT921X_LEDx_PORT_ACT_RX BIT(7) +#define YT921X_LEDx_PORT_ACT_1000M BIT(6) +#define YT921X_LEDx_PORT_ACT_100M BIT(5) +#define YT921X_LEDx_PORT_ACT_10M BIT(4) +#define YT921X_LEDx_PORT_ACT_COLLISION_BLINK BIT(3) +#define YT921X_LEDx_PORT_ACT_1000M_BLINK BIT(2) +#define YT921X_LEDx_PORT_ACT_100M_BLINK BIT(1) +#define YT921X_LEDx_PORT_ACT_10M_BLINK BIT(0) +#define YT921X_LED_SER_CTRL 0xd0100 +#define YT921X_LED_SER_CTRL_UNK GENMASK(25, 24) /* delay? */ +#define YT921X_LED_SER_CTRL_ACTIVE_LOW BIT(4) +#define YT921X_LED_SER_CTRL_GRP_NUM_M GENMASK(1, 0) /* #grp - 1 */ +#define YT921X_LED_SER_CTRL_GRP_NUM(x) FIELD_PREP(YT921X_LED_SER_CTRL_GRP_NUM_M, (x)) +#define YT921X_LED_SER_MAPnm(grp, port) (0xd0104 + 8 * (2 - (grp)) + 4 * ((port) / 5)) +#define YT921X_LED_SER_MAP_DSTn_PORT_M(port) GENMASK(6 * ((port) % 5) + 5, 6 * ((port) % 5) + 2) +#define YT921X_LED_SER_MAP_DSTn_PORT(port, x) ((x) << (6 * ((port) % 5) + 2)) +#define YT921X_LED_SER_MAP_DSTn_LED_M(port) GENMASK(6 * ((port) % 5) + 1, 6 * ((port) % 5)) +#define YT921X_LED_SER_MAP_DSTn_LED(port, x) ((x) << (6 * ((port) % 5))) +#define YT921X_LED_PAR_PORTS 0xd01c4 +#define YT921X_LED_PAR_INV 0xd01c8 +#define YT921X_LED_PAR_INV_INVnm(grp, port) BIT(10 * (grp) + (port)) +#define YT921X_LED_PAR_MAPn(port) (0xd01d0 + 4 * (port)) +#define YT921X_LED_PAR_MAP_DSTn_PORT_M(grp) GENMASK(6 * (grp) + 5, 6 * (grp) + 2) +#define YT921X_LED_PAR_MAP_DSTn_PORT(grp, x) ((x) << (6 * (grp) + 2)) +#define YT921X_LED_PAR_MAP_DSTn_LED_M(grp) GENMASK(6 * (grp) + 1, 6 * (grp)) +#define YT921X_LED_PAR_MAP_DSTn_LED(grp, x) ((x) << (6 * (grp))) + +#define YT921X_LED_BLINK_MIN 64 +#define YT921X_LED_BLINK_DEF 512 +#define YT921X_LED_BLINK_MAX 2048 + +/* 2 * lcm(2, 3, 4, 6) */ +#define YT921X_LED_DUTY_DENOM 24 +#define YT921X_LED_DUTY(nom, denom) (YT921X_LED_DUTY_DENOM * (nom) / (denom)) + +struct yt921x_priv; + +struct yt921x_led { + struct led_classdev cdev; + struct yt921x_port *port; + unsigned char group; +}; + +#if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) + +void yt921x_leds_remove(struct yt921x_priv *priv); +int yt921x_leds_setup(struct yt921x_priv *priv); + +#else + +static inline void yt921x_leds_remove(struct yt921x_priv *priv) {} + +static inline int yt921x_leds_setup(struct yt921x_priv *priv) +{ + return 0; +} + +#endif + +#endif diff --git a/drivers/net/dsa/motorcomm/mdio_bus.c b/drivers/net/dsa/motorcomm/mdio_bus.c new file mode 100644 index 000000000000..dd6c52f37bbf --- /dev/null +++ b/drivers/net/dsa/motorcomm/mdio_bus.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include <linux/of_mdio.h> + +#include "chip.h" +#include "mdio_bus.h" +#include "smi.h" + +static int yt921x_intif_wait(struct yt921x_priv *priv) +{ + u32 val = 0; + + return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev = yt921x_priv_to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_READ; + res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val != val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp = (u16)val; + return 0; +} + +int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res = yt921x_intif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_WRITE; + res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_intif_wait(priv); +} + +int +yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg, + u16 mask, u16 val) +{ + int res; + u16 v; + u16 u; + + res = yt921x_intif_read(priv, port, reg, &v); + if (res) + return res; + + u = v; + u &= ~mask; + u |= val; + if (u == v) + return 0; + + res = yt921x_intif_write(priv, port, reg, u); + if (res) + return res; + + return 1; +} + +static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv = mbus->priv; + u16 val; + int res; + + if (port >= YT921X_PORT_NUM) + return U16_MAX; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv = mbus->priv; + int res; + + if (port >= YT921X_PORT_NUM) + return -ENODEV; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct mii_bus *mbus; + int res; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = "YT921x internal MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); + mbus->priv = priv; + mbus->read = yt921x_mbus_int_read; + mbus->write = yt921x_mbus_int_write; + mbus->parent = dev; + mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); + + res = devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_int = mbus; + + return 0; +} + +static int yt921x_extif_wait(struct yt921x_priv *priv) +{ + u32 val = 0; + + return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, + &val); +} + +static int +yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) +{ + struct device *dev = yt921x_priv_to_device(priv); + u32 mask; + u32 ctrl; + u32 val; + int res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; + res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); + if (res) + return res; + + if ((u16)val != val) + dev_info(dev, + "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", + __func__, port, reg, val); + *valp = (u16)val; + return 0; +} + +static int +yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) +{ + u32 mask; + u32 ctrl; + int res; + + res = yt921x_extif_wait(priv); + if (res) + return res; + + mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | + YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; + ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | + YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; + res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); + if (res) + return res; + res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); + if (res) + return res; + + return yt921x_extif_wait(priv); +} + +static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) +{ + struct yt921x_priv *priv = mbus->priv; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_extif_read(priv, port, reg, &val); + mutex_unlock(&priv->reg_lock); + + if (res) + return res; + return val; +} + +static int +yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) +{ + struct yt921x_priv *priv = mbus->priv; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_extif_write(priv, port, reg, data); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct mii_bus *mbus; + int res; + + mbus = devm_mdiobus_alloc(dev); + if (!mbus) + return -ENOMEM; + + mbus->name = "YT921x external MDIO bus"; + snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); + mbus->priv = priv; + /* TODO: c45? */ + mbus->read = yt921x_mbus_ext_read; + mbus->write = yt921x_mbus_ext_write; + mbus->parent = dev; + + res = devm_of_mdiobus_register(dev, mbus, mnp); + if (res) + return res; + + priv->mbus_ext = mbus; + + return 0; +} diff --git a/drivers/net/dsa/motorcomm/mdio_bus.h b/drivers/net/dsa/motorcomm/mdio_bus.h new file mode 100644 index 000000000000..e79b725d435b --- /dev/null +++ b/drivers/net/dsa/motorcomm/mdio_bus.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_MDIO_BUS_H +#define _YT_MDIO_BUS_H + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/of.h> + +#define YT921X_EXT_MBUS_OP 0x6a000 +#define YT921X_INT_MBUS_OP 0xf0000 +#define YT921X_MBUS_OP_START BIT(0) +#define YT921X_EXT_MBUS_CTRL 0x6a004 +#define YT921X_INT_MBUS_CTRL 0xf0004 +#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) +#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x)) +#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) +#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x)) +#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ +#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x)) +#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4) +#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ +#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x)) +#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1) +#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2) +#define YT921X_EXT_MBUS_DOUT 0x6a008 +#define YT921X_INT_MBUS_DOUT 0xf0008 +#define YT921X_EXT_MBUS_DIN 0x6a00c +#define YT921X_INT_MBUS_DIN 0xf000c + +struct yt921x_priv; + +int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp); +int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val); +int yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg, + u16 mask, u16 val); + +static inline int +yt921x_intif_modify(struct yt921x_priv *priv, int port, int reg, u16 mask, + u16 val) +{ + int res; + + res = yt921x_intif_modify_changed(priv, port, reg, mask, val); + return res >= 0 ? 0 : res; +} + +int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp); +int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp); + +#endif diff --git a/drivers/net/dsa/motorcomm/mib.c b/drivers/net/dsa/motorcomm/mib.c new file mode 100644 index 000000000000..6b170fc6e187 --- /dev/null +++ b/drivers/net/dsa/motorcomm/mib.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include "chip.h" +#include "mib.h" +#include "smi.h" + +struct yt921x_mib_desc { + unsigned int size; + unsigned int offset; + const char *name; +}; + +#define MIB_DESC(_size, _offset, _name) \ + {_size, _offset, _name} + +/* Must agree with yt921x_mib_stats + * + * Unstructured fields (name != NULL) will appear in get_ethtool_stats(), + * structured go to their *_stats() methods, but we need their sizes and offsets + * to perform 32bit MIB overflow wraparound. + */ +static const struct yt921x_mib_desc yt921x_mib_descs[] = { + MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL), + MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL), + + MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"), + MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL), + + MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL), + + MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL), + MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL), + MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"), + MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"), +}; + +static_assert(ARRAY_SIZE(yt921x_mib_descs) == YT921X_MIB_STAT_NUM); + +/* The interval should be small enough to avoid overflow of 32bit MIBs. + * + * Until we can read MIBs from stats64 call directly (i.e. sleep + * there), we have to poll stats more frequently then it is actually needed. + * For overflow protection, normally, 100 sec interval should have been OK. + */ +#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ) + +/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */ +static int yt921x_mib_read(struct yt921x_priv *priv, int port) +{ + struct device *dev = yt921x_priv_to_device(priv); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + u64 *buf = pm->data; + unsigned long flags; + u64 rx_frames; + u64 tx_frames; + int res = 0; + + mib = &pm->stats; + + for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; + u32 reg = YT921X_MIBn_DATA0(port) + desc->offset; + u32 val0; + u64 val; + + res = yt921x_reg_read(priv, reg, &val0); + if (res) + break; + + if (desc->size <= 1) { + u64 old_val = buf[i]; + + val = (old_val & ~(u64)U32_MAX) | val0; + if (val < old_val) + val += 1ull << 32; + } else { + u32 val1; + + res = yt921x_reg_read(priv, reg + 4, &val1); + if (res) + break; + val = ((u64)val1 << 32) | val0; + } + + buf[i] = val; + } + + flags = u64_stats_update_begin_irqsave(&pm->syncp); + for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) + u64_stats_set(&((u64_stats_t *)mib)[i], buf[i]); + rx_frames = u64_stats_read(&mib->rx_64byte) + + u64_stats_read(&mib->rx_65_127byte) + + u64_stats_read(&mib->rx_128_255byte) + + u64_stats_read(&mib->rx_256_511byte) + + u64_stats_read(&mib->rx_512_1023byte) + + u64_stats_read(&mib->rx_1024_1518byte) + + u64_stats_read(&mib->rx_jumbo); + tx_frames = u64_stats_read(&mib->tx_64byte) + + u64_stats_read(&mib->tx_65_127byte) + + u64_stats_read(&mib->tx_128_255byte) + + u64_stats_read(&mib->tx_256_511byte) + + u64_stats_read(&mib->tx_512_1023byte) + + u64_stats_read(&mib->tx_1024_1518byte) + + u64_stats_read(&mib->tx_jumbo); + u64_stats_set(&pm->rx_frames, rx_frames); + u64_stats_set(&pm->tx_frames, tx_frames); + u64_stats_update_end_irqrestore(&pm->syncp, flags); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "read stats for", + port, res); + return res; +} + +void yt921x_mib_poll(struct work_struct *work) +{ + struct yt921x_mib *pm = container_of_const(work, struct yt921x_mib, + work.work); + struct yt921x_port *pp = pm->port; + unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES; + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int port = pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_mib_read(priv, port); + mutex_unlock(&priv->reg_lock); + if (res) + delay *= 4; + + schedule_delayed_work(&pm->work, delay); +} + +void +yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data) +{ + if (stringset != ETH_SS_STATS) + return; + + for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; + + if (desc->name) + ethtool_puts(&data, desc->name); + } +} + +void +yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + u64 *buf; + size_t j; + + if (!pm) + return; + buf = pm->data; + + mutex_lock(&priv->reg_lock); + + yt921x_mib_read(priv, port); + + j = 0; + for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; + + if (!desc->name) + continue; + + data[j] = buf[i]; + j++; + } + + mutex_unlock(&priv->reg_lock); +} + +int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset) +{ + int cnt = 0; + + if (sset != ETH_SS_STATS) + return 0; + + for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { + const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; + + if (desc->name) + cnt++; + } + + return cnt; +} + +void +yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + + if (!pm) + return; + mib = &pm->stats; + + mutex_lock(&priv->reg_lock); + + yt921x_mib_read(priv, port); + + mac_stats->FramesTransmittedOK = u64_stats_read(&pm->tx_frames); + mac_stats->SingleCollisionFrames = u64_stats_read(&mib->tx_single_collisions); + mac_stats->MultipleCollisionFrames = u64_stats_read(&mib->tx_multiple_collisions); + mac_stats->FramesReceivedOK = u64_stats_read(&pm->rx_frames); + mac_stats->FrameCheckSequenceErrors = u64_stats_read(&mib->rx_crc_errors); + mac_stats->AlignmentErrors = u64_stats_read(&mib->rx_alignment_errors); + mac_stats->OctetsTransmittedOK = u64_stats_read(&mib->tx_good_bytes); + mac_stats->FramesWithDeferredXmissions = u64_stats_read(&mib->tx_deferred); + mac_stats->LateCollisions = u64_stats_read(&mib->tx_late_collisions); + mac_stats->FramesAbortedDueToXSColls = u64_stats_read(&mib->tx_aborted_errors); + /* mac_stats->FramesLostDueToIntMACXmitError */ + /* mac_stats->CarrierSenseErrors */ + mac_stats->OctetsReceivedOK = u64_stats_read(&mib->rx_good_bytes); + /* mac_stats->FramesLostDueToIntMACRcvError */ + mac_stats->MulticastFramesXmittedOK = u64_stats_read(&mib->tx_multicast); + mac_stats->BroadcastFramesXmittedOK = u64_stats_read(&mib->tx_broadcast); + /* mac_stats->FramesWithExcessiveDeferral */ + mac_stats->MulticastFramesReceivedOK = u64_stats_read(&mib->rx_multicast); + mac_stats->BroadcastFramesReceivedOK = u64_stats_read(&mib->rx_broadcast); + /* mac_stats->InRangeLengthErrors */ + /* mac_stats->OutOfRangeLengthField */ + mac_stats->FrameTooLongErrors = u64_stats_read(&mib->rx_oversize_errors); + + mutex_unlock(&priv->reg_lock); +} + +void +yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + + if (!pm) + return; + mib = &pm->stats; + + mutex_lock(&priv->reg_lock); + + yt921x_mib_read(priv, port); + + ctrl_stats->MACControlFramesTransmitted = u64_stats_read(&mib->tx_pause); + ctrl_stats->MACControlFramesReceived = u64_stats_read(&mib->rx_pause); + /* ctrl_stats->UnsupportedOpcodesReceived */ + + mutex_unlock(&priv->reg_lock); +} + +static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = { + { 0, 64 }, + { 65, 127 }, + { 128, 255 }, + { 256, 511 }, + { 512, 1023 }, + { 1024, 1518 }, + { 1519, YT921X_FRAME_SIZE_MAX }, + {} +}; + +void +yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + + if (!pm) + return; + mib = &pm->stats; + + mutex_lock(&priv->reg_lock); + + yt921x_mib_read(priv, port); + + *ranges = yt921x_rmon_ranges; + + rmon_stats->undersize_pkts = u64_stats_read(&mib->rx_undersize_errors); + rmon_stats->oversize_pkts = u64_stats_read(&mib->rx_oversize_errors); + rmon_stats->fragments = u64_stats_read(&mib->rx_alignment_errors); + /* rmon_stats->jabbers */ + + rmon_stats->hist[0] = u64_stats_read(&mib->rx_64byte); + rmon_stats->hist[1] = u64_stats_read(&mib->rx_65_127byte); + rmon_stats->hist[2] = u64_stats_read(&mib->rx_128_255byte); + rmon_stats->hist[3] = u64_stats_read(&mib->rx_256_511byte); + rmon_stats->hist[4] = u64_stats_read(&mib->rx_512_1023byte); + rmon_stats->hist[5] = u64_stats_read(&mib->rx_1024_1518byte); + rmon_stats->hist[6] = u64_stats_read(&mib->rx_jumbo); + + rmon_stats->hist_tx[0] = u64_stats_read(&mib->tx_64byte); + rmon_stats->hist_tx[1] = u64_stats_read(&mib->tx_65_127byte); + rmon_stats->hist_tx[2] = u64_stats_read(&mib->tx_128_255byte); + rmon_stats->hist_tx[3] = u64_stats_read(&mib->tx_256_511byte); + rmon_stats->hist_tx[4] = u64_stats_read(&mib->tx_512_1023byte); + rmon_stats->hist_tx[5] = u64_stats_read(&mib->tx_1024_1518byte); + rmon_stats->hist_tx[6] = u64_stats_read(&mib->tx_jumbo); + + mutex_unlock(&priv->reg_lock); +} + +void +yt921x_dsa_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *stats) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + unsigned int start; + + if (!pm) + return; + mib = &pm->stats; + + do { + start = u64_stats_fetch_begin(&pm->syncp); + + stats->rx_length_errors = u64_stats_read(&mib->rx_undersize_errors) + + u64_stats_read(&mib->rx_fragment_errors); + stats->rx_over_errors = u64_stats_read(&mib->rx_oversize_errors); + stats->rx_crc_errors = u64_stats_read(&mib->rx_crc_errors); + stats->rx_frame_errors = u64_stats_read(&mib->rx_alignment_errors); + /* stats->rx_fifo_errors */ + /* stats->rx_missed_errors */ + + stats->tx_aborted_errors = u64_stats_read(&mib->tx_aborted_errors); + /* stats->tx_carrier_errors */ + stats->tx_fifo_errors = u64_stats_read(&mib->tx_undersize_errors); + /* stats->tx_heartbeat_errors */ + stats->tx_window_errors = u64_stats_read(&mib->tx_late_collisions); + + stats->rx_packets = u64_stats_read(&pm->rx_frames); + stats->tx_packets = u64_stats_read(&pm->tx_frames); + stats->rx_bytes = u64_stats_read(&mib->rx_good_bytes) - + ETH_FCS_LEN * stats->rx_packets; + stats->tx_bytes = u64_stats_read(&mib->tx_good_bytes) - + ETH_FCS_LEN * stats->tx_packets; + stats->rx_dropped = u64_stats_read(&mib->rx_dropped); + /* stats->tx_dropped */ + stats->multicast = u64_stats_read(&mib->rx_multicast); + stats->collisions = u64_stats_read(&mib->tx_collisions); + } while (u64_stats_fetch_retry(&pm->syncp, start)); + + stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors + + stats->rx_crc_errors + stats->rx_frame_errors; + stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors + + stats->tx_window_errors; +} + +void +yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_port *pp = &priv->ports[port]; + struct yt921x_mib *pm = pp->mib; + struct yt921x_mib_stats *mib; + + if (!pm) + return; + mib = &pm->stats; + + mutex_lock(&priv->reg_lock); + + yt921x_mib_read(priv, port); + + pause_stats->tx_pause_frames = u64_stats_read(&mib->tx_pause); + pause_stats->rx_pause_frames = u64_stats_read(&mib->rx_pause); + + mutex_unlock(&priv->reg_lock); +} diff --git a/drivers/net/dsa/motorcomm/mib.h b/drivers/net/dsa/motorcomm/mib.h new file mode 100644 index 000000000000..9c24a52d46cc --- /dev/null +++ b/drivers/net/dsa/motorcomm/mib.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_MIB_H +#define _YT_MIB_H + +#include <linux/u64_stats_sync.h> + +#include <net/dsa.h> + +#define YT921X_MIB_CTRL 0xc0004 +#define YT921X_MIB_CTRL_CLEAN BIT(30) +#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3) +#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x)) +#define YT921X_MIB_CTRL_ONE_PORT BIT(1) +#define YT921X_MIB_CTRL_ALL_PORT BIT(0) +#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port)) +#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x)) +#define YT921X_MIB_DATA_RX_BROADCAST 0x00 +#define YT921X_MIB_DATA_RX_PAUSE 0x04 +#define YT921X_MIB_DATA_RX_MULTICAST 0x08 +#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c + +#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10 +#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14 +#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18 +#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c + +#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20 +#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24 +#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28 +#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c + +#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30 +#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34 +/* 0x38: unused */ +#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c + +/* 0x40: 64 bytes */ +#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44 +/* 0x48: 64 bytes */ +#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c + +#define YT921X_MIB_DATA_RX_DROPPED 0x50 +#define YT921X_MIB_DATA_TX_BROADCAST 0x54 +#define YT921X_MIB_DATA_TX_PAUSE 0x58 +#define YT921X_MIB_DATA_TX_MULTICAST 0x5c + +#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60 +#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64 +#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68 +#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c + +#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70 +#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74 +#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78 +#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c + +/* 0x80: unused */ +#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84 +/* 0x88: 64 bytes */ +#define YT921X_MIB_DATA_TX_COLLISION 0x8c + +#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90 +#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94 +#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98 +#define YT921X_MIB_DATA_TX_PKT 0x9c + +#define YT921X_MIB_DATA_TX_DEFERRED 0xa0 +#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4 +#define YT921X_MIB_DATA_RX_OAM 0xa8 +#define YT921X_MIB_DATA_TX_OAM 0xac + +struct yt921x_mib_stats { + u64_stats_t rx_broadcast; + u64_stats_t rx_pause; + u64_stats_t rx_multicast; + u64_stats_t rx_crc_errors; + + u64_stats_t rx_alignment_errors; + u64_stats_t rx_undersize_errors; + u64_stats_t rx_fragment_errors; + u64_stats_t rx_64byte; + + u64_stats_t rx_65_127byte; + u64_stats_t rx_128_255byte; + u64_stats_t rx_256_511byte; + u64_stats_t rx_512_1023byte; + + u64_stats_t rx_1024_1518byte; + u64_stats_t rx_jumbo; + u64_stats_t rx_good_bytes; + + u64_stats_t rx_bad_bytes; + u64_stats_t rx_oversize_errors; + + u64_stats_t rx_dropped; + u64_stats_t tx_broadcast; + u64_stats_t tx_pause; + u64_stats_t tx_multicast; + + u64_stats_t tx_undersize_errors; + u64_stats_t tx_64byte; + u64_stats_t tx_65_127byte; + u64_stats_t tx_128_255byte; + + u64_stats_t tx_256_511byte; + u64_stats_t tx_512_1023byte; + u64_stats_t tx_1024_1518byte; + u64_stats_t tx_jumbo; + + u64_stats_t tx_good_bytes; + u64_stats_t tx_collisions; + + u64_stats_t tx_aborted_errors; + u64_stats_t tx_multiple_collisions; + u64_stats_t tx_single_collisions; + u64_stats_t tx_good; + + u64_stats_t tx_deferred; + u64_stats_t tx_late_collisions; + u64_stats_t rx_oam; + u64_stats_t tx_oam; +}; + +#define YT921X_MIB_STAT_NUM (sizeof(struct yt921x_mib_stats) / sizeof(u64_stats_t)) + +struct yt921x_mib { + struct yt921x_port *port; + + struct delayed_work work; + struct u64_stats_sync syncp; + /* writers: priv->reg_lock + syncp; readers: priv->reg_lock or syncp */ + struct yt921x_mib_stats stats; + u64_stats_t rx_frames; + u64_stats_t tx_frames; + /* protected by priv->reg_lock */ + u64 data[YT921X_MIB_STAT_NUM]; +}; + +void yt921x_mib_poll(struct work_struct *work); +void +yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, + uint8_t *data); +void +yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data); +int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset); +void +yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_mac_stats *mac_stats); +void +yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port, + struct ethtool_eth_ctrl_stats *ctrl_stats); +void +yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port, + struct ethtool_rmon_stats *rmon_stats, + const struct ethtool_rmon_hist_range **ranges); +void +yt921x_dsa_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *stats); +void +yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats); + +#endif diff --git a/drivers/net/dsa/motorcomm/pcs-921x.c b/drivers/net/dsa/motorcomm/pcs-921x.c new file mode 100644 index 000000000000..e942b61d8eb2 --- /dev/null +++ b/drivers/net/dsa/motorcomm/pcs-921x.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include "chip.h" +#include "mdio_bus.h" +#include "pcs.h" +#include "smi.h" + +static int +yt921x_serdes_config(struct yt921x_priv *priv, int port, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, bool permit_pause_to_mac) +{ + bool inband = neg_mode & PHYLINK_PCS_NEG_INBAND; + struct yt921x_port *pp = &priv->ports[port]; + bool changed = false; + u16 bmcr; + u32 mask; + u32 ctrl; + u16 val; + int adv; + int res; + + switch (interface) { + case PHY_INTERFACE_MODE_SGMII: + ctrl = YT921X_SERDES_MODE_SGMII; + break; + case PHY_INTERFACE_MODE_100BASEX: + ctrl = YT921X_SERDES_MODE_100BASEX; + break; + case PHY_INTERFACE_MODE_1000BASEX: + ctrl = YT921X_SERDES_MODE_1000BASEX; + break; + case PHY_INTERFACE_MODE_2500BASEX: + ctrl = YT921X_SERDES_MODE_2500BASEX; + break; + default: + return -EOPNOTSUPP; + } + + res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), + YT921X_SERDES_MODE_M, ctrl); + if (res) + return res; + + mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_CTRL_PAUSE_AN; + ctrl = 0; + if (inband) + ctrl |= YT921X_PORT_CTRL_LINK_AN; + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED && permit_pause_to_mac && + interface != PHY_INTERFACE_MODE_SGMII) + ctrl |= YT921X_PORT_CTRL_PAUSE_AN; + res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); + if (res) + return res; + + if (inband) { + res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), + YT921X_SERDES_LINK); + if (res) + return res; + } + + adv = phylink_mii_c22_pcs_encode_advertisement(interface, advertising); + if (adv >= 0) { + res = yt921x_intif_modify_changed(priv, port, MII_ADVERTISE, + U16_MAX, adv); + if (res < 0) + return res; + + changed = !!res; + } + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + return res; + + bmcr = val; + if (neg_mode == PHYLINK_PCS_NEG_INBAND_ENABLED) + bmcr |= BMCR_ANENABLE; + else + bmcr &= ~BMCR_ANENABLE; + + /* If the ANENABLE bit was changed, the PHY will restart negotiation, + * so we don't need to flag a change to trigger its own restart. + */ + if (bmcr != val) + changed = false; + + bmcr &= ~BMCR_ISOLATE; + res = yt921x_intif_write(priv, port, MII_BMCR, bmcr); + if (res) + return res; + + pp->inband = inband; + return changed; +} + +static unsigned int +yt921x_phylink_pcs_inband_caps(struct phylink_pcs *pcs, + phy_interface_t interface) +{ + if (interface == PHY_INTERFACE_MODE_100BASEX) + return LINK_INBAND_DISABLE; + + return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE; +} + +static void +yt921x_phylink_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, + struct phylink_link_state *state) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = yt921x_priv_to_device(priv); + int port = pp->index; + u16 bmsr; + u16 lpa; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMSR, &bmsr); + if (res) + goto end; + + res = yt921x_intif_read(priv, port, MII_LPA, &lpa); + +end: + mutex_unlock(&priv->reg_lock); + + if (res) { + dev_err_ratelimited(dev, "Failed to %s PCS port %d: %i\n", + "get state of", port, res); + state->link = false; + return; + } + + if (state->interface == PHY_INTERFACE_MODE_100BASEX) { + state->speed = SPEED_100; + state->duplex = DUPLEX_FULL; + state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX; + state->link = !!(bmsr & BMSR_LSTATUS); + return; + } + + phylink_mii_c22_pcs_decode_state(state, neg_mode, bmsr, lpa); +} + +static void yt921x_phylink_pcs_an_restart(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = yt921x_priv_to_device(priv); + int port = pp->index; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + goto end; + + val |= BMCR_ANRESTART; + res = yt921x_intif_write(priv, port, MII_BMCR, val); + +end: + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s PCS port %d: %i\n", "restart", + port, res); +} + +static void yt921x_phylink_pcs_disable(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + struct device *dev = yt921x_priv_to_device(priv); + int port = pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_intif_modify(priv, port, MII_BMCR, BMCR_PDOWN, BMCR_PDOWN); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s PCS port %d: %i\n", "power down", + port, res); +} + +static int yt921x_phylink_pcs_enable(struct phylink_pcs *pcs) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int port = pp->index; + u16 val; + int res; + + mutex_lock(&priv->reg_lock); + + res = yt921x_intif_read(priv, port, MII_BMCR, &val); + if (res) + goto end; + + val &= ~BMCR_PDOWN; + val |= BMCR_ANRESTART; + res = yt921x_intif_write(priv, port, MII_BMCR, val); + +end: + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct yt921x_port *pp = pcs_to_yt921x_port(pcs); + struct yt921x_priv *priv = yt921x_port_to_priv(pp); + int port = pp->index; + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_serdes_config(priv, port, neg_mode, interface, + advertising, permit_pause_to_mac); + mutex_unlock(&priv->reg_lock); + + return res; +} + +const struct phylink_pcs_ops yt921x_phylink_pcs_ops = { + .pcs_inband_caps = yt921x_phylink_pcs_inband_caps, + .pcs_get_state = yt921x_phylink_pcs_get_state, + .pcs_an_restart = yt921x_phylink_pcs_an_restart, + .pcs_disable = yt921x_phylink_pcs_disable, + .pcs_enable = yt921x_phylink_pcs_enable, + .pcs_config = yt921x_phylink_pcs_config, +}; diff --git a/drivers/net/dsa/motorcomm/pcs.h b/drivers/net/dsa/motorcomm/pcs.h new file mode 100644 index 000000000000..42426558086a --- /dev/null +++ b/drivers/net/dsa/motorcomm/pcs.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_PCS_H +#define _YT_PCS_H + +#include <linux/phylink.h> + +extern const struct phylink_pcs_ops yt921x_phylink_pcs_ops; + +#endif diff --git a/drivers/net/dsa/motorcomm/smi.c b/drivers/net/dsa/motorcomm/smi.c new file mode 100644 index 000000000000..bf3adfd64165 --- /dev/null +++ b/drivers/net/dsa/motorcomm/smi.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include <linux/iopoll.h> + +#include "chip.h" +#include "smi.h" + +#define YT921X_POLL_SLEEP_US 10000 +#define YT921X_POLL_TIMEOUT_US 100000 + +int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp) +{ + lockdep_assert_held_once(&priv->reg_lock); + + return priv->reg_ops->read(priv->reg_ctx, reg, valp); +} + +int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val) +{ + lockdep_assert_held_once(&priv->reg_lock); + + return priv->reg_ops->write(priv->reg_ctx, reg, val); +} + +int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp) +{ + u32 val; + int res; + int ret; + + ret = read_poll_timeout(yt921x_reg_read, res, + res || (val & mask) == *valp, + YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US, + false, priv, reg, &val); + if (ret) + return ret; + if (res) + return res; + + *valp = val; + return 0; +} + +int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val) +{ + int res; + u32 v; + u32 u; + + res = yt921x_reg_read(priv, reg, &v); + if (res) + return res; + + u = v; + u &= ~mask; + u |= val; + if (u == v) + return 0; + + return yt921x_reg_write(priv, reg, u); +} + +static int +yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals, + unsigned int num_regs) +{ + int res; + + for (unsigned int i = 0; i < num_regs; i++) { + res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals, + unsigned int num_regs) +{ + int res; + + for (unsigned int i = 0; i < num_regs; i++) { + res = yt921x_reg_write(priv, reg + 4 * i, vals[i]); + if (res) + return res; + } + + return 0; +} + +static int +yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + const u32 *vals, unsigned int num_regs) +{ + bool changed = false; + u32 vs[4]; + int res; + + BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); + + res = yt921x_regs_read(priv, reg, vs, num_regs); + if (res) + return res; + + for (unsigned int i = 0; i < num_regs; i++) { + u32 u = vs[i]; + + u &= ~masks[i]; + u |= vals[i]; + if (u != vs[i]) + changed = true; + + vs[i] = u; + } + + if (!changed) + return 0; + + return yt921x_regs_write(priv, reg, vs, num_regs); +} + +static int +yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + unsigned int num_regs) +{ + bool changed = false; + u32 vs[4]; + int res; + + BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); + + res = yt921x_regs_read(priv, reg, vs, num_regs); + if (res) + return res; + + for (unsigned int i = 0; i < num_regs; i++) { + u32 u = vs[i]; + + u &= ~masks[i]; + if (u != vs[i]) + changed = true; + + vs[i] = u; + } + + if (!changed) + return 0; + + return yt921x_regs_write(priv, reg, vs, num_regs); +} + +int +yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) +{ + return yt921x_regs_write(priv, reg, vals, 2); +} + +int +yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, + const u32 *vals) +{ + return yt921x_regs_update_bits(priv, reg, masks, vals, 2); +} + +int +yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks) +{ + return yt921x_regs_clear_bits(priv, reg, masks, 2); +} + +int +yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) +{ + return yt921x_regs_write(priv, reg, vals, 3); +} diff --git a/drivers/net/dsa/motorcomm/smi.h b/drivers/net/dsa/motorcomm/smi.h new file mode 100644 index 000000000000..212e20f71d80 --- /dev/null +++ b/drivers/net/dsa/motorcomm/smi.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_SMI_H +#define _YT_SMI_H + +#include <linux/types.h> +#include <linux/wordpart.h> + +struct yt921x_priv; + +int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp); +int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val); +int yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp); +int yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, + u32 val); + +static inline int +yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask) +{ + return yt921x_reg_update_bits(priv, reg, 0, mask); +} + +static inline int +yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask) +{ + return yt921x_reg_update_bits(priv, reg, mask, 0); +} + +static inline int +yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set) +{ + return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask); +} + +/* Some multi-word registers, like VLANn_CTRL, should be treated as a single + * long register. More specifically, writes to parts of its words won't become + * visible, until the last word is written. + * + * Here we require full read and write operations over these registers to + * eliminate potential issues, although partial reads/writes are also possible. + */ + +static inline void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val) +{ + *lo &= ~lower_32_bits(mask); + *hi &= ~upper_32_bits(mask); + *lo |= lower_32_bits(val); + *hi |= upper_32_bits(val); +} + +int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals); +int yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, + const u32 *masks, const u32 *vals); +int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, + const u32 *masks); +int yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals); + +#endif diff --git a/drivers/net/dsa/motorcomm/tc.c b/drivers/net/dsa/motorcomm/tc.c new file mode 100644 index 000000000000..9dbd61e48fee --- /dev/null +++ b/drivers/net/dsa/motorcomm/tc.c @@ -0,0 +1,1711 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2026 David Yang + */ + +#include "chip.h" +#include "smi.h" +#include "tc.h" + +static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port) +{ + struct dsa_port *dp = dsa_to_port(&priv->ds, port); + + return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN; +} + +/* v * 2^e */ +static u64 ldexpu64(u64 v, int e) +{ + return e >= 0 ? v << e : v >> -e; +} + +/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */ +static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C) +{ + int e = 2 * unit + C + YT921X_TOKEN_RATE_C; + + return div_u64(ldexpu64(slot_ns * rate, -e), NSEC_PER_SEC); +} + +static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C) +{ + int e = 2 * unit + C + YT921X_TOKEN_RATE_C; + + return div_u64(ldexpu64(mul_u32_u32(NSEC_PER_SEC, token), e), slot_ns); +} + +/* burst = 2^C * token * 4^unit */ +static u32 burst2token(u64 burst, int unit, int C) +{ + return ldexpu64(burst, -(2 * unit + C)); +} + +static u64 token2burst(u32 token, int unit, int C) +{ + return ldexpu64(token, 2 * unit + C); +} + +struct yt921x_marker { + u32 cir; + u32 cbs; + u32 ebs; + int unit; + bool pkt_mode; +}; + +#define YT921X_MARKER_PKT_MODE BIT(0) +#define YT921X_MARKER_SINGLE_BUCKET BIT(1) + +static int +yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst, + unsigned int flags, unsigned int slot_ns, u32 cir_max, + u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C : + YT921X_TOKEN_BYTE_C; + struct device *dev = yt921x_priv_to_device(priv); + struct yt921x_marker m; + u64 burst_est; + u64 burst_sug; + u64 burst_max; + u64 rate_max; + + m.unit = unit_max; + rate_max = token2rate(cir_max, slot_ns, m.unit, C); + burst_max = token2burst(cbs_max, m.unit, C); + + /* Check for unusual values */ + if (rate > rate_max || burst > burst_max) { + NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate"); + return -ERANGE; + } + + /* Check for matching burst */ + burst_est = div_u64(slot_ns * rate, NSEC_PER_SEC); + burst_sug = burst_est; + if (flags & YT921X_MARKER_PKT_MODE) + burst_sug++; + else + burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) + + ETH_FCS_LEN; + if (burst_sug > burst) + NL_SET_ERR_MSG_FMT_MOD(extack, + "Consider match rate %llu with burst at least %llu", + rate, burst_sug); + + /* Select unit */ + for (; m.unit > 0; m.unit--) { + if (rate > (rate_max >> 2) || burst > (burst_max >> 2)) + break; + rate_max >>= 2; + burst_max >>= 2; + } + + /* Calculate information rate and bucket size */ + m.cir = rate2token(rate, slot_ns, m.unit, C); + if (!m.cir) + m.cir = 1; + else if (WARN_ON(m.cir > cir_max)) + m.cir = cir_max; + m.cbs = burst2token(burst, m.unit, C); + if (!m.cbs) + m.cbs = 1; + else if (WARN_ON(m.cbs > cbs_max)) + m.cbs = cbs_max; + + /* Cut EBS */ + m.ebs = 0; + if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) { + /* We don't have a chance to adjust rate when MTU is changed */ + if (flags & YT921X_MARKER_PKT_MODE) + burst_est++; + else + burst_est += YT921X_FRAME_SIZE_MAX; + + if (burst_est < burst) { + u32 pbs = m.cbs; + + m.cbs = burst2token(burst_est, m.unit, C); + if (!m.cbs) + m.cbs = 1; + else if (WARN_ON(m.cbs > cbs_max)) + m.cbs = cbs_max; + + if (pbs > m.cbs) + m.ebs = pbs - m.cbs; + } + } + + dev_dbg(dev, + "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n", + slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs); + + m.pkt_mode = flags & YT921X_MARKER_PKT_MODE; + *marker = m; + return 0; +} + +static int +yt921x_marker_tfm_police(struct yt921x_marker *marker, + const struct flow_action_police *police, + unsigned int flags, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + bool pkt_mode = !!police->rate_pkt_ps; + u64 burst; + u64 rate; + + rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps; + burst = pkt_mode ? police->burst_pkt : police->burst; + if (pkt_mode) + flags |= YT921X_MARKER_PKT_MODE; + + return yt921x_marker_tfm(marker, rate, burst, flags, + priv->meter_slot_ns, YT921X_METER_CIR_MAX, + YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX, + priv, port, extack); +} + +static int +yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst, + unsigned int flags, struct yt921x_priv *priv, int port, + struct netlink_ext_ack *extack) +{ + return yt921x_marker_tfm(marker, rate, burst, flags, + priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX, + YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX, + priv, port, extack); +} + +static int +yt921x_police_validate(const struct flow_action_police *police, + const struct flow_action *action, + const struct flow_action_entry *act, + struct netlink_ext_ack *extack) +{ + if (police->exceed.act_id != FLOW_ACTION_DROP) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when exceed action is not drop"); + return -EOPNOTSUPP; + } + + if (police->notexceed.act_id != FLOW_ACTION_PIPE && + police->notexceed.act_id != FLOW_ACTION_ACCEPT) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is not pipe or ok"); + return -EOPNOTSUPP; + } + + if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act && + !flow_action_is_last_entry(action, act)) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when conform action is ok, but action is not last"); + return -EOPNOTSUPP; + } + + /* mtu defaults to unlimited but we got 2040 here, don't know why */ + if (police->peakrate_bytes_ps || police->avrate || police->overhead) { + NL_SET_ERR_MSG_MOD(extack, + "Offload not supported when peakrate/avrate/overhead is configured"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int +yt921x_meter_config(struct yt921x_priv *priv, unsigned int id, + const struct yt921x_marker *marker) +{ + u32 ctrls[3]; + + ctrls[0] = 0; + ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir); + ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) | + YT921X_METER_CTRLc_DROP_R | + YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN | + YT921X_METER_CTRLc_METER_EN; + if (marker->pkt_mode) + ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE; + update_ctrls_unaligned(&ctrls[0], &ctrls[1], + YT921X_METER_CTRLab_EBS_M, + YT921X_METER_CTRLab_EBS(marker->ebs)); + update_ctrls_unaligned(&ctrls[1], &ctrls[2], + YT921X_METER_CTRLbc_CBS_M, + YT921X_METER_CTRLbc_CBS(marker->cbs)); + + return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls); +} + +void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on", + port, res); +} + +int +yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port, + const struct flow_action_police *police, + struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_marker marker; + u32 ctrl; + int res; + + res = yt921x_police_validate(police, NULL, NULL, extack); + if (res) + return res; + + res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack); + if (res) + return res; + + mutex_lock(&priv->reg_lock); + res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker); + if (res) + goto end; + + ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN; + res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl); +end: + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port, + const struct tc_tbf_qopt_offload *qopt) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct netlink_ext_ack *extack = qopt->extack; + u32 ctrls[2]; + int res; + + if (qopt->parent != TC_H_ROOT) + return -EOPNOTSUPP; + + switch (qopt->command) { + case TC_TBF_STATS: + /* Unfortunately the convention for TC_*_STATS is a mess, + * neither 0 nor -EOPNOTSUPP is perfect here. + */ + return -EOPNOTSUPP; + case TC_TBF_DESTROY: + ctrls[0] = 0; + ctrls[1] = 0; + break; + case TC_TBF_REPLACE: { + const struct tc_tbf_qopt_offload_replace_params *p; + struct yt921x_marker marker; + + p = &qopt->replace_params; + + res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps, + p->max_size, + YT921X_MARKER_SINGLE_BUCKET, + priv, port, extack); + if (res) + return res; + + ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) | + YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs); + ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) | + YT921X_PORT_SHAPE_CTRLb_EN; + break; + } + default: + return -EOPNOTSUPP; + } + + mutex_lock(&priv->reg_lock); + res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int +yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data) +{ + switch (type) { + case TC_SETUP_QDISC_TBF: { + const struct tc_tbf_qopt_offload *qopt = type_data; + + return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt); + } + default: + return -EOPNOTSUPP; + } +} + +/* ACL: 48 blocks * 8 entries + * + * One rule can span multiple entries, but within a block. + */ + +static void +yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset, + u32 flags, bool set) +{ + if (set) + entry->key[offset] |= flags; + entry->mask[offset] |= flags; +} + +static unsigned int +yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries, + unsigned int size, bool set) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV4_DA: + case YT921X_ACL_TYPE_IPV4_SA: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV4_FRAG, set); + return size; + case YT921X_ACL_TYPE_IPV6_DA3: + case YT921X_ACL_TYPE_IPV6_SA3: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV6_xA3_FRAG, + set); + return size; + case YT921X_ACL_TYPE_MISC: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_MISC_FRAG, set); + return size; + case YT921X_ACL_TYPE_L4: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_L4_FRAG, set); + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set); + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries, + unsigned int size, bool set) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV6_DA2: + case YT921X_ACL_TYPE_IPV6_SA2: + yt921x_acl_entry_set(&entries[i], 1, + YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG, + set); + return size; + case YT921X_ACL_TYPE_MISC: + yt921x_acl_entry_set(&entries[i], 0, + YT921X_ACL_BINa_MISC_FIRST_FRAG, + set); + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + yt921x_acl_entry_set(&entries[size], 0, + YT921X_ACL_BINa_MISC_FIRST_FRAG, set); + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries, + unsigned int size, enum yt921x_l3_type type) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_MAC_DA0: + case YT921X_ACL_TYPE_MAC_SA0: + entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M; + return size; + case YT921X_ACL_TYPE_MISC: + entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type); + entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M; + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type); + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M; + + return size + 1; +} + +static unsigned int +yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries, + unsigned int size, enum yt921x_l4_type type) +{ + for (unsigned int i = 0; i < size; i++) + switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { + case YT921X_ACL_TYPE_IPV4_DA: + case YT921X_ACL_TYPE_IPV4_SA: + entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_IPV6_DA0: + case YT921X_ACL_TYPE_IPV6_DA1: + case YT921X_ACL_TYPE_IPV6_DA2: + case YT921X_ACL_TYPE_IPV6_DA3: + case YT921X_ACL_TYPE_IPV6_SA0: + case YT921X_ACL_TYPE_IPV6_SA1: + case YT921X_ACL_TYPE_IPV6_SA2: + case YT921X_ACL_TYPE_IPV6_SA3: + entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_L4: + entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M; + return size; + case YT921X_ACL_TYPE_MISC: + entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type); + entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M; + return size; + } + + if (size >= YT921X_ACL_ENT_PER_BLK) + return 0; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) | + YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); + entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M; + + return size + 1; +} + +static struct yt921x_acl_entry * +yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep, + u32 type) +{ + unsigned int size = *sizep; + + if (size >= YT921X_ACL_ENT_PER_BLK) + return NULL; + + entries[size] = (typeof(*entries)){}; + entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type); + + (*sizep)++; + return &entries[size]; +} + +static struct yt921x_acl_entry * +yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep, + u32 type) +{ + for (unsigned int i = 0; i < *sizep; i++) + if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) == + type) + return &entries[i]; + return yt921x_acl_entries_new(entries, sizep, type); +} + +static void +yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord, + u16 ports_mask) +{ + struct yt921x_acl_entry *entries = aclrule->entries; + + for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) { + entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) | + YT921X_ACL_KEYb_ORD(ord); + } +} + +struct yt921x_udf_entry { + unsigned char type; + unsigned char offset; + u8 key; + u8 mask; +}; + +struct yt921x_udf_map { + unsigned char type; + unsigned char offset; +}; + +/* UDF windows are preselected - that's enough for now. */ +static struct yt921x_udf_map yt921x_udf_maps[] = { + {YT921X_UDF_TYPE_L3, 7}, + {YT921X_UDF_TYPE_L4, 0}, +}; + +struct yt921x_acl_rule_ext { + struct yt921x_acl_rule r; + + struct yt921x_marker marker; +}; + +static int +yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, + const struct flow_cls_offload *cls) +{ + const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + struct yt921x_acl_entry *entries = ruleext->r.entries; + struct netlink_ext_ack *extack = cls->common.extack; + struct yt921x_udf_entry udfs[YT921X_UDF_NUM]; + const struct flow_dissector *dissector; + struct yt921x_acl_entry *entry; + unsigned int udfs_cnt = 0; + unsigned int size = 0; + __be16 n_proto = 0; + u8 ip_proto = 0; + bool use_dport; + bool use_sport; + + /* Incomplete and probably won't, since it supports custom u32 filters. + * New adapters are welcome. + */ + dissector = rule->match.dissector; + if (dissector->used_keys & + ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | + BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | + BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) | + BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | + BIT_ULL(FLOW_DISSECTOR_KEY_IP) | + BIT_ULL(FLOW_DISSECTOR_KEY_TCP) | + BIT_ULL(FLOW_DISSECTOR_KEY_ICMP))) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used"); + return -EOPNOTSUPP; + } + + /* Entries */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { + struct flow_match_ipv4_addrs match; + + flow_rule_match_ipv4_addrs(rule, &match); + + if (match.mask->dst) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV4_DA); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->dst); + entry->mask[0] |= ntohl(match.mask->dst); + } + + if (match.mask->src) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV4_SA); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->src); + entry->mask[0] |= ntohl(match.mask->src); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { + struct flow_match_ipv6_addrs match; + + flow_rule_match_ipv6_addrs(rule, &match); + + for (unsigned int i = 0; i < 4; i++) { + if (!match.mask->dst.s6_addr32[i]) + continue; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV6_DA0 + i); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]); + entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]); + } + + for (unsigned int i = 0; i < 4; i++) { + if (!match.mask->src.s6_addr32[i]) + continue; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_IPV6_SA0 + i); + if (!entry) + goto err; + + entry->key[0] |= ntohl(match.key->src.s6_addr32[i]); + entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]); + } + } + + use_dport = false; + use_sport = false; + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { + struct flow_match_ports match; + + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_L4); + if (!entry) + goto err; + + flow_rule_match_ports(rule, &match); + + use_dport = !!match.mask->dst; + use_sport = !!match.mask->src; + + entry->key[0] |= (ntohs(match.key->dst) << 16) | + ntohs(match.key->src); + entry->mask[0] |= (ntohs(match.mask->dst) << 16) | + ntohs(match.mask->src); + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) { + struct flow_match_ports_range match; + + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_L4); + if (!entry) + goto err; + + flow_rule_match_ports_range(rule, &match); + + if ((use_dport && match.mask->tp.dst) || + (use_sport && match.mask->tp.src)) { + NL_SET_ERR_MSG_MOD(extack, + "Port mask and range are mutually exclusive"); + return -EINVAL; + } + + if (match.mask->tp.dst) { + entry->key[0] |= ntohs(match.key->tp_min.dst) << 16; + entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN; + entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16; + } + + if (match.mask->tp.src) { + entry->key[0] |= ntohs(match.key->tp_min.src); + entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN; + entry->mask[0] |= ntohs(match.key->tp_max.src); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { + struct flow_match_eth_addrs match; + u32 mask; + + flow_rule_match_eth_addrs(rule, &match); + + mask = ethaddr_hi4_to_u32(match.mask->dst); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_DA0); + if (!entry) + goto err; + + entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst); + entry->mask[0] |= mask; + } + + mask = ethaddr_hi4_to_u32(match.mask->src); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_SA0); + if (!entry) + goto err; + + entry->key[0] |= ethaddr_hi4_to_u32(match.key->src); + entry->mask[0] |= mask; + } + + mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) | + ethaddr_lo2_to_u32(match.mask->src); + if (mask) { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_MAC_DA1_SA1); + if (!entry) + goto err; + + entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) | + ethaddr_lo2_to_u32(match.key->src); + entry->mask[0] |= mask; + } + } + + /* Entries + Misc */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { + struct flow_match_basic match; + + flow_rule_match_basic(rule, &match); + + if (match.mask->n_proto) { + enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER; + + if (match.mask->n_proto == htons(~0)) { + n_proto = match.key->n_proto; + + switch (match.key->n_proto) { + case htons(ETH_P_IP): + l3type = YT921X_L3_TYPE_IPV4; + break; + case htons(ETH_P_IPV6): + l3type = YT921X_L3_TYPE_IPV6; + break; + case htons(ETH_P_ARP): + l3type = YT921X_L3_TYPE_ARP; + break; + case htons(ETH_P_LLDP): + l3type = YT921X_L3_TYPE_LLDP; + break; + case htons(ETH_P_PAE): + l3type = YT921X_L3_TYPE_PAE; + break; + case htons(ETH_P_CFM): + l3type = YT921X_L3_TYPE_ERP; + break; + } + } + + if (l3type != YT921X_L3_TYPE_OTHER) { + size = yt921x_acl_entries_set_l3_type(entries, + size, + l3type); + if (!size) + goto err; + } else { + entry = yt921x_acl_entries_new(entries, &size, + YT921X_ACL_TYPE_ETHERTYPE); + if (!entry) + goto err; + + entry->key[0] |= ntohs(match.key->n_proto); + entry->mask[0] |= ntohs(match.mask->n_proto); + } + } + + if (match.mask->ip_proto) { + enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER; + + if (match.mask->ip_proto == (u8)~0) { + ip_proto = match.key->ip_proto; + + switch (match.key->ip_proto) { + case IPPROTO_TCP: + l4type = YT921X_L4_TYPE_TCP; + break; + case IPPROTO_UDP: + l4type = YT921X_L4_TYPE_UDP; + break; + case IPPROTO_UDPLITE: + l4type = YT921X_L4_TYPE_UDPLITE; + break; + case IPPROTO_ICMP: + l4type = YT921X_L4_TYPE_ICMP; + break; + case IPPROTO_IGMP: + l4type = YT921X_L4_TYPE_IGMP; + break; + } + } + + if (l4type != YT921X_L4_TYPE_OTHER) { + size = yt921x_acl_entries_set_l4_type(entries, + size, + l4type); + if (!size) + goto err; + } else { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto); + entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto); + } + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { + u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG; + struct flow_match_control match; + + flow_rule_match_control(rule, &match); + if (!flow_rule_is_supp_control_flags(supp_flags, + match.mask->flags, extack)) + return -EOPNOTSUPP; + + if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { + bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT; + + size = yt921x_acl_entries_set_is_fragment(entries, size, + set); + if (!size) + goto err; + } + if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { + bool set = match.key->flags & FLOW_DIS_FIRST_FRAG; + + size = yt921x_acl_entries_set_first_frag(entries, size, + set); + if (!size) + goto err; + } + } + + /* Misc only */ + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { + struct flow_match_ip match; + + flow_rule_match_ip(rule, &match); + + if (match.mask->ttl) { + if (n_proto != htons(ETH_P_IP) && + n_proto != htons(ETH_P_IPV6)) { + NL_SET_ERR_MSG_MOD(extack, + "Matching on TTL requires protocol ip or ipv6"); + return -EOPNOTSUPP; + } + + if (udfs_cnt >= YT921X_UDF_NUM) + goto err; + + udfs[udfs_cnt] = (struct yt921x_udf_entry){ + .type = YT921X_UDF_TYPE_L3, + .offset = n_proto == htons(ETH_P_IP) ? 8 : 7, + .key = match.key->ttl, + .mask = match.mask->ttl, + }; + udfs_cnt++; + } + + if (match.mask->tos) { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos); + entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) { + struct flow_match_tcp match; + + flow_rule_match_tcp(rule, &match); + if (match.mask->flags & htons(~0xff)) { + NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags"); + return -EOPNOTSUPP; + } + + if (match.mask->flags) { + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_MISC); + if (!entry) + goto err; + + entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags)); + entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags)); + } + } + + if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ICMP)) { + struct flow_match_icmp match; + + flow_rule_match_icmp(rule, &match); + + if (match.mask->type || match.mask->code) { + if (ip_proto != IPPROTO_ICMP && + ip_proto != IPPROTO_ICMPV6) { + NL_SET_ERR_MSG_MOD(extack, + "Matching on ICMP type/code requires ip_proto icmp or icmpv6"); + return -EOPNOTSUPP; + } + + if (match.mask->type) { + if (udfs_cnt >= YT921X_UDF_NUM) + goto err; + + udfs[udfs_cnt] = (struct yt921x_udf_entry){ + .type = YT921X_UDF_TYPE_L4, + .offset = 0, + .key = match.key->type, + .mask = match.mask->type, + }; + udfs_cnt++; + } + + if (match.mask->code) { + if (udfs_cnt >= YT921X_UDF_NUM) + goto err; + + udfs[udfs_cnt] = (struct yt921x_udf_entry){ + .type = YT921X_UDF_TYPE_L4, + .offset = 1, + .key = match.key->code, + .mask = match.mask->code, + }; + udfs_cnt++; + } + } + } + + /* Compile UDFs */ + for (unsigned int i = 0; i < udfs_cnt; i++) { + unsigned int o; + u32 mask; + u32 key; + + for (o = 0; o < ARRAY_SIZE(yt921x_udf_maps); o++) + if (udfs[i].type == yt921x_udf_maps[o].type && + (udfs[i].offset == yt921x_udf_maps[o].offset || + udfs[i].offset == yt921x_udf_maps[o].offset + 1)) + break; + + if (WARN_ON(o == ARRAY_SIZE(yt921x_udf_maps))) + return -EOPNOTSUPP; + + entry = yt921x_acl_entries_find(entries, &size, + YT921X_ACL_TYPE_UDF0 + o / 2); + if (!entry) + goto err; + + key = udfs[i].key; + mask = udfs[i].mask; + if (udfs[i].offset == yt921x_udf_maps[o].offset) { + key <<= 8; + mask <<= 8; + } + if (!(o % 2)) { + key <<= 16; + mask <<= 16; + } + + entry->key[0] |= key; + entry->mask[0] |= mask; + } + + if (!size) { + NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen"); + return -EOPNOTSUPP; + } + + ruleext->r.mask = (1 << size) - 1; + return 0; + +err: + NL_SET_ERR_MSG_MOD(extack, "Rule too complex"); + return -EOPNOTSUPP; +} + +static int +yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, + const struct flow_cls_offload *cls, + struct yt921x_priv *priv, int port) +{ + const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); + const struct flow_action *flow_action = &rule->action; + struct netlink_ext_ack *extack = cls->common.extack; + enum flow_action_id redir_act = NUM_FLOW_ACTIONS; + const struct flow_action_entry *act; + u32 *action = ruleext->r.action; + bool seen_priority = false; + const char *reason = NULL; + bool seen_police = false; + unsigned int i; + int res; + + memset(action, 0, 3 * sizeof(*action)); + flow_action_for_each(i, act, flow_action) + switch (act->id) { + case FLOW_ACTION_ACCEPT: + case FLOW_ACTION_DROP: + case FLOW_ACTION_REDIRECT: + if (redir_act != NUM_FLOW_ACTIONS && + redir_act != act->id) { + reason = "Different redirect actions"; + goto fallback; + } + redir_act = act->id; + + switch (act->id) { + case FLOW_ACTION_ACCEPT: + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_FWD; + break; + case FLOW_ACTION_DROP: + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_REDIR; + break; + case FLOW_ACTION_REDIRECT: { + struct dsa_port *to_dp; + + to_dp = dsa_port_from_netdev(act->dev); + if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) { + reason = "Redirect to non-local port"; + goto fallback; + } + + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_REDIR | + YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index); + break; + } + default: + break; + } + break; + case FLOW_ACTION_PRIORITY: + if (seen_priority) { + action[0] &= ~YT921X_ACL_ACTa_PRIO_EN; + action[1] &= ~YT921X_ACL_ACTb_PRIO_M; + + reason = "Multiple priority actions"; + goto fallback; + } + seen_priority = true; + + if (act->priority >= YT921X_PRIO_NUM) { + NL_SET_ERR_MSG_MOD(extack, + "Priority value is too high"); + return -EOPNOTSUPP; + } + action[0] |= YT921X_ACL_ACTa_PRIO_EN; + action[1] |= YT921X_ACL_ACTb_PRIO(act->priority); + break; + case FLOW_ACTION_POLICE: { + const struct flow_action_police *police = &act->police; + + if (seen_police) { + action[0] &= ~YT921X_ACL_ACTa_METER_EN; + + reason = "Multiple police actions"; + goto fallback; + } + seen_police = true; + + res = yt921x_police_validate(police, flow_action, act, + extack); + if (res) + return res; + + res = yt921x_marker_tfm_police(&ruleext->marker, police, + 0, priv, port, extack); + if (res) + return res; + + action[0] |= YT921X_ACL_ACTa_METER_EN; + break; + } + default: +fallback: + if (cls->common.skip_sw) { + NL_SET_ERR_MSG_FMT_MOD(extack, + "Action not supported when skip_sw: %s", + reason); + return -EOPNOTSUPP; + } + fallthrough; + case FLOW_ACTION_TRAP: + redir_act = FLOW_ACTION_TRAP; + + action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & + ~YT921X_ACL_ACTc_FWD_M; + action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_TRAP; + break; + } + + ruleext->r.sw_assisted = !cls->common.skip_sw; + return 0; +} + +static int +yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port, + const struct flow_cls_offload *cls, bool ingress, + struct yt921x_priv *priv) +{ + const unsigned int scale = (U16_MAX + 1) / YT921X_ACL_ORD_NUM; + struct netlink_ext_ack *extack = cls->common.extack; + u32 prio = cls->common.prio; + u32 ord; + int res; + + if (!ingress) { + NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); + return -EOPNOTSUPP; + } + + if (cls->common.chain_index) { + NL_SET_ERR_MSG(extack, "Only chain 0 is supported"); + return -EOPNOTSUPP; + } + + /* Lower TC priorities take precedence, while larger orders win in + * hardware. Spread the 512 hardware orders over the TC priority range + * as evenly as possible, retaining the relative order: + * + * prio 65535 -> ORD 0 catchall filter + * prio 65408 -> ORD 1 highest mappable TC priority + * ... + * prio 49152 -> ORD 128 default value for the first tc flower rule + * ... + * prio 128 -> ORD 511 + * prio 0 (invalid TC priority) + * + * The catchall filter always comes with priority 65535; pin it to ORD + * 0, the lowest hardware order, instead of rejecting it. + * + * Prefer explicit rejects over implicit behaviour changes: reject + * the priorities which do not map onto a hardware order instead of + * silently colliding with a neighbouring one. + */ + if (prio == U16_MAX) { + ord = 0; + } else if (!(prio % scale)) { + ord = YT921X_ACL_ORD_NUM - prio / scale; + } else { + NL_SET_ERR_MSG_FMT_MOD(extack, + "Invalid priority %u, must be a multiple of %u or 65535", + prio, scale); + return -EOPNOTSUPP; + } + + res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port); + if (res) + return res; + res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls); + if (res) + return res; + + yt921x_acl_rule_set_ports(&ruleext->r, ord, BIT(port)); + ruleext->r.tag = cls->cookie; + ruleext->r.type = TC_SETUP_CLSFLOWER; + return 0; +} + +/* Ports that _should_ have ACL activated on. ACL_PORT \succeq activated_ports() + */ +static u16 yt921x_acl_activated_ports(const struct yt921x_priv *priv) +{ + u16 mask = 0; + + for (int port = 0; port < YT921X_PORT_NUM; port++) + if (priv->ports[port].acl_cnt) + mask |= BIT(port); + + return mask; +} + +static unsigned int +yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type, + unsigned long tag) +{ + for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) { + const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid]; + + if (!aclblk) + continue; + + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) + if (aclblk->rules[i] && aclblk->rules[i]->tag == tag && + aclblk->rules[i]->type == type) + return YT921X_ACL_ENT_PER_BLK * blkid + i; + } + + return UINT_MAX; +} + +static unsigned int +yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt, + struct netlink_ext_ack *extack) +{ + int candidates[YT921X_ACL_ENT_PER_BLK + 1]; + unsigned int acl_used_cnt = 0; + + if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK)) + return UINT_MAX; + + for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++) + candidates[i] = -1; + for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) { + unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]); + + candidates[blk_used_cnt] = i; + acl_used_cnt += blk_used_cnt; + } + + if (acl_used_cnt >= YT921X_ACL_NUM) { + NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached"); + return UINT_MAX; + } + if (acl_used_cnt + entscnt <= YT921X_ACL_NUM) + for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1; + i-- > 0;) + if (candidates[i] >= 0) + return YT921X_ACL_ENT_PER_BLK * candidates[i] + + ffz(priv->acl_masks[candidates[i]]); + + NL_SET_ERR_MSG_MOD(extack, + "ACL entry allocation failed, simplify your rules or remove existing rules"); + return UINT_MAX; +} + +static int +yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask) +{ + const struct yt921x_acl_rule *aclrule; + const struct yt921x_acl_blk *aclblk; + unsigned int blkid; + unsigned int binid; + unsigned long mask; + u32 zeros[3] = {}; + unsigned int i; + unsigned int o; + u32 ctrl; + int res; + + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + aclrule = aclblk->rules[binid]; + + /* Write actions */ + res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid), + aclrule ? aclrule->action : zeros); + if (res) + return res; + + /* Select the block */ + ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid); + res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); + if (res) + return res; + + /* Write keys and masks */ + ctrl = 0; + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) + ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i); + + mask = entsmask; + i = 0; + for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) { + res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o), + aclrule ? aclrule->entries[i].key : + zeros); + if (res) + return res; + + res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o), + aclrule ? aclrule->entries[i].mask : + zeros); + if (res) + return res; + + ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o); + i++; + } + + res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl); + if (res) + return res; + + ctrl = 0; + for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) { + const struct yt921x_acl_rule *other = aclblk->rules[i]; + + if (!other) + continue; + + mask = other->mask; + for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) + ctrl |= YT921X_ACL_ENTRY_ENm(o) | + YT921X_ACL_ENTRY_GRPIDm(o, i); + } + res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl); + if (res) + return res; + + /* Commit the block */ + ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid); + res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); + if (res) + return res; + + return 0; +} + +static int +yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type, + unsigned long tag) +{ + struct yt921x_acl_rule *aclrule; + struct yt921x_acl_blk *aclblk; + bool refresh_en = false; + unsigned int binid; + unsigned int blkid; + unsigned int entid; + unsigned long m; + unsigned int o; + u32 ctrl; + int res; + int ret; + + entid = yt921x_acl_find(priv, type, tag); + if (entid == UINT_MAX) + return -ENOENT; + + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + aclrule = aclblk->rules[binid]; + + aclblk->rules[binid] = NULL; + ret = yt921x_acl_commit(priv, entid, aclrule->mask); + /* the kernel never rolls back on failure */ + + m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, aclrule->entries[0].key[1]); + for_each_set_bit(o, &m, YT921X_PORT_NUM) { + struct yt921x_port *pp = &priv->ports[o]; + + if (!WARN_ON(!pp->acl_cnt)) { + pp->acl_cnt--; + if (pp->acl_cnt) + continue; + } + refresh_en = true; + } + if (refresh_en) { + ctrl = yt921x_acl_activated_ports(priv); + res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl); + if (res) + ret = res; + } + + if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN) + clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M, + aclrule->action[0]), + priv->meters_map); + priv->acl_masks[blkid] &= ~aclrule->mask; + kvfree(aclrule); + if (!priv->acl_masks[blkid]) { + kvfree(aclblk); + priv->acl_blks[blkid] = NULL; + } + return ret; +} + +static int +yt921x_acl_add(struct yt921x_priv *priv, + const struct yt921x_acl_rule_ext *ruleext, + struct netlink_ext_ack *extack) +{ + unsigned int entscnt = hweight8(ruleext->r.mask); + struct yt921x_acl_rule *aclrule; + struct yt921x_acl_blk *aclblk; + bool use_trap = false; + unsigned int meterid; + unsigned int binid; + unsigned int blkid; + unsigned int entid; + unsigned long m; + unsigned int o; + u32 ctrl; + int res; + + /* Allocate resources */ + entid = yt921x_acl_reserve(priv, entscnt, extack); + if (entid == UINT_MAX) + return -EOPNOTSUPP; + + if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) { + meterid = YT921X_METER_NUM; + } else { + meterid = find_first_zero_bit(priv->meters_map, + YT921X_METER_NUM); + if (meterid < YT921X_METER_NUM) { + res = yt921x_meter_config(priv, meterid, + &ruleext->marker); + if (res) + return res; + } else if (ruleext->r.sw_assisted) { + use_trap = true; + } else { + NL_SET_ERR_MSG_MOD(extack, + "No more meters available"); + return -EOPNOTSUPP; + } + } + + /* Prepare acl block ctrlblk */ + blkid = entid / YT921X_ACL_ENT_PER_BLK; + binid = entid % YT921X_ACL_ENT_PER_BLK; + aclblk = priv->acl_blks[blkid]; + if (!aclblk) { + aclblk = kvzalloc_obj(*aclblk); + if (!aclblk) + return -ENOMEM; + priv->acl_blks[blkid] = aclblk; + } + + /* Prepare acl rule ctrlblk */ + aclrule = kvmemdup(&ruleext->r, + offsetof(struct yt921x_acl_rule, entries[entscnt]), + GFP_KERNEL); + if (!aclrule) { + res = -ENOMEM; + goto err; + } + + /* Replace the placeholder resource IDs */ + aclrule->mask = 0; + m = priv->acl_masks[blkid]; + for_each_clear_bit(o, &m, YT921X_ACL_ENT_PER_BLK) { + aclrule->mask |= BIT(o); + entscnt--; + if (!entscnt) + break; + } + + if (use_trap) { + aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & + ~YT921X_ACL_ACTc_FWD_M; + aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN | + YT921X_ACL_ACTc_FWD_TRAP; + } + if (meterid < YT921X_METER_NUM) + aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid); + else + aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN; + + /* Activate ACL if needed */ + ctrl = 0; + m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]); + for_each_set_bit(o, &m, YT921X_PORT_NUM) + if (!priv->ports[o].acl_cnt) + ctrl |= YT921X_ACL_PORT_PORTn(o); + if (ctrl) { + ctrl |= yt921x_acl_activated_ports(priv); + res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl); + if (res) + goto err; + } + + /* Write rules */ + aclblk->rules[binid] = aclrule; + res = yt921x_acl_commit(priv, entid, aclrule->mask); + if (res) { + aclblk->rules[binid] = NULL; + goto err; + } + + /* Bookkeeping */ + m = FIELD_GET(YT921X_ACL_KEYb_SPORTS_M, ruleext->r.entries[0].key[1]); + for_each_set_bit(o, &m, YT921X_PORT_NUM) + priv->ports[o].acl_cnt++; + + if (meterid < YT921X_METER_NUM) + set_bit(meterid, priv->meters_map); + priv->acl_masks[blkid] |= aclrule->mask; + return 0; + +err: + kvfree(aclrule); + if (!priv->acl_masks[blkid]) { + kvfree(aclblk); + priv->acl_blks[blkid] = NULL; + } + return res; +} + +int +yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int +yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress) +{ + struct netlink_ext_ack *extack = cls->common.extack; + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct yt921x_acl_rule_ext ruleext; + int res; + + res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress, + priv); + if (res) + return res; + + mutex_lock(&priv->reg_lock); + res = yt921x_acl_add(priv, &ruleext, extack); + mutex_unlock(&priv->reg_lock); + + return res; +} + +static int +yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress) +{ + u32 mask; + + if (ingress) + mask = YT921X_MIRROR_IGR_PORTn(port); + else + mask = YT921X_MIRROR_EGR_PORTn(port); + return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask); +} + +static int +yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress, + int to_local_port, struct netlink_ext_ack *extack) +{ + u32 srcs; + u32 ctrl; + u32 val; + u32 dst; + int res; + + if (ingress) + srcs = YT921X_MIRROR_IGR_PORTn(port); + else + srcs = YT921X_MIRROR_EGR_PORTn(port); + dst = YT921X_MIRROR_PORT(to_local_port); + + res = yt921x_reg_read(priv, YT921X_MIRROR, &val); + if (res) + return res; + + /* other mirror tasks & different dst port -> conflict */ + if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M | + YT921X_MIRROR_IGR_PORTS_M)) && + (val & YT921X_MIRROR_PORT_M) != dst) { + NL_SET_ERR_MSG_MOD(extack, + "Sniffer port is already configured, delete existing rules & retry"); + return -EBUSY; + } + + ctrl = val & ~YT921X_MIRROR_PORT_M; + ctrl |= srcs; + ctrl |= dst; + + if (ctrl == val) + return 0; + + return yt921x_reg_write(priv, YT921X_MIRROR, ctrl); +} + +void +yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + struct device *dev = yt921x_priv_to_device(priv); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_mirror_del(priv, port, mirror->ingress); + mutex_unlock(&priv->reg_lock); + + if (res) + dev_err(dev, "Failed to %s port %d: %i\n", "unmirror", + port, res); +} + +int +yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +{ + struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); + int res; + + mutex_lock(&priv->reg_lock); + res = yt921x_mirror_add(priv, port, ingress, + mirror->to_local_port, extack); + mutex_unlock(&priv->reg_lock); + + return res; +} + +int yt921x_chip_setup_tc(struct yt921x_priv *priv) +{ + unsigned int op_ns; + u32 ctrl; + int res; + + op_ns = 8 * priv->cycle_ns; + + ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN); + res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl); + if (res) + return res; + priv->meter_slot_ns = ctrl * op_ns; + + ctrl = max(priv->port_shape_slot_ns / op_ns, + YT921X_PORT_SHAPE_SLOT_MIN); + res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl); + if (res) + return res; + priv->port_shape_slot_ns = ctrl * op_ns; + + return 0; +} + +int yt921x_chip_setup_acl(struct yt921x_priv *priv) +{ + u32 ctrl; + int res; + + ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M; + res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl); + if (res) + return res; + + res = yt921x_reg_write(priv, YT921X_ACL_PORT, 0); + if (res) + return res; + + for (unsigned int o = 0; o < ARRAY_SIZE(yt921x_udf_maps); o++) { + ctrl = YT921X_UDF_CTRL_UDF_OFFSET(yt921x_udf_maps[o].offset) | + YT921X_UDF_CTRL_UDF_TYPE(yt921x_udf_maps[o].type); + res = yt921x_reg_write(priv, YT921X_UDFn_CTRL(o), ctrl); + if (res) + return res; + } + + return 0; +} diff --git a/drivers/net/dsa/motorcomm/tc.h b/drivers/net/dsa/motorcomm/tc.h new file mode 100644 index 000000000000..cc53f148d690 --- /dev/null +++ b/drivers/net/dsa/motorcomm/tc.h @@ -0,0 +1,387 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2026 David Yang + */ + +#ifndef _YT_TC_H +#define _YT_TC_H + +#include <net/dsa.h> +#include <net/pkt_cls.h> + +#define YT921X_ACLn_ACT(n) (0x1c0000 + 0x10 * (n)) +#define YT921X_ACL_ACTc_STAG_M GENMASK(26, 25) +#define YT921X_ACL_ACTc_STAG(x) FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x)) +#define YT921X_ACL_ACTc_STAG_DONTCARE YT921X_ACL_ACTc_STAG(0) +#define YT921X_ACL_ACTc_STAG_UNTAG YT921X_ACL_ACTc_STAG(1) +#define YT921X_ACL_ACTc_STAG_TAG YT921X_ACL_ACTc_STAG(2) +#define YT921X_ACL_ACTc_STAG_KEEP YT921X_ACL_ACTc_STAG(3) +#define YT921X_ACL_ACTc_CTAG_M GENMASK(24, 23) +#define YT921X_ACL_ACTc_CTAG(x) FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x)) +#define YT921X_ACL_ACTc_CTAG_DONTCARE YT921X_ACL_ACTc_CTAG(0) +#define YT921X_ACL_ACTc_CTAG_UNTAG YT921X_ACL_ACTc_CTAG(1) +#define YT921X_ACL_ACTc_CTAG_TAG YT921X_ACL_ACTc_CTAG(2) +#define YT921X_ACL_ACTc_CTAG_KEEP YT921X_ACL_ACTc_CTAG(3) +#define YT921X_ACL_ACTc_FWD_M GENMASK(22, 21) +#define YT921X_ACL_ACTc_FWD(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x)) +#define YT921X_ACL_ACTc_FWD_FWD YT921X_ACL_ACTc_FWD(0) +#define YT921X_ACL_ACTc_FWD_COPY YT921X_ACL_ACTc_FWD(1) +#define YT921X_ACL_ACTc_FWD_REDIR YT921X_ACL_ACTc_FWD(2) +#define YT921X_ACL_ACTc_FWD_TRAP YT921X_ACL_ACTc_FWD(3) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M GENMASK(20, 10) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x)) +#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port) BIT((port) + 10) +#define YT921X_ACL_ACTc_FWD_EN BIT(9) +#define YT921X_ACL_ACTc_SDEI BIT(8) +#define YT921X_ACL_ACTc_SDEI_REPLACE BIT(7) +#define YT921X_ACL_ACTc_SPRI_M GENMASK(6, 4) +#define YT921X_ACL_ACTc_SPRI(x) FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x)) +#define YT921X_ACL_ACTc_SPRI_REPLACE BIT(3) +#define YT921X_ACL_ACTbc_SVID_M GENMASK_ULL(34, 23) +#define YT921X_ACL_ACTbc_SVID(x) FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x)) +#define YT921X_ACL_ACTb_SVID_REPLACE BIT(22) +#define YT921X_ACL_ACTb_CDEI BIT(21) +#define YT921X_ACL_ACTb_CDEI_REPLACE BIT(20) +#define YT921X_ACL_ACTb_CPRI_M GENMASK(19, 17) +#define YT921X_ACL_ACTb_CPRI(x) FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x)) +#define YT921X_ACL_ACTb_CPRI_REPLACE BIT(16) +#define YT921X_ACL_ACTb_CVID_M GENMASK(15, 4) +#define YT921X_ACL_ACTb_CVID(x) FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x)) +#define YT921X_ACL_ACTb_CVID_REPLACE BIT(3) +#define YT921X_ACL_ACTb_PRIO_M GENMASK(2, 0) +#define YT921X_ACL_ACTb_PRIO(x) FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x)) +#define YT921X_ACL_ACTa_PRIO_EN BIT(31) +#define YT921X_ACL_ACTa_COLOR_M GENMASK(30, 29) +#define YT921X_ACL_ACTa_COLOR(x) FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x)) +#define YT921X_ACL_ACTa_COLOR_GREEN YT921X_ACL_ACTa_COLOR(0) +#define YT921X_ACL_ACTa_COLOR_YELLOW YT921X_ACL_ACTa_COLOR(1) +#define YT921X_ACL_ACTa_COLOR_RED YT921X_ACL_ACTa_COLOR(2) +#define YT921X_ACL_ACTa_COLOR_EN BIT(28) +#define YT921X_ACL_ACTa_DSCP_M GENMASK(27, 22) +#define YT921X_ACL_ACTa_DSCP(x) FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x)) +#define YT921X_ACL_ACTa_DSCP_REPLACE BIT(21) +#define YT921X_ACL_ACTa_METER_ID_M GENMASK(20, 15) +#define YT921X_ACL_ACTa_METER_ID(x) FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x)) +#define YT921X_ACL_ACTa_METER_EN BIT(14) +#define YT921X_ACL_ACTa_MIRROR_EN BIT(13) +#define YT921X_ACL_ACTa_FLOWSTAT_EN BIT(12) +#define YT921X_ACL_ACTa_FLOWSTAT_ID_M GENMASK(11, 6) +#define YT921X_ACL_ACTa_FLOWSTAT_ID(x) FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x)) +#define YT921X_ACL_ACTa_GPIO_EN BIT(5) +#define YT921X_ACL_ACTa_GPIO_PIN_M GENMASK(4, 1) +#define YT921X_ACL_ACTa_GPIO_PIN(x) FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x)) +#define YT921X_ACL_ACTa_INTR_EN BIT(0) +#define YT921X_ACL_BLK_KEEP 0x201000 +#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin) (7 << (4 * (bin) + 1)) +#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x) ((x) << (4 * (bin) + 1)) +#define YT921X_ACL_BLK_KEEP_KEEPn(bin) BIT(4 * (bin)) +#define YT921X_ACL_PORT 0x202000 +#define YT921X_ACL_PORT_PORTS_M GENMASK(10, 0) +#define YT921X_ACL_PORT_PORTS(x) FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x)) +#define YT921X_ACL_PORT_PORTn(port) BIT(port) +#define YT921X_ACL_BLK_CMD 0x202004 +#define YT921X_ACL_BLK_CMD_BLKID_M GENMASK(6, 1) +#define YT921X_ACL_BLK_CMD_BLKID(x) FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x)) +#define YT921X_ACL_BLK_CMD_MODIFY BIT(0) +#define YT921X_ACLn_ENTRY(blk) (0x203000 + 4 * (blk)) +#define YT921X_ACL_ENTRY_GRPIDm_M(bin) (7 << (4 * (bin) + 1)) +#define YT921X_ACL_ENTRY_GRPIDm(bin, x) ((x) << (4 * (bin) + 1)) +#define YT921X_ACL_ENTRY_ENm(bin) BIT(4 * (bin)) +#define YT921X_ACLn_KEYm(blk, bin) (0x204000 + 0x200 * (bin) + 8 * (blk)) +#define YT921X_ACL_KEYb_ORD_M GENMASK(29, 21) +#define YT921X_ACL_KEYb_ORD(x) FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x)) +#define YT921X_ACL_KEYb_SPORTS_M GENMASK(20, 10) +#define YT921X_ACL_KEYb_SPORTS(x) FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x)) +#define YT921X_ACL_KEYb_SPORTn(port) BIT((port) + 10) +#define YT921X_ACL_KEYb_REVERSE BIT(9) /* reverse match */ +#define YT921X_ACL_KEYb_TYPE_M GENMASK(8, 4) +#define YT921X_ACL_KEYb_TYPE(x) FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x)) +/* KEY_* fields need no masks */ +#define YT921X_ACLn_MASKm(blk, bin) (0x205000 + 0x200 * (bin) + 8 * (blk)) + +enum yt921x_acl_type { + YT921X_ACL_TYPE_NA, + YT921X_ACL_TYPE_MAC_DA0, + YT921X_ACL_TYPE_MAC_SA0, + YT921X_ACL_TYPE_MAC_DA1_SA1, + YT921X_ACL_TYPE_VLAN, + YT921X_ACL_TYPE_VTAG, + YT921X_ACL_TYPE_IPV4_DA, + YT921X_ACL_TYPE_IPV4_SA, + YT921X_ACL_TYPE_IPV6_DA0, + YT921X_ACL_TYPE_IPV6_DA1, + YT921X_ACL_TYPE_IPV6_DA2, + YT921X_ACL_TYPE_IPV6_DA3, + YT921X_ACL_TYPE_IPV6_SA0, + YT921X_ACL_TYPE_IPV6_SA1, + YT921X_ACL_TYPE_IPV6_SA2, + YT921X_ACL_TYPE_IPV6_SA3, + YT921X_ACL_TYPE_MISC, + YT921X_ACL_TYPE_L4, + /* Each User Defined Filter entry contains 2 UDF windows (UDF_CTRL 2i + * and 2i+1) + */ + YT921X_ACL_TYPE_UDF0, + YT921X_ACL_TYPE_UDF1, + YT921X_ACL_TYPE_UDF2, + YT921X_ACL_TYPE_UDF3, + YT921X_ACL_TYPE_UDF4, + YT921X_ACL_TYPE_UDF5, + YT921X_ACL_TYPE_UDF6, + YT921X_ACL_TYPE_UDF7, + YT921X_ACL_TYPE_ETHERTYPE, + YT921X_ACL_TYPE_NUM +}; + +/* Range: turn KEY:MASK into MIN:MAX */ + +#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M GENMASK(3, 0) +#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x)) +#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x)) +#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M GENMASK(31, 16) +#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M GENMASK(15, 0) + +#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_VLAN_CDEI BIT(3) +#define YT921X_ACL_BINb_VLAN_CPRI_M GENMASK(2, 0) +#define YT921X_ACL_BINb_VLAN_CPRI(x) FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x)) +#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M GENMASK(31, 30) +#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x)) +#define YT921X_ACL_BINa_VLAN_SDEI BIT(29) +#define YT921X_ACL_BINa_VLAN_SPRI_M GENMASK(28, 26) +#define YT921X_ACL_BINa_VLAN_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x)) +#define YT921X_ACL_BINa_VLAN_STAG_FMT_M GENMASK(25, 24) +#define YT921X_ACL_BINa_VLAN_STAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x)) +#define YT921X_ACL_BINa_VLAN_SVID_M GENMASK(23, 12) +#define YT921X_ACL_BINa_VLAN_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x)) +#define YT921X_ACL_BINa_VLAN_CVID_M GENMASK(11, 0) +#define YT921X_ACL_BINa_VLAN_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x)) + +#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN BIT(30) +#define YT921X_ACL_BINa_VTAG_CDEI BIT(31) +#define YT921X_ACL_BINa_VTAG_CPRI_M GENMASK(30, 28) +#define YT921X_ACL_BINa_VTAG_CPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x)) +#define YT921X_ACL_BINa_VTAG_SDEI BIT(27) +#define YT921X_ACL_BINa_VTAG_SPRI_M GENMASK(26, 24) +#define YT921X_ACL_BINa_VTAG_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x)) +#define YT921X_ACL_BINa_VTAG_SVID_M GENMASK(23, 12) +#define YT921X_ACL_BINa_VTAG_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x)) +#define YT921X_ACL_BINa_VTAG_CVID_M GENMASK(11, 0) +#define YT921X_ACL_BINa_VTAG_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x)) + +#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_IPV4_FRAG BIT(3) +#define YT921X_ACL_BINb_IPV4_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_IPV4_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_IPV4_ADDR_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_IPV6_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_IPV6_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_IPV6_ADDRx_M GENMASK(31, 0) + +#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION BIT(3) + +#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG BIT(3) + +#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_IPV6_xA3_FRAG BIT(3) + +#define YT921X_ACL_BINb_MISC_FRAG BIT(3) +#define YT921X_ACL_BINb_MISC_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_MISC_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_MISC_PPPOE_FLAG BIT(30) +#define YT921X_ACL_BINa_MISC_FIRST_FRAG BIT(29) +#define YT921X_ACL_BINa_MISC_IP_OPTION BIT(28) +#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M GENMASK(27, 20) +#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x)) +#define YT921X_ACL_BINa_MISC_IP_PROTO_M GENMASK(19, 12) +#define YT921X_ACL_BINa_MISC_IP_PROTO(x) FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x)) +#define YT921X_ACL_BINa_MISC_TOS_M GENMASK(11, 4) +#define YT921X_ACL_BINa_MISC_TOS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x)) +#define YT921X_ACL_BINa_MISC_L3_TYPE_M GENMASK(3, 0) +#define YT921X_ACL_BINa_MISC_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x)) + +#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN BIT(31) +#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_L4_FRAG BIT(3) +#define YT921X_ACL_BINb_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_L4_DPORT_M GENMASK(31, 16) +#define YT921X_ACL_BINa_L4_SPORT_M GENMASK(15, 0) + +#define YT921X_ACL_BINb_UDF_IS_IGMP BIT(0) +#define YT921X_ACL_BINa_UDF_UDF0_M GENMASK(31, 16) +#define YT921X_ACL_BINa_UDF_UDF0(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x)) +#define YT921X_ACL_BINa_UDF_UDF1_M GENMASK(15, 0) +#define YT921X_ACL_BINa_UDF_UDF1(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x)) + +#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN BIT(30) +#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M GENMASK(2, 0) +#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x)) +#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M GENMASK(15, 0) +#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x) FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x)) + +enum yt921x_l2_type { + YT921X_L2_TYPE_ETH, + YT921X_L2_TYPE_ETHV2, + YT921X_L2_TYPE_ETHSAP, + YT921X_L2_TYPE_ETHSNAP, +}; + +enum yt921x_l3_type { + YT921X_L3_TYPE_OTHER, + YT921X_L3_TYPE_IPV4, + YT921X_L3_TYPE_IPV6, + YT921X_L3_TYPE_ARP, + YT921X_L3_TYPE_LLDP, + YT921X_L3_TYPE_PAE, + YT921X_L3_TYPE_ERP, + YT921X_L3_TYPE_SLOW_PROTOCOL, +}; + +enum yt921x_l4_type { + YT921X_L4_TYPE_OTHER, + YT921X_L4_TYPE_TCP, + YT921X_L4_TYPE_UDP, + YT921X_L4_TYPE_UDPLITE, + YT921X_L4_TYPE_ICMP, + YT921X_L4_TYPE_IGMP, + YT921X_L4_TYPE_MLD, + YT921X_L4_TYPE_ND, +}; + +#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x)) +#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7) +#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x)) +#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0) +#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x)) + +enum yt921x_udf_type { + YT921X_UDF_TYPE_L2, + YT921X_UDF_TYPE_L3, + YT921X_UDF_TYPE_L4, +}; + +#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port)) +#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */ +#define YT921X_METER_SLOT 0x220104 +#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0) +#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port)) +#define YT921X_PORT_METER_EN BIT(4) +#define YT921X_PORT_METER_ID_M GENMASK(3, 0) +#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x)) +#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x)) +#define YT921X_METER_CTRLc_METER_EN BIT(14) +#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */ +#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11) +#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x)) +#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0) +#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1) +#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2) +#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3) +#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10) +#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7) +#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x)) +#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */ +#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */ +#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */ +#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20) +#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x)) +#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2) +#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x)) +#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18) +#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x)) +#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0) +#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x)) +#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x)) + +#define YT921X_MIRROR 0x300300 +#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16) +#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x)) +#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16) +#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4) +#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x)) +#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4) +#define YT921X_MIRROR_PORT_M GENMASK(3, 0) +#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x)) + +#define YT921X_PORT_SHAPE_SLOT 0x34000c +#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0) +#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port)) +#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4) +#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */ +#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0) +#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x)) +#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18) +#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x)) +#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0) +#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x)) +#define YT921X_PORTn_SHAPE_STAT(port) (0x356000 + 4 * (port)) + +#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */ +#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */ +#define YT921X_TOKEN_RATE_C -15 +#define YT921X_METER_SLOT_MIN 80 +#define YT921X_METER_UNIT_MAX ((1 << 3) - 1) +#define YT921X_METER_CIR_MAX ((1 << 18) - 1) +#define YT921X_METER_CBS_MAX ((1 << 16) - 1) +#define YT921X_PORT_SHAPE_SLOT_MIN 80 +#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1) +#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1) +#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1) + +#define YT921X_ACL_ENT_PER_BLK 8 +#define YT921X_ACL_NUM 384 +#define YT921X_ACL_ORD_NUM 512 + +struct yt921x_acl_entry { + u32 key[2]; + u32 mask[2]; +}; + +struct yt921x_acl_rule { + unsigned long tag; + enum tc_setup_type type; + + u32 action[3]; + bool sw_assisted; + + u8 mask; + struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK]; +}; + +struct yt921x_acl_blk { + struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK]; +}; + +void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port); +int +yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port, + const struct flow_action_police *police, + struct netlink_ext_ack *extack); +int +yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port, + enum tc_setup_type type, void *type_data); +int +yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress); +int +yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port, + struct flow_cls_offload *cls, bool ingress); +void +yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror); +int +yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack); + +int yt921x_chip_setup_tc(struct yt921x_priv *priv); +int yt921x_chip_setup_acl(struct yt921x_priv *priv); + +#endif diff --git a/drivers/net/dsa/mt7530-mdio.c b/drivers/net/dsa/mt7530-mdio.c index de42f70afcfa..0be107dc6759 100644 --- a/drivers/net/dsa/mt7530-mdio.c +++ b/drivers/net/dsa/mt7530-mdio.c @@ -48,6 +48,9 @@ mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) u16 page, r, lo, hi; int ret; + /* Callers do not check for errors, keep the value deterministic */ + *val = 0; + page = (reg >> 6) & 0x3ff; r = (reg >> 2) & 0xf; @@ -139,7 +142,7 @@ 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 */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt7530_of_match); @@ -148,12 +151,14 @@ static const struct regmap_config regmap_config = { .val_bits = 32, .reg_stride = 4, .max_register = MT7530_CREV, - .disable_locking = true, + .lock = mt7530_mdio_regmap_lock, + .unlock = mt7530_mdio_regmap_unlock, }; static int mt7530_probe(struct mdio_device *mdiodev) { + struct regmap_config rc = regmap_config; struct mt7530_priv *priv; struct device_node *dn; int ret; @@ -207,8 +212,9 @@ mt7530_probe(struct mdio_device *mdiodev) return PTR_ERR(priv->io_pwr); } - priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv, - ®map_config); + rc.lock_arg = &priv->bus->mdio_lock; + priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, + priv, &rc); if (IS_ERR(priv->regmap)) return PTR_ERR(priv->regmap); diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c index fd68b1cd0630..cf3bdb23a2d5 100644 --- a/drivers/net/dsa/mt7530-mmio.c +++ b/drivers/net/dsa/mt7530-mmio.c @@ -14,7 +14,7 @@ static const struct of_device_id mt7988_of_match[] = { { .compatible = "airoha,en7581-switch", .data = &mt753x_table[ID_EN7581], }, { .compatible = "econet,en7528-switch", .data = &mt753x_table[ID_EN7528], }, { .compatible = "mediatek,mt7988-switch", .data = &mt753x_table[ID_MT7988], }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mt7988_of_match); diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 96832852c65a..7781a63b4e6f 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -155,97 +155,6 @@ core_clear(struct mt7530_priv *priv, u32 reg, u32 val) } static int -mt7530_mii_write(struct mt7530_priv *priv, u32 reg, u32 val) -{ - int ret; - - ret = regmap_write(priv->regmap, reg, val); - - if (ret < 0) - dev_err(priv->dev, - "failed to write mt7530 register\n"); - - return ret; -} - -static u32 -mt7530_mii_read(struct mt7530_priv *priv, u32 reg) -{ - int ret; - u32 val; - - ret = regmap_read(priv->regmap, reg, &val); - if (ret) { - WARN_ON_ONCE(1); - dev_err(priv->dev, - "failed to read mt7530 register\n"); - return 0; - } - - return val; -} - -static int -mt7530_write(struct mt7530_priv *priv, u32 reg, u32 val) -{ - int ret; - - mt7530_mutex_lock(priv); - - ret = mt7530_mii_write(priv, reg, val); - - mt7530_mutex_unlock(priv); - - return ret; -} - -static u32 -_mt7530_read(struct mt7530_dummy_poll *p) -{ - u32 val; - - mt7530_mutex_lock(p->priv); - - val = mt7530_mii_read(p->priv, p->reg); - - mt7530_mutex_unlock(p->priv); - - return val; -} - -static u32 -mt7530_read(struct mt7530_priv *priv, u32 reg) -{ - struct mt7530_dummy_poll p; - - INIT_MT7530_DUMMY_POLL(&p, priv, reg); - return _mt7530_read(&p); -} - -static void -mt7530_rmw(struct mt7530_priv *priv, u32 reg, - u32 mask, u32 set) -{ - mt7530_mutex_lock(priv); - - regmap_update_bits(priv->regmap, reg, mask, set); - - mt7530_mutex_unlock(priv); -} - -static void -mt7530_set(struct mt7530_priv *priv, u32 reg, u32 val) -{ - mt7530_rmw(priv, reg, val, val); -} - -static void -mt7530_clear(struct mt7530_priv *priv, u32 reg, u32 val) -{ - mt7530_rmw(priv, reg, val, 0); -} - -static int mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) { u32 val; @@ -253,19 +162,15 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) /* Set the command operating upon the MAC address entries */ val = ATC_BUSY | ATC_MAT(0) | cmd; - ret = mt7530_write(priv, MT7530_ATC, val); + ret = regmap_write(priv->regmap, MT7530_ATC, val); if (ret) return ret; - mt7530_mutex_lock(priv); - ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val, !(val & ATC_BUSY), 20, 20000); if (!ret) ret = regmap_read(priv->regmap, MT7530_ATC, &val); - mt7530_mutex_unlock(priv); - if (ret < 0) { dev_err(priv->dev, "reset timeout\n"); return ret; @@ -283,6 +188,26 @@ mt7530_fdb_cmd(struct mt7530_priv *priv, enum mt7530_fdb_cmd cmd, u32 *rsp) return 0; } +static void mt7530_port_fast_age(struct dsa_switch *ds, int port) +{ + struct mt7530_priv *priv = ds->priv; + u32 val; + int ret; + + mutex_lock(&priv->reg_mutex); + + /* Flush all non-static MAC address entries */ + val = ATC_BUSY | ATC_MAT_NON_STATIC_MAC | MT7530_FDB_FLUSH; + ret = regmap_write(priv->regmap, MT7530_ATC, val); + if (!ret) + ret = regmap_read_poll_timeout(priv->regmap, MT7530_ATC, val, + !(val & ATC_BUSY), 20, 20000); + if (ret < 0) + dev_err(priv->dev, "fast age failed: %d\n", ret); + + mutex_unlock(&priv->reg_mutex); +} + static void mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb) { @@ -291,22 +216,23 @@ mt7530_fdb_read(struct mt7530_priv *priv, struct mt7530_fdb *fdb) /* Read from ARL table into an array */ for (i = 0; i < 3; i++) { - reg[i] = mt7530_read(priv, MT7530_TSRA1 + (i * 4)); + regmap_read(priv->regmap, MT7530_TSRA1 + (i * 4), + ®[i]); dev_dbg(priv->dev, "%s(%d) reg[%d]=0x%x\n", __func__, __LINE__, i, reg[i]); } - fdb->vid = (reg[1] >> CVID) & CVID_MASK; - fdb->aging = (reg[2] >> AGE_TIMER) & AGE_TIMER_MASK; - fdb->port_mask = (reg[2] >> PORT_MAP) & PORT_MAP_MASK; - fdb->mac[0] = (reg[0] >> MAC_BYTE_0) & MAC_BYTE_MASK; - fdb->mac[1] = (reg[0] >> MAC_BYTE_1) & MAC_BYTE_MASK; - fdb->mac[2] = (reg[0] >> MAC_BYTE_2) & MAC_BYTE_MASK; - fdb->mac[3] = (reg[0] >> MAC_BYTE_3) & MAC_BYTE_MASK; - fdb->mac[4] = (reg[1] >> MAC_BYTE_4) & MAC_BYTE_MASK; - fdb->mac[5] = (reg[1] >> MAC_BYTE_5) & MAC_BYTE_MASK; - fdb->noarp = ((reg[2] >> ENT_STATUS) & ENT_STATUS_MASK) == STATIC_ENT; + fdb->vid = FIELD_GET(CVID_MASK, reg[1]); + fdb->aging = FIELD_GET(AGE_TIMER_MASK, reg[2]); + fdb->port_mask = FIELD_GET(PORT_MAP_MASK, reg[2]); + fdb->mac[0] = FIELD_GET(MAC_BYTE_0_MASK, reg[0]); + fdb->mac[1] = FIELD_GET(MAC_BYTE_1_MASK, reg[0]); + fdb->mac[2] = FIELD_GET(MAC_BYTE_2_MASK, reg[0]); + fdb->mac[3] = FIELD_GET(MAC_BYTE_3_MASK, reg[0]); + fdb->mac[4] = FIELD_GET(MAC_BYTE_4_MASK, reg[1]); + fdb->mac[5] = FIELD_GET(MAC_BYTE_5_MASK, reg[1]); + fdb->noarp = FIELD_GET(ENT_STATUS_MASK, reg[2]) == STATIC_ENT; } static void @@ -317,26 +243,26 @@ mt7530_fdb_write(struct mt7530_priv *priv, u16 vid, u32 reg[3] = { 0 }; int i; - reg[1] |= vid & CVID_MASK; + reg[1] |= FIELD_PREP(CVID_MASK, vid); reg[1] |= ATA2_IVL; reg[1] |= ATA2_FID(FID_BRIDGED); - reg[2] |= (aging & AGE_TIMER_MASK) << AGE_TIMER; - reg[2] |= (port_mask & PORT_MAP_MASK) << PORT_MAP; + reg[2] |= FIELD_PREP(AGE_TIMER_MASK, aging); + reg[2] |= FIELD_PREP(PORT_MAP_MASK, port_mask); /* STATIC_ENT indicate that entry is static wouldn't * be aged out and STATIC_EMP specified as erasing an * entry */ - reg[2] |= (type & ENT_STATUS_MASK) << ENT_STATUS; - reg[1] |= mac[5] << MAC_BYTE_5; - reg[1] |= mac[4] << MAC_BYTE_4; - reg[0] |= mac[3] << MAC_BYTE_3; - reg[0] |= mac[2] << MAC_BYTE_2; - reg[0] |= mac[1] << MAC_BYTE_1; - reg[0] |= mac[0] << MAC_BYTE_0; + reg[2] |= FIELD_PREP(ENT_STATUS_MASK, type); + reg[1] |= FIELD_PREP(MAC_BYTE_5_MASK, mac[5]); + reg[1] |= FIELD_PREP(MAC_BYTE_4_MASK, mac[4]); + reg[0] |= FIELD_PREP(MAC_BYTE_3_MASK, mac[3]); + reg[0] |= FIELD_PREP(MAC_BYTE_2_MASK, mac[2]); + reg[0] |= FIELD_PREP(MAC_BYTE_1_MASK, mac[1]); + reg[0] |= FIELD_PREP(MAC_BYTE_0_MASK, mac[0]); /* Write array into the ARL table */ for (i = 0; i < 3; i++) - mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]); + regmap_write(priv->regmap, MT7530_ATA1 + (i * 4), reg[i]); } /* Set up switch core clock for MT7530 */ @@ -390,14 +316,16 @@ mt7530_setup_port6(struct dsa_switch *ds, phy_interface_t interface) core_clear(priv, CORE_TRGMII_GSW_CLK_CG, REG_TRGMIICK_EN); if (interface == PHY_INTERFACE_MODE_RGMII) { - mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, - P6_INTF_MODE(0)); + regmap_update_bits(priv->regmap, MT7530_P6ECR, + P6_INTF_MODE_MASK, P6_INTF_MODE(0)); return; } - mt7530_rmw(priv, MT7530_P6ECR, P6_INTF_MODE_MASK, P6_INTF_MODE(1)); + regmap_update_bits(priv->regmap, MT7530_P6ECR, P6_INTF_MODE_MASK, + P6_INTF_MODE(1)); - xtal = mt7530_read(priv, MT753X_MTRAP) & MT7530_XTAL_MASK; + regmap_read(priv->regmap, MT753X_MTRAP, &xtal); + xtal &= MT7530_XTAL_MASK; if (xtal == MT7530_XTAL_25MHZ) ssc_delta = 0x57; @@ -441,9 +369,9 @@ mt7531_pll_setup(struct mt7530_priv *priv) u32 hwstrap; u32 val; - val = mt7530_read(priv, MT7531_CREV); - top_sig = mt7530_read(priv, MT7531_TOP_SIG_SR); - hwstrap = mt7530_read(priv, MT753X_TRAP); + regmap_read(priv->regmap, MT7531_CREV, &val); + regmap_read(priv->regmap, MT7531_TOP_SIG_SR, &top_sig); + regmap_read(priv->regmap, MT753X_TRAP, &hwstrap); if ((val & CHIP_REV_M) > 0) xtal = (top_sig & PAD_MCM_SMI_EN) ? MT7531_XTAL_FSEL_40MHZ : MT7531_XTAL_FSEL_25MHZ; @@ -452,72 +380,72 @@ mt7531_pll_setup(struct mt7530_priv *priv) MT7531_XTAL_FSEL_40MHZ; /* Step 1 : Disable MT7531 COREPLL */ - val = mt7530_read(priv, MT7531_PLLGP_EN); + regmap_read(priv->regmap, MT7531_PLLGP_EN, &val); val &= ~EN_COREPLL; - mt7530_write(priv, MT7531_PLLGP_EN, val); + regmap_write(priv->regmap, MT7531_PLLGP_EN, val); /* Step 2: switch to XTAL output */ - val = mt7530_read(priv, MT7531_PLLGP_EN); + regmap_read(priv->regmap, MT7531_PLLGP_EN, &val); val |= SW_CLKSW; - mt7530_write(priv, MT7531_PLLGP_EN, val); + regmap_write(priv->regmap, MT7531_PLLGP_EN, val); - val = mt7530_read(priv, MT7531_PLLGP_CR0); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); val &= ~RG_COREPLL_EN; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); /* Step 3: disable PLLGP and enable program PLLGP */ - val = mt7530_read(priv, MT7531_PLLGP_EN); + regmap_read(priv->regmap, MT7531_PLLGP_EN, &val); val |= SW_PLLGP; - mt7530_write(priv, MT7531_PLLGP_EN, val); + regmap_write(priv->regmap, MT7531_PLLGP_EN, val); /* Step 4: program COREPLL output frequency to 500MHz */ - val = mt7530_read(priv, MT7531_PLLGP_CR0); - val &= ~RG_COREPLL_POSDIV_M; - val |= 2 << RG_COREPLL_POSDIV_S; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); + val &= ~RG_COREPLL_POSDIV_MASK; + val |= RG_COREPLL_POSDIV(2); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); usleep_range(25, 35); switch (xtal) { case MT7531_XTAL_FSEL_25MHZ: - val = mt7530_read(priv, MT7531_PLLGP_CR0); - val &= ~RG_COREPLL_SDM_PCW_M; - val |= 0x140000 << RG_COREPLL_SDM_PCW_S; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); + val &= ~RG_COREPLL_SDM_PCW_MASK; + val |= RG_COREPLL_SDM_PCW(0x140000); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); break; case MT7531_XTAL_FSEL_40MHZ: - val = mt7530_read(priv, MT7531_PLLGP_CR0); - val &= ~RG_COREPLL_SDM_PCW_M; - val |= 0x190000 << RG_COREPLL_SDM_PCW_S; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); + val &= ~RG_COREPLL_SDM_PCW_MASK; + val |= RG_COREPLL_SDM_PCW(0x190000); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); break; } /* Set feedback divide ratio update signal to high */ - val = mt7530_read(priv, MT7531_PLLGP_CR0); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); val |= RG_COREPLL_SDM_PCW_CHG; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); /* Wait for at least 16 XTAL clocks */ usleep_range(10, 20); /* Step 5: set feedback divide ratio update signal to low */ - val = mt7530_read(priv, MT7531_PLLGP_CR0); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); val &= ~RG_COREPLL_SDM_PCW_CHG; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); /* Enable 325M clock for SGMII */ - mt7530_write(priv, MT7531_ANA_PLLGP_CR5, 0xad0000); + regmap_write(priv->regmap, MT7531_ANA_PLLGP_CR5, 0xad0000); /* Enable 250SSC clock for RGMII */ - mt7530_write(priv, MT7531_ANA_PLLGP_CR2, 0x4f40000); + regmap_write(priv->regmap, MT7531_ANA_PLLGP_CR2, 0x4f40000); /* Step 6: Enable MT7531 PLL */ - val = mt7530_read(priv, MT7531_PLLGP_CR0); + regmap_read(priv->regmap, MT7531_PLLGP_CR0, &val); val |= RG_COREPLL_EN; - mt7530_write(priv, MT7531_PLLGP_CR0, val); + regmap_write(priv->regmap, MT7531_PLLGP_CR0, val); - val = mt7530_read(priv, MT7531_PLLGP_EN); + regmap_read(priv->regmap, MT7531_PLLGP_EN, &val); val |= EN_COREPLL; - mt7530_write(priv, MT7531_PLLGP_EN, val); + regmap_write(priv->regmap, MT7531_PLLGP_EN, val); usleep_range(25, 35); } @@ -526,8 +454,8 @@ mt7530_mib_reset(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; - mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_FLUSH); - mt7530_write(priv, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); + regmap_write(priv->regmap, MT7530_MIB_CCR, CCR_MIB_FLUSH); + regmap_write(priv->regmap, MT7530_MIB_CCR, CCR_MIB_ACTIVATE); } static int mt7530_phy_read_c22(struct mt7530_priv *priv, int port, int regnum) @@ -560,7 +488,7 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, u32 reg, val; int ret; - mt7530_mutex_lock(priv); + mutex_lock(&priv->reg_mutex); ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -571,7 +499,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_DEV_ADDR(devad) | regnum; - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + reg | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -584,7 +513,8 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, reg = MT7531_MDIO_CL45_READ | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_DEV_ADDR(devad); - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + reg | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -597,7 +527,7 @@ mt7531_ind_c45_phy_read(struct mt7530_priv *priv, int port, int devad, ret = val & MT7531_MDIO_RW_DATA_MASK; out: - mt7530_mutex_unlock(priv); + mutex_unlock(&priv->reg_mutex); return ret; } @@ -609,7 +539,7 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, u32 val, reg; int ret; - mt7530_mutex_lock(priv); + mutex_lock(&priv->reg_mutex); ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -620,7 +550,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, reg = MT7531_MDIO_CL45_ADDR | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_DEV_ADDR(devad) | regnum; - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + reg | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -633,7 +564,8 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, reg = MT7531_MDIO_CL45_WRITE | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_DEV_ADDR(devad) | data; - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + reg | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -645,7 +577,7 @@ mt7531_ind_c45_phy_write(struct mt7530_priv *priv, int port, int devad, } out: - mt7530_mutex_unlock(priv); + mutex_unlock(&priv->reg_mutex); return ret; } @@ -656,7 +588,7 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) int ret; u32 val; - mt7530_mutex_lock(priv); + mutex_lock(&priv->reg_mutex); ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, val, !(val & MT7531_PHY_ACS_ST), 20, 100000); @@ -668,7 +600,8 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) val = MT7531_MDIO_CL22_READ | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_REG_ADDR(regnum); - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, val | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + val | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -681,7 +614,7 @@ mt7531_ind_c22_phy_read(struct mt7530_priv *priv, int port, int regnum) ret = val & MT7531_MDIO_RW_DATA_MASK; out: - mt7530_mutex_unlock(priv); + mutex_unlock(&priv->reg_mutex); return ret; } @@ -693,7 +626,7 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, int ret; u32 reg; - mt7530_mutex_lock(priv); + mutex_lock(&priv->reg_mutex); ret = regmap_read_poll_timeout(priv->regmap, MT7531_PHY_IAC, reg, !(reg & MT7531_PHY_ACS_ST), 20, 100000); @@ -705,7 +638,8 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, reg = MT7531_MDIO_CL22_WRITE | MT7531_MDIO_PHY_ADDR(port) | MT7531_MDIO_REG_ADDR(regnum) | data; - ret = mt7530_mii_write(priv, MT7531_PHY_IAC, reg | MT7531_PHY_ACS_ST); + ret = regmap_write(priv->regmap, MT7531_PHY_IAC, + reg | MT7531_PHY_ACS_ST); if (ret < 0) goto out; @@ -717,7 +651,7 @@ mt7531_ind_c22_phy_write(struct mt7530_priv *priv, int port, int regnum, } out: - mt7530_mutex_unlock(priv); + mutex_unlock(&priv->reg_mutex); return ret; } @@ -774,11 +708,11 @@ mt7530_read_port_stats(struct mt7530_priv *priv, int port, { u32 val, reg = MT7530_PORT_MIB_COUNTER(port) + offset; - val = mt7530_read(priv, reg); + regmap_read(priv->regmap, reg, &val); *data = val; if (size == 2) { - val = mt7530_read(priv, reg + 4); + regmap_read(priv->regmap, reg + 4, &val); *data |= (u64)val << 32; } } @@ -1060,7 +994,8 @@ mt7530_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) } } - mt7530_write(priv, MT7530_AAC, AGE_CNT(age_count) | AGE_UNIT(age_unit)); + regmap_write(priv->regmap, MT7530_AAC, + AGE_CNT(age_count) | AGE_UNIT(age_unit)); return 0; } @@ -1081,11 +1016,11 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) { struct mt7530_priv *priv = ds->priv; u8 tx_delay = 0; - int val; + u32 val; mutex_lock(&priv->reg_mutex); - val = mt7530_read(priv, MT753X_MTRAP); + regmap_read(priv->regmap, MT753X_MTRAP, &val); val &= ~MT7530_P5_PHY0_SEL & ~MT7530_P5_MAC_SEL & ~MT7530_P5_RGMII_MODE; @@ -1098,7 +1033,7 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) /* MUX_PHY_P4: P4 -> P5 -> SoC MAC */ case MUX_PHY_P4: /* Setup the MAC by default for the cpu port */ - mt7530_write(priv, MT753X_PMCR_P(5), 0x56300); + regmap_write(priv->regmap, MT753X_PMCR_P(5), 0x56300); break; /* GMAC5: P5 -> SoC MAC or external PHY */ @@ -1112,7 +1047,8 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) val |= MT7530_P5_RGMII_MODE; /* P5 RGMII RX Clock Control: delay setting for 1000M */ - mt7530_write(priv, MT7530_P5RGMIIRXCR, CSR_RGMII_EDGE_ALIGN); + regmap_write(priv->regmap, MT7530_P5RGMIIRXCR, + CSR_RGMII_EDGE_ALIGN); /* Don't set delay in DSA mode */ if (!dsa_is_dsa_port(priv->ds, 5) && @@ -1121,15 +1057,15 @@ static void mt7530_setup_port5(struct dsa_switch *ds, phy_interface_t interface) tx_delay = 4; /* n * 0.5 ns */ /* P5 RGMII TX Clock Control: delay x */ - mt7530_write(priv, MT7530_P5RGMIITXCR, + regmap_write(priv->regmap, MT7530_P5RGMIITXCR, CSR_RGMII_TXC_CFG(0x10 + tx_delay)); /* reduce P5 RGMII Tx driving, 8mA */ - mt7530_write(priv, MT7530_IO_DRV_CR, + regmap_write(priv->regmap, MT7530_IO_DRV_CR, P5_IO_CLK_DRV(1) | P5_IO_DATA_DRV(1)); } - mt7530_write(priv, MT753X_MTRAP, val); + regmap_write(priv->regmap, MT753X_MTRAP, val); dev_dbg(ds->dev, "Setup P5, HWTRAP=0x%x, mode=%s, phy-mode=%s\n", val, mt7530_p5_mode_str(priv->p5_mode), phy_modes(interface)); @@ -1314,35 +1250,35 @@ mt753x_trap_frames(struct mt7530_priv *priv) * switch egress VLAN tag processing. This preserves VLAN tags * for reception on VLAN sub-interfaces. */ - mt7530_rmw(priv, MT753X_BPC, - PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | - BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, - PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | - PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | - BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | - TO_CPU_FW_CPU_ONLY); + regmap_update_bits(priv->regmap, MT753X_BPC, + PAE_BPDU_FR | PAE_EG_TAG_MASK | PAE_PORT_FW_MASK | + BPDU_EG_TAG_MASK | BPDU_PORT_FW_MASK, + PAE_BPDU_FR | PAE_EG_TAG(MT7530_VLAN_EG_DISABLED) | + PAE_PORT_FW(TO_CPU_FW_CPU_ONLY) | + BPDU_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); /* Trap frames with :01 and :02 MAC DAs to the CPU port(s) and * egress them with EG_TAG disabled. */ - mt7530_rmw(priv, MT753X_RGAC1, - R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | - R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, - R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | - R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | - R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | - TO_CPU_FW_CPU_ONLY); + regmap_update_bits(priv->regmap, MT753X_RGAC1, + R02_BPDU_FR | R02_EG_TAG_MASK | R02_PORT_FW_MASK | + R01_BPDU_FR | R01_EG_TAG_MASK | R01_PORT_FW_MASK, + R02_BPDU_FR | R02_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R02_PORT_FW(TO_CPU_FW_CPU_ONLY) | R01_BPDU_FR | + R01_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); /* Trap frames with :03 and :0E MAC DAs to the CPU port(s) and * egress them with EG_TAG disabled. */ - mt7530_rmw(priv, MT753X_RGAC2, - R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | - R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, - R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | - R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | - R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | - TO_CPU_FW_CPU_ONLY); + regmap_update_bits(priv->regmap, MT753X_RGAC2, + R0E_BPDU_FR | R0E_EG_TAG_MASK | R0E_PORT_FW_MASK | + R03_BPDU_FR | R03_EG_TAG_MASK | R03_PORT_FW_MASK, + R0E_BPDU_FR | R0E_EG_TAG(MT7530_VLAN_EG_DISABLED) | + R0E_PORT_FW(TO_CPU_FW_CPU_ONLY) | R03_BPDU_FR | + R03_EG_TAG(MT7530_VLAN_EG_DISABLED) | + TO_CPU_FW_CPU_ONLY); } static void @@ -1351,12 +1287,11 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port) struct mt7530_priv *priv = ds->priv; /* Enable Mediatek header mode on the cpu port */ - mt7530_write(priv, MT7530_PVC_P(port), - PORT_SPEC_TAG); + regmap_write(priv->regmap, MT7530_PVC_P(port), PORT_SPEC_TAG); /* Enable flooding on the CPU port */ - mt7530_set(priv, MT753X_MFC, BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | - UNU_FFP(BIT(port))); + regmap_set_bits(priv->regmap, MT753X_MFC, + BC_FFP(BIT(port)) | UNM_FFP(BIT(port)) | UNU_FFP(BIT(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 @@ -1364,17 +1299,18 @@ mt753x_cpu_port_enable(struct dsa_switch *ds, int port) */ if (priv->id == ID_MT7531 || priv->id == ID_MT7988 || priv->id == ID_EN7581 || priv->id == ID_AN7583) - mt7530_set(priv, MT7531_CFC, MT7531_CPU_PMAP(BIT(port))); + regmap_set_bits(priv->regmap, MT7531_CFC, + MT7531_CPU_PMAP(BIT(port))); /* CPU port gets connected to all user ports of * the switch. */ - mt7530_write(priv, MT7530_PCR_P(port), + regmap_write(priv->regmap, MT7530_PCR_P(port), PCR_MATRIX(dsa_user_ports(priv->ds))); /* Set to fallback mode for independent VLAN learning */ - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, - MT7530_PORT_FALLBACK_MODE); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_PORT_VLAN_MASK, MT7530_PORT_FALLBACK_MODE); } static int @@ -1396,8 +1332,8 @@ mt7530_port_enable(struct dsa_switch *ds, int port, priv->ports[port].pm |= PCR_MATRIX(BIT(cpu_dp->index)); } priv->ports[port].enable = true; - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, - priv->ports[port].pm); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), PCR_MATRIX_MASK, + priv->ports[port].pm); mutex_unlock(&priv->reg_mutex); @@ -1405,9 +1341,9 @@ mt7530_port_enable(struct dsa_switch *ds, int port, return 0; if (port == 5) - mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS); + regmap_clear_bits(priv->regmap, MT753X_MTRAP, MT7530_P5_DIS); else if (port == 6) - mt7530_clear(priv, MT753X_MTRAP, MT7530_P6_DIS); + regmap_clear_bits(priv->regmap, MT753X_MTRAP, MT7530_P6_DIS); return 0; } @@ -1423,8 +1359,8 @@ mt7530_port_disable(struct dsa_switch *ds, int port) * enablement for the port. */ priv->ports[port].enable = false; - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, - PCR_MATRIX_CLR); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), PCR_MATRIX_MASK, + PCR_MATRIX_CLR); mutex_unlock(&priv->reg_mutex); @@ -1433,9 +1369,9 @@ mt7530_port_disable(struct dsa_switch *ds, int port) /* Do not set MT7530_P5_DIS when port 5 is being used for PHY muxing. */ if (port == 5 && priv->p5_mode == GMAC5) - mt7530_set(priv, MT753X_MTRAP, MT7530_P5_DIS); + regmap_set_bits(priv->regmap, MT753X_MTRAP, MT7530_P5_DIS); else if (port == 6) - mt7530_set(priv, MT753X_MTRAP, MT7530_P6_DIS); + regmap_set_bits(priv->regmap, MT753X_MTRAP, MT7530_P6_DIS); } static int @@ -1452,9 +1388,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) if (!dsa_is_cpu_port(ds, port)) return 0; - mt7530_mutex_lock(priv); - - val = mt7530_mii_read(priv, MT7530_GMACCR); + regmap_read(priv->regmap, MT7530_GMACCR, &val); val &= ~MAX_RX_PKT_LEN_MASK; /* RX length also includes Ethernet header, MTK tag, and FCS length */ @@ -1471,9 +1405,7 @@ mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) val |= MAX_RX_PKT_LEN_JUMBO; } - mt7530_mii_write(priv, MT7530_GMACCR, val); - - mt7530_mutex_unlock(priv); + regmap_write(priv->regmap, MT7530_GMACCR, val); return 0; } @@ -1509,8 +1441,9 @@ mt7530_stp_state_set(struct dsa_switch *ds, int port, u8 state) break; } - mt7530_rmw(priv, MT7530_SSP_P(port), FID_PST_MASK(FID_BRIDGED), - FID_PST(FID_BRIDGED, stp_state)); + regmap_update_bits(priv->regmap, MT7530_SSP_P(port), + FID_PST_MASK(FID_BRIDGED), + FID_PST(FID_BRIDGED, stp_state)); } static void mt7530_update_port_member(struct mt7530_priv *priv, int port, @@ -1549,8 +1482,9 @@ static void mt7530_update_port_member(struct mt7530_priv *priv, int port, } if (other_p->enable) - mt7530_rmw(priv, MT7530_PCR_P(other_port), - PCR_MATRIX_MASK, other_p->pm); + regmap_update_bits(priv->regmap, + MT7530_PCR_P(other_port), + PCR_MATRIX_MASK, other_p->pm); } /* Add/remove the all other ports to this port matrix. For !join @@ -1559,7 +1493,8 @@ static void mt7530_update_port_member(struct mt7530_priv *priv, int port, */ p->pm = PCR_MATRIX(port_bitmap); if (priv->ports[port].enable) - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, p->pm); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_MATRIX_MASK, p->pm); } static int @@ -1582,20 +1517,23 @@ mt7530_port_bridge_flags(struct dsa_switch *ds, int port, struct mt7530_priv *priv = ds->priv; if (flags.mask & BR_LEARNING) - mt7530_rmw(priv, MT7530_PSC_P(port), SA_DIS, - flags.val & BR_LEARNING ? 0 : SA_DIS); + regmap_update_bits(priv->regmap, MT7530_PSC_P(port), SA_DIS, + flags.val & BR_LEARNING ? 0 : SA_DIS); if (flags.mask & BR_FLOOD) - mt7530_rmw(priv, MT753X_MFC, UNU_FFP(BIT(port)), - flags.val & BR_FLOOD ? UNU_FFP(BIT(port)) : 0); + regmap_update_bits(priv->regmap, MT753X_MFC, + UNU_FFP(BIT(port)), + flags.val & BR_FLOOD ? UNU_FFP(BIT(port)) : 0); if (flags.mask & BR_MCAST_FLOOD) - mt7530_rmw(priv, MT753X_MFC, UNM_FFP(BIT(port)), - flags.val & BR_MCAST_FLOOD ? UNM_FFP(BIT(port)) : 0); + regmap_update_bits(priv->regmap, MT753X_MFC, + UNM_FFP(BIT(port)), + flags.val & BR_MCAST_FLOOD ? UNM_FFP(BIT(port)) : 0); if (flags.mask & BR_BCAST_FLOOD) - mt7530_rmw(priv, MT753X_MFC, BC_FFP(BIT(port)), - flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0); + regmap_update_bits(priv->regmap, MT753X_MFC, + BC_FFP(BIT(port)), + flags.val & BR_BCAST_FLOOD ? BC_FFP(BIT(port)) : 0); if (flags.mask & BR_ISOLATED) { struct dsa_port *dp = dsa_to_port(ds, port); @@ -1623,8 +1561,8 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port, mt7530_update_port_member(priv, port, bridge.dev, true); /* Set to fallback mode for independent VLAN learning */ - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, - MT7530_PORT_FALLBACK_MODE); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_PORT_VLAN_MASK, MT7530_PORT_FALLBACK_MODE); mutex_unlock(&priv->reg_mutex); @@ -1637,20 +1575,16 @@ mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid) u32 val; int ret; - val = VTCR_BUSY | VTCR_FUNC(cmd) | vid; - ret = mt7530_write(priv, MT7530_VTCR, val); + val = VTCR_BUSY | VTCR_FUNC(cmd) | VTCR_VID(vid); + ret = regmap_write(priv->regmap, MT7530_VTCR, val); if (ret) return ret; - mt7530_mutex_lock(priv); - ret = regmap_read_poll_timeout(priv->regmap, MT7530_VTCR, val, !(val & VTCR_BUSY), 20, 20000); if (!ret) ret = regmap_read(priv->regmap, MT7530_VTCR, &val); - mt7530_mutex_unlock(priv); - if (ret < 0) { dev_err(priv->dev, "poll timeout\n"); return ret; @@ -1674,8 +1608,8 @@ mt7530_setup_vlan0(struct mt7530_priv *priv) */ val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) | VLAN_VALID; - mt7530_write(priv, MT7530_VAWD1, val); - mt7530_write(priv, MT7530_VAWD2, 0); + regmap_write(priv->regmap, MT7530_VAWD1, val); + regmap_write(priv->regmap, MT7530_VAWD2, 0); return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0); } @@ -1691,18 +1625,19 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port) * bridge. Don't set standalone ports to fallback mode. */ if (dsa_port_bridge_dev_get(dsa_to_port(ds, port))) - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, - MT7530_PORT_FALLBACK_MODE); - - mt7530_rmw(priv, MT7530_PVC_P(port), - VLAN_ATTR_MASK | PVC_EG_TAG_MASK | ACC_FRM_MASK, - VLAN_ATTR(MT7530_VLAN_TRANSPARENT) | - PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT) | - MT7530_VLAN_ACC_ALL); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_PORT_VLAN_MASK, + MT7530_PORT_FALLBACK_MODE); + + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + VLAN_ATTR_MASK | PVC_EG_TAG_MASK | ACC_FRM_MASK, + VLAN_ATTR(MT7530_VLAN_TRANSPARENT) | + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT) | + MT7530_VLAN_ACC_ALL); /* Set PVID to 0 */ - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, - G0_PORT_VID_DEF); + regmap_update_bits(priv->regmap, MT7530_PPBV1_P(port), + G0_PORT_VID_MASK, G0_PORT_VID_DEF); for (i = 0; i < priv->ds->num_ports; i++) { if (i == port) @@ -1733,24 +1668,27 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port) * table lookup. */ if (dsa_is_user_port(ds, port)) { - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, - MT7530_PORT_SECURITY_MODE); - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, - G0_PORT_VID(priv->ports[port].pvid)); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_PORT_VLAN_MASK, + MT7530_PORT_SECURITY_MODE); + regmap_update_bits(priv->regmap, MT7530_PPBV1_P(port), + G0_PORT_VID_MASK, + G0_PORT_VID(priv->ports[port].pvid)); /* Only accept tagged frames if PVID is not set */ if (!priv->ports[port].pvid) - mt7530_rmw(priv, MT7530_PVC_P(port), ACC_FRM_MASK, - MT7530_VLAN_ACC_TAGGED); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + ACC_FRM_MASK, + MT7530_VLAN_ACC_TAGGED); /* Set the port as a user port which is to be able to recognize * VID from incoming packets before fetching entry within the * VLAN table. */ - mt7530_rmw(priv, MT7530_PVC_P(port), - VLAN_ATTR_MASK | PVC_EG_TAG_MASK, - VLAN_ATTR(MT7530_VLAN_USER) | - PVC_EG_TAG(MT7530_VLAN_EG_DISABLED)); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + VLAN_ATTR_MASK | PVC_EG_TAG_MASK, + VLAN_ATTR(MT7530_VLAN_USER) | + PVC_EG_TAG(MT7530_VLAN_EG_DISABLED)); } else { /* Also set CPU ports to the "user" VLAN port attribute, to * allow VLAN classification, but keep the EG_TAG attribute as @@ -1759,8 +1697,9 @@ mt7530_port_set_vlan_aware(struct dsa_switch *ds, int port) * are forwarded to user ports as tagged, and untagged as * untagged. */ - mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK, - VLAN_ATTR(MT7530_VLAN_USER)); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + VLAN_ATTR_MASK, + VLAN_ATTR(MT7530_VLAN_USER)); } } @@ -1778,8 +1717,8 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, * back to the default as is at initial boot which is a VLAN-unaware * port. */ - mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, - MT7530_PORT_MATRIX_MODE); + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_PORT_VLAN_MASK, MT7530_PORT_MATRIX_MODE); mutex_unlock(&priv->reg_mutex); } @@ -1862,14 +1801,16 @@ mt7530_port_mdb_add(struct dsa_switch *ds, int port, const u8 *addr = mdb->addr; u16 vid = mdb->vid; u8 port_mask = 0; + u32 val; int ret; mutex_lock(&priv->reg_mutex); mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP); - if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) - port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP) - & PORT_MAP_MASK; + if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) { + regmap_read(priv->regmap, MT7530_ATRD, &val); + port_mask = FIELD_GET(PORT_MAP_MASK, val); + } port_mask |= BIT(port); mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); @@ -1889,14 +1830,16 @@ mt7530_port_mdb_del(struct dsa_switch *ds, int port, const u8 *addr = mdb->addr; u16 vid = mdb->vid; u8 port_mask = 0; + u32 val; int ret; mutex_lock(&priv->reg_mutex); mt7530_fdb_write(priv, vid, 0, addr, 0, STATIC_EMP); - if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) - port_mask = (mt7530_read(priv, MT7530_ATRD) >> PORT_MAP) - & PORT_MAP_MASK; + if (!mt7530_fdb_cmd(priv, MT7530_FDB_READ, NULL)) { + regmap_read(priv->regmap, MT7530_ATRD, &val); + port_mask = FIELD_GET(PORT_MAP_MASK, val); + } port_mask &= ~BIT(port); mt7530_fdb_write(priv, vid, port_mask, addr, -1, @@ -1945,7 +1888,7 @@ mt7530_hw_vlan_add(struct mt7530_priv *priv, */ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | FID(FID_BRIDGED) | VLAN_VALID; - mt7530_write(priv, MT7530_VAWD1, val); + regmap_write(priv->regmap, MT7530_VAWD1, val); /* Decide whether adding tag or not for those outgoing packets from the * port inside the VLAN. @@ -1960,9 +1903,9 @@ mt7530_hw_vlan_add(struct mt7530_priv *priv, val = MT7530_VLAN_EGRESS_UNTAG; else val = MT7530_VLAN_EGRESS_TAG; - mt7530_rmw(priv, MT7530_VAWD2, - ETAG_CTRL_P_MASK(entry->port), - ETAG_CTRL_P(entry->port, val)); + regmap_update_bits(priv->regmap, MT7530_VAWD2, + ETAG_CTRL_P_MASK(entry->port), + ETAG_CTRL_P(entry->port, val)); } static void @@ -1974,7 +1917,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *priv, new_members = entry->old_members & ~BIT(entry->port); - val = mt7530_read(priv, MT7530_VAWD1); + regmap_read(priv->regmap, MT7530_VAWD1, &val); if (!(val & VLAN_VALID)) { dev_err(priv->dev, "Cannot be deleted due to invalid entry\n"); @@ -1984,10 +1927,10 @@ mt7530_hw_vlan_del(struct mt7530_priv *priv, if (new_members) { val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID; - mt7530_write(priv, MT7530_VAWD1, val); + regmap_write(priv->regmap, MT7530_VAWD1, val); } else { - mt7530_write(priv, MT7530_VAWD1, 0); - mt7530_write(priv, MT7530_VAWD2, 0); + regmap_write(priv->regmap, MT7530_VAWD1, 0); + regmap_write(priv->regmap, MT7530_VAWD2, 0); } } @@ -2001,9 +1944,9 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid, /* Fetch entry */ mt7530_vlan_cmd(priv, MT7530_VTCR_RD_VID, vid); - val = mt7530_read(priv, MT7530_VAWD1); + regmap_read(priv->regmap, MT7530_VAWD1, &val); - entry->old_members = (val >> PORT_MEM_SHFT) & PORT_MEM_MASK; + entry->old_members = FIELD_GET(PORT_MEM_MASK, val); /* Manipulate entry */ vlan_op(priv, entry); @@ -2040,25 +1983,26 @@ skip_vlan_table: priv->ports[port].pvid = vlan->vid; /* Accept all frames if PVID is set */ - mt7530_rmw(priv, MT7530_PVC_P(port), ACC_FRM_MASK, - MT7530_VLAN_ACC_ALL); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + ACC_FRM_MASK, MT7530_VLAN_ACC_ALL); /* Only configure PVID if VLAN filtering is enabled */ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) - mt7530_rmw(priv, MT7530_PPBV1_P(port), - G0_PORT_VID_MASK, - G0_PORT_VID(vlan->vid)); + regmap_update_bits(priv->regmap, MT7530_PPBV1_P(port), + G0_PORT_VID_MASK, + G0_PORT_VID(vlan->vid)); } else if (vlan->vid && priv->ports[port].pvid == vlan->vid) { /* This VLAN is overwritten without PVID, so unset it */ priv->ports[port].pvid = G0_PORT_VID_DEF; /* Only accept tagged frames if the port is VLAN-aware */ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) - mt7530_rmw(priv, MT7530_PVC_P(port), ACC_FRM_MASK, - MT7530_VLAN_ACC_TAGGED); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + ACC_FRM_MASK, + MT7530_VLAN_ACC_TAGGED); - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, - G0_PORT_VID_DEF); + regmap_update_bits(priv->regmap, MT7530_PPBV1_P(port), + G0_PORT_VID_MASK, G0_PORT_VID_DEF); } mutex_unlock(&priv->reg_mutex); @@ -2092,11 +2036,12 @@ skip_vlan_table: /* Only accept tagged frames if the port is VLAN-aware */ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) - mt7530_rmw(priv, MT7530_PVC_P(port), ACC_FRM_MASK, - MT7530_VLAN_ACC_TAGGED); + regmap_update_bits(priv->regmap, MT7530_PVC_P(port), + ACC_FRM_MASK, + MT7530_VLAN_ACC_TAGGED); - mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, - G0_PORT_VID_DEF); + regmap_update_bits(priv->regmap, MT7530_PPBV1_P(port), + G0_PORT_VID_MASK, G0_PORT_VID_DEF); } @@ -2117,7 +2062,7 @@ static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, if ((ingress ? priv->mirror_rx : priv->mirror_tx) & BIT(port)) return -EEXIST; - val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id)); + regmap_read(priv->regmap, MT753X_MIRROR_REG(priv->id), &val); /* MT7530 only supports one monitor port */ monitor_port = MT753X_MIRROR_PORT_GET(priv->id, val); @@ -2128,9 +2073,9 @@ static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, val |= MT753X_MIRROR_EN(priv->id); val &= ~MT753X_MIRROR_PORT_MASK(priv->id); val |= MT753X_MIRROR_PORT_SET(priv->id, mirror->to_local_port); - mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val); + regmap_write(priv->regmap, MT753X_MIRROR_REG(priv->id), val); - val = mt7530_read(priv, MT7530_PCR_P(port)); + regmap_read(priv->regmap, MT7530_PCR_P(port), &val); if (ingress) { val |= PORT_RX_MIR; priv->mirror_rx |= BIT(port); @@ -2138,7 +2083,7 @@ static int mt753x_port_mirror_add(struct dsa_switch *ds, int port, val |= PORT_TX_MIR; priv->mirror_tx |= BIT(port); } - mt7530_write(priv, MT7530_PCR_P(port), val); + regmap_write(priv->regmap, MT7530_PCR_P(port), val); return 0; } @@ -2149,7 +2094,7 @@ static void mt753x_port_mirror_del(struct dsa_switch *ds, int port, struct mt7530_priv *priv = ds->priv; u32 val; - val = mt7530_read(priv, MT7530_PCR_P(port)); + regmap_read(priv->regmap, MT7530_PCR_P(port), &val); if (mirror->ingress) { val &= ~PORT_RX_MIR; priv->mirror_rx &= ~BIT(port); @@ -2157,12 +2102,12 @@ static void mt753x_port_mirror_del(struct dsa_switch *ds, int port, val &= ~PORT_TX_MIR; priv->mirror_tx &= ~BIT(port); } - mt7530_write(priv, MT7530_PCR_P(port), val); + regmap_write(priv->regmap, MT7530_PCR_P(port), val); if (!priv->mirror_rx && !priv->mirror_tx) { - val = mt7530_read(priv, MT753X_MIRROR_REG(priv->id)); + regmap_read(priv->regmap, MT753X_MIRROR_REG(priv->id), &val); val &= ~MT753X_MIRROR_EN(priv->id); - mt7530_write(priv, MT753X_MIRROR_REG(priv->id), val); + regmap_write(priv->regmap, MT753X_MIRROR_REG(priv->id), val); } } @@ -2192,8 +2137,11 @@ mt7530_gpio_get(struct gpio_chip *gc, unsigned int offset) { struct mt7530_priv *priv = gpiochip_get_data(gc); u32 bit = mt7530_gpio_to_bit(offset); + u32 val; + + regmap_read(priv->regmap, MT7530_LED_GPIO_DATA, &val); - return !!(mt7530_read(priv, MT7530_LED_GPIO_DATA) & bit); + return !!(val & bit); } static int @@ -2202,10 +2150,7 @@ mt7530_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) struct mt7530_priv *priv = gpiochip_get_data(gc); u32 bit = mt7530_gpio_to_bit(offset); - if (value) - mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); - else - mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); + regmap_assign_bits(priv->regmap, MT7530_LED_GPIO_DATA, bit, value); return 0; } @@ -2215,8 +2160,11 @@ mt7530_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) { struct mt7530_priv *priv = gpiochip_get_data(gc); u32 bit = mt7530_gpio_to_bit(offset); + u32 val; - return (mt7530_read(priv, MT7530_LED_GPIO_DIR) & bit) ? + regmap_read(priv->regmap, MT7530_LED_GPIO_DIR, &val); + + return (val & bit) ? GPIO_LINE_DIRECTION_OUT : GPIO_LINE_DIRECTION_IN; } @@ -2226,8 +2174,8 @@ mt7530_gpio_direction_input(struct gpio_chip *gc, unsigned int offset) struct mt7530_priv *priv = gpiochip_get_data(gc); u32 bit = mt7530_gpio_to_bit(offset); - mt7530_clear(priv, MT7530_LED_GPIO_OE, bit); - mt7530_clear(priv, MT7530_LED_GPIO_DIR, bit); + regmap_clear_bits(priv->regmap, MT7530_LED_GPIO_OE, bit); + regmap_clear_bits(priv->regmap, MT7530_LED_GPIO_DIR, bit); return 0; } @@ -2238,14 +2186,11 @@ mt7530_gpio_direction_output(struct gpio_chip *gc, unsigned int offset, int valu struct mt7530_priv *priv = gpiochip_get_data(gc); u32 bit = mt7530_gpio_to_bit(offset); - mt7530_set(priv, MT7530_LED_GPIO_DIR, bit); + regmap_set_bits(priv->regmap, MT7530_LED_GPIO_DIR, bit); - if (value) - mt7530_set(priv, MT7530_LED_GPIO_DATA, bit); - else - mt7530_clear(priv, MT7530_LED_GPIO_DATA, bit); + regmap_assign_bits(priv->regmap, MT7530_LED_GPIO_DATA, bit, value); - mt7530_set(priv, MT7530_LED_GPIO_OE, bit); + regmap_set_bits(priv->regmap, MT7530_LED_GPIO_OE, bit); return 0; } @@ -2260,9 +2205,9 @@ mt7530_setup_gpio(struct mt7530_priv *priv) if (!gc) return -ENOMEM; - mt7530_write(priv, MT7530_LED_GPIO_OE, 0); - mt7530_write(priv, MT7530_LED_GPIO_DIR, 0); - mt7530_write(priv, MT7530_LED_IO_MODE, 0); + regmap_write(priv->regmap, MT7530_LED_GPIO_OE, 0); + regmap_write(priv->regmap, MT7530_LED_GPIO_DIR, 0); + regmap_write(priv->regmap, MT7530_LED_IO_MODE, 0); gc->label = "mt7530"; gc->parent = dev; @@ -2319,21 +2264,6 @@ static const struct regmap_irq mt7530_irqs[] = { REGMAP_IRQ_REG_LINE(31, 32), /* ACL */ }; -/* Serialize regmap-irq's mask sync like every other regmap user */ -static int mt7530_irq_mask_sync(int index, unsigned int mask_buf_def, - unsigned int mask_buf, void *irq_drv_data) -{ - struct mt7530_priv *priv = irq_drv_data; - int ret; - - mt7530_mutex_lock(priv); - ret = regmap_update_bits(priv->regmap, MT7530_SYS_INT_EN, - mask_buf_def, ~mask_buf); - mt7530_mutex_unlock(priv); - - return ret; -} - static const struct regmap_irq_chip mt7530_regmap_irq_chip = { .name = KBUILD_MODNAME, .status_base = MT7530_SYS_INT_STS, @@ -2343,14 +2273,12 @@ static const struct regmap_irq_chip mt7530_regmap_irq_chip = { .irqs = mt7530_irqs, .num_irqs = ARRAY_SIZE(mt7530_irqs), .num_regs = 1, - .handle_mask_sync = mt7530_irq_mask_sync, }; static int mt7530_setup_irq(struct mt7530_priv *priv) { struct regmap_irq_chip_data *irq_data; - struct regmap_irq_chip *chip; struct device *dev = priv->dev; struct device_node *np = dev->of_node; int irq, ret; @@ -2368,19 +2296,13 @@ mt7530_setup_irq(struct mt7530_priv *priv) /* This register must be set for MT7530 to properly fire interrupts */ if (priv->id == ID_MT7530 || priv->id == ID_MT7621) - mt7530_set(priv, MT7530_TOP_SIG_CTRL, TOP_SIG_CTRL_NORMAL); - - chip = devm_kmemdup(dev, &mt7530_regmap_irq_chip, sizeof(*chip), - GFP_KERNEL); - if (!chip) - return -ENOMEM; - - chip->irq_drv_data = priv; + regmap_set_bits(priv->regmap, MT7530_TOP_SIG_CTRL, + TOP_SIG_CTRL_NORMAL); ret = devm_regmap_add_irq_chip_fwnode(dev, dev_fwnode(dev), priv->regmap, irq, IRQF_ONESHOT, - 0, chip, + 0, &mt7530_regmap_irq_chip, &irq_data); if (ret) return ret; @@ -2461,7 +2383,6 @@ mt7530_setup(struct dsa_switch *ds) struct device_node *dn = NULL; struct device_node *phy_node; struct device_node *mac_np; - struct mt7530_dummy_poll p; phy_interface_t interface; struct dsa_port *cpu_dp; u32 id, val; @@ -2522,16 +2443,15 @@ mt7530_setup(struct dsa_switch *ds) } /* Waiting for MT7530 got to stable */ - INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP); - ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, - 20, 1000000); + ret = regmap_read_poll_timeout(priv->regmap, MT753X_TRAP, val, + val != 0, 20, 1000000); if (ret < 0) { dev_err(priv->dev, "reset timeout\n"); return ret; } - id = mt7530_read(priv, MT7530_CREV); - id >>= CHIP_NAME_SHIFT; + regmap_read(priv->regmap, MT7530_CREV, &id); + id = FIELD_GET(CHIP_NAME_MASK, id); if (id != MT7530_ID) { dev_err(priv->dev, "chip %x can't be supported\n", id); return -ENODEV; @@ -2544,24 +2464,24 @@ mt7530_setup(struct dsa_switch *ds) } /* Reset the switch through internal reset */ - mt7530_write(priv, MT7530_SYS_CTRL, - SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | - SYS_CTRL_REG_RST); + regmap_write(priv->regmap, MT7530_SYS_CTRL, + SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); /* Lower Tx driving for TRGMII path */ for (i = 0; i < NUM_TRGMII_CTRL; i++) - mt7530_write(priv, MT7530_TRGMII_TD_ODT(i), + regmap_write(priv->regmap, 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)); + regmap_update_bits(priv->regmap, MT7530_TRGMII_RD(i), + RD_TAP_MASK, RD_TAP(16)); /* Allow modifying the trap and directly access PHY registers via the * MDIO bus the switch is on. */ - mt7530_rmw(priv, MT753X_MTRAP, MT7530_CHG_TRAP | - MT7530_PHY_INDIRECT_ACCESS, MT7530_CHG_TRAP); + regmap_update_bits(priv->regmap, MT753X_MTRAP, + MT7530_CHG_TRAP | MT7530_PHY_INDIRECT_ACCESS, + MT7530_CHG_TRAP); if ((val & MT7530_XTAL_MASK) == MT7530_XTAL_40MHZ) mt7530_pll_setup(priv); @@ -2575,17 +2495,16 @@ mt7530_setup(struct dsa_switch *ds) /* Clear link settings and enable force mode to force link down * on all ports until they're enabled later. */ - mt7530_rmw(priv, MT753X_PMCR_P(i), - PMCR_LINK_SETTINGS_MASK | - MT753X_FORCE_MODE(priv->id), - MT753X_FORCE_MODE(priv->id)); + regmap_update_bits(priv->regmap, MT753X_PMCR_P(i), + PMCR_LINK_SETTINGS_MASK | MT753X_FORCE_MODE(priv->id), + MT753X_FORCE_MODE(priv->id)); /* Disable forwarding by default on all ports */ - mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, - PCR_MATRIX_CLR); + regmap_update_bits(priv->regmap, 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); + regmap_set_bits(priv->regmap, MT7530_PSC_P(i), SA_DIS); if (dsa_is_cpu_port(ds, i)) { mt753x_cpu_port_enable(ds, i); @@ -2593,16 +2512,17 @@ mt7530_setup(struct dsa_switch *ds) 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); + regmap_update_bits(priv->regmap, 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)); + regmap_update_bits(priv->regmap, MT7530_PVC_P(i), + PVC_EG_TAG_MASK, + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); } /* Allow mirroring frames received on the local port (monitor port). */ - mt7530_set(priv, MT753X_AGC, LOCAL_EN); + regmap_set_bits(priv->regmap, MT753X_AGC, LOCAL_EN); /* Setup VLAN ID 0 for VLAN-unaware bridges */ ret = mt7530_setup_vlan0(priv); @@ -2649,7 +2569,8 @@ mt7530_setup(struct dsa_switch *ds) if (priv->p5_mode == MUX_PHY_P0 || priv->p5_mode == MUX_PHY_P4) { - mt7530_clear(priv, MT753X_MTRAP, MT7530_P5_DIS); + regmap_clear_bits(priv->regmap, MT753X_MTRAP, + MT7530_P5_DIS); mt7530_setup_port5(ds, interface); } } @@ -2688,26 +2609,26 @@ mt7531_setup_common(struct dsa_switch *ds) mt7530_mib_reset(ds); /* Disable flooding on all ports */ - mt7530_clear(priv, MT753X_MFC, BC_FFP_MASK | UNM_FFP_MASK | - UNU_FFP_MASK); + regmap_clear_bits(priv->regmap, MT753X_MFC, + BC_FFP_MASK | UNM_FFP_MASK | UNU_FFP_MASK); for (i = 0; i < priv->ds->num_ports; i++) { /* Clear link settings and enable force mode to force link down * on all ports until they're enabled later. */ - mt7530_rmw(priv, MT753X_PMCR_P(i), - PMCR_LINK_SETTINGS_MASK | - MT753X_FORCE_MODE(priv->id), - MT753X_FORCE_MODE(priv->id)); + regmap_update_bits(priv->regmap, MT753X_PMCR_P(i), + PMCR_LINK_SETTINGS_MASK | MT753X_FORCE_MODE(priv->id), + MT753X_FORCE_MODE(priv->id)); /* Disable forwarding by default on all ports */ - mt7530_rmw(priv, MT7530_PCR_P(i), PCR_MATRIX_MASK, - PCR_MATRIX_CLR); + regmap_update_bits(priv->regmap, 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); + regmap_set_bits(priv->regmap, MT7530_PSC_P(i), SA_DIS); - mt7530_set(priv, MT7531_DBG_CNT(i), MT7531_DIS_CLR); + regmap_set_bits(priv->regmap, MT7531_DBG_CNT(i), + MT7531_DIS_CLR); if (dsa_is_cpu_port(ds, i)) { mt753x_cpu_port_enable(ds, i); @@ -2715,21 +2636,22 @@ mt7531_setup_common(struct dsa_switch *ds) 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); + regmap_update_bits(priv->regmap, 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)); + regmap_update_bits(priv->regmap, MT7530_PVC_P(i), + PVC_EG_TAG_MASK, + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); } /* Allow mirroring frames received on the local port (monitor port). */ - mt7530_set(priv, MT753X_AGC, LOCAL_EN); + regmap_set_bits(priv->regmap, MT753X_AGC, LOCAL_EN); /* Enable Special Tag for rx frames */ if (priv->id == ID_EN7581 || priv->id == ID_AN7583) - mt7530_write(priv, MT753X_CPORT_SPTAG_CFG, + regmap_write(priv->regmap, MT753X_CPORT_SPTAG_CFG, CPORT_SW2FE_STAG_EN | CPORT_FE2SW_STAG_EN); /* Flush the FDB table */ @@ -2745,7 +2667,6 @@ static int mt7531_setup(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; - struct mt7530_dummy_poll p; u32 val, id; int ret, i; @@ -2763,16 +2684,15 @@ mt7531_setup(struct dsa_switch *ds) } /* Waiting for MT7530 got to stable */ - INIT_MT7530_DUMMY_POLL(&p, priv, MT753X_TRAP); - ret = readx_poll_timeout(_mt7530_read, &p, val, val != 0, - 20, 1000000); + ret = regmap_read_poll_timeout(priv->regmap, MT753X_TRAP, val, + val != 0, 20, 1000000); if (ret < 0) { dev_err(priv->dev, "reset timeout\n"); return ret; } - id = mt7530_read(priv, MT7531_CREV); - id >>= CHIP_NAME_SHIFT; + regmap_read(priv->regmap, MT7531_CREV, &id); + id = FIELD_GET(CHIP_NAME_MASK, id); if (id != MT7531_ID) { dev_err(priv->dev, "chip %x can't be supported\n", id); @@ -2782,15 +2702,17 @@ mt7531_setup(struct dsa_switch *ds) /* MT7531AE has got two SGMII units. One for port 5, one for port 6. * MT7531BE has got only one SGMII unit which is for port 6. */ - val = mt7530_read(priv, MT7531_TOP_SIG_SR); + regmap_read(priv->regmap, MT7531_TOP_SIG_SR, &val); priv->p5_sgmii = !!(val & PAD_DUAL_SGMII_EN); /* Force link down on all ports before internal reset */ for (i = 0; i < priv->ds->num_ports; i++) - mt7530_write(priv, MT753X_PMCR_P(i), MT7531_FORCE_MODE_LNK); + regmap_write(priv->regmap, MT753X_PMCR_P(i), + MT7531_FORCE_MODE_LNK); /* Reset the switch through internal reset */ - mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); + regmap_write(priv->regmap, MT7530_SYS_CTRL, + SYS_CTRL_SW_RST | SYS_CTRL_REG_RST); if (!priv->p5_sgmii) { mt7531_pll_setup(priv); @@ -2799,14 +2721,16 @@ mt7531_setup(struct dsa_switch *ds) * MT7531AE. Set the GPIO 11-12 pins to function as MDC and MDIO * to expose the MDIO bus of the switch. */ - mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO11_RG_RXD2_MASK, - MT7531_EXT_P_MDC_11); - mt7530_rmw(priv, MT7531_GPIO_MODE1, MT7531_GPIO12_RG_RXD3_MASK, - MT7531_EXT_P_MDIO_12); + regmap_update_bits(priv->regmap, MT7531_GPIO_MODE1, + MT7531_GPIO11_RG_RXD2_MASK, + MT7531_EXT_P_MDC_11); + regmap_update_bits(priv->regmap, MT7531_GPIO_MODE1, + MT7531_GPIO12_RG_RXD3_MASK, + MT7531_EXT_P_MDIO_12); } - mt7530_rmw(priv, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, - MT7531_GPIO0_INTERRUPT); + regmap_update_bits(priv->regmap, MT7531_GPIO_MODE0, MT7531_GPIO0_MASK, + MT7531_GPIO0_INTERRUPT); /* Enable Energy-Efficient Ethernet (EEE) and PHY core PLL, since * phy_device has not yet been created provided for @@ -2998,7 +2922,7 @@ static void mt7531_rgmii_setup(struct mt7530_priv *priv, { u32 val; - val = mt7530_read(priv, MT7531_CLKGEN_CTRL); + regmap_read(priv->regmap, MT7531_CLKGEN_CTRL, &val); val |= GP_CLK_EN; val &= ~GP_MODE_MASK; val |= GP_MODE(MT7531_GP_MODE_RGMII); @@ -3029,7 +2953,7 @@ static void mt7531_rgmii_setup(struct mt7530_priv *priv, } } - mt7530_write(priv, MT7531_CLKGEN_CTRL, val); + regmap_write(priv->regmap, MT7531_CLKGEN_CTRL, val); } static void @@ -3082,7 +3006,8 @@ mt753x_phylink_mac_config(struct phylink_config *config, unsigned int mode, /* Are we connected to external phy */ if (port == 5 && dsa_is_user_port(ds, 5)) - mt7530_set(priv, MT753X_PMCR_P(port), PMCR_EXT_PHY); + regmap_set_bits(priv->regmap, MT753X_PMCR_P(port), + PMCR_EXT_PHY); } static void mt753x_phylink_mac_link_down(struct phylink_config *config, @@ -3092,7 +3017,8 @@ static void mt753x_phylink_mac_link_down(struct phylink_config *config, struct dsa_port *dp = dsa_phylink_to_port(config); struct mt7530_priv *priv = dp->ds->priv; - mt7530_clear(priv, MT753X_PMCR_P(dp->index), PMCR_LINK_SETTINGS_MASK); + regmap_clear_bits(priv->regmap, MT753X_PMCR_P(dp->index), + PMCR_LINK_SETTINGS_MASK); } static void mt753x_phylink_mac_link_up(struct phylink_config *config, @@ -3126,7 +3052,7 @@ static void mt753x_phylink_mac_link_up(struct phylink_config *config, mcr |= PMCR_FORCE_RX_FC_EN; } - mt7530_set(priv, MT753X_PMCR_P(dp->index), mcr); + regmap_set_bits(priv->regmap, MT753X_PMCR_P(dp->index), mcr); } static void mt753x_phylink_mac_disable_tx_lpi(struct phylink_config *config) @@ -3134,8 +3060,8 @@ static void mt753x_phylink_mac_disable_tx_lpi(struct phylink_config *config) struct dsa_port *dp = dsa_phylink_to_port(config); struct mt7530_priv *priv = dp->ds->priv; - mt7530_clear(priv, MT753X_PMCR_P(dp->index), - PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); + regmap_clear_bits(priv->regmap, MT753X_PMCR_P(dp->index), + PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); } static int mt753x_phylink_mac_enable_tx_lpi(struct phylink_config *config, @@ -3156,11 +3082,11 @@ static int mt753x_phylink_mac_enable_tx_lpi(struct phylink_config *config, else val = LPI_THRESH_MASK; - mt7530_rmw(priv, MT753X_PMEEECR_P(dp->index), - LPI_THRESH_MASK | LPI_MODE_EN, val); + regmap_update_bits(priv->regmap, MT753X_PMEEECR_P(dp->index), + LPI_THRESH_MASK | LPI_MODE_EN, val); - mt7530_set(priv, MT753X_PMCR_P(dp->index), - PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); + regmap_set_bits(priv->regmap, MT753X_PMCR_P(dp->index), + PMCR_FORCE_EEE1G | PMCR_FORCE_EEE100); return 0; } @@ -3182,7 +3108,9 @@ static void mt753x_phylink_get_caps(struct dsa_switch *ds, int port, */ if (priv->id != ID_EN7528 && config->mac_capabilities & (MAC_100FD | MAC_1000FD)) { - u32 eeecr = mt7530_read(priv, MT753X_PMEEECR_P(port)); + u32 eeecr; + + regmap_read(priv->regmap, MT753X_PMEEECR_P(port), &eeecr); /* LPI above 1 Gbps is not supported */ config->lpi_capabilities = config->mac_capabilities & @@ -3218,7 +3146,7 @@ static void mt7530_pcs_get_state(struct phylink_pcs *pcs, unsigned int neg_mode, int port = pcs_to_mt753x_pcs(pcs)->port; u32 pmsr; - pmsr = mt7530_read(priv, MT7530_PMSR_P(port)); + regmap_read(priv->regmap, MT7530_PMSR_P(port), &pmsr); state->link = (pmsr & PMSR_LINK); state->an_complete = state->link; @@ -3323,6 +3251,43 @@ static int mt753x_set_mac_eee(struct dsa_switch *ds, int port, return 0; } +static int +mt753x_port_change_conduit(struct dsa_switch *ds, int port, + struct net_device *conduit, + struct netlink_ext_ack *extack) +{ + struct dsa_port *new_cpu_dp = conduit->dsa_ptr; + struct dsa_port *dp = dsa_to_port(ds, port); + struct mt7530_priv *priv = ds->priv; + + if (priv->id != ID_MT7531) { + NL_SET_ERR_MSG_MOD(extack, + "Changing DSA conduit is only supported on MT7531"); + return -EOPNOTSUPP; + } + + if (new_cpu_dp->ds != ds) { + NL_SET_ERR_MSG_MOD(extack, + "Cannot assign a conduit on a different switch"); + return -EOPNOTSUPP; + } + + mutex_lock(&priv->reg_mutex); + + /* dp->cpu_dp still points to the old CPU port */ + priv->ports[port].pm &= ~PCR_MATRIX(BIT(dp->cpu_dp->index)); + priv->ports[port].pm |= PCR_MATRIX(BIT(new_cpu_dp->index)); + if (priv->ports[port].enable) + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_MATRIX_MASK, priv->ports[port].pm); + + mutex_unlock(&priv->reg_mutex); + + mt7530_port_fast_age(ds, port); + + return 0; +} + static void mt753x_conduit_state_change(struct dsa_switch *ds, const struct net_device *conduit, @@ -3353,7 +3318,8 @@ mt753x_conduit_state_change(struct dsa_switch *ds, MT7530_CPU_PORT(__ffs(priv->active_cpu_ports)); } - mt7530_rmw(priv, MT753X_MFC, MT7530_CPU_EN | MT7530_CPU_PORT_MASK, val); + regmap_update_bits(priv->regmap, MT753X_MFC, + MT7530_CPU_EN | MT7530_CPU_PORT_MASK, val); } static int mt753x_tc_setup_qdisc_tbf(struct dsa_switch *ds, int port, @@ -3370,8 +3336,8 @@ static int mt753x_tc_setup_qdisc_tbf(struct dsa_switch *ds, int port, case TC_TBF_DESTROY: { u32 val, tick; - mt7530_rmw(priv, MT753X_GERLCR, EGR_BC_MASK, - EGR_BC_CRC_IPG_PREAMBLE); + regmap_update_bits(priv->regmap, MT753X_GERLCR, EGR_BC_MASK, + EGR_BC_CRC_IPG_PREAMBLE); /* if rate is greater than 10Mbps tick is 1/32 ms, * 1ms otherwise @@ -3382,7 +3348,7 @@ static int mt753x_tc_setup_qdisc_tbf(struct dsa_switch *ds, int port, FIELD_PREP(ERLCR_EXP_MASK, tick) | ERLCR_TBF_MODE_MASK | FIELD_PREP(ERLCR_MANT_MASK, 0xf); - mt7530_write(priv, MT753X_ERLCR_P(port), val); + regmap_write(priv->regmap, MT753X_ERLCR_P(port), val); break; } default: @@ -3415,16 +3381,16 @@ static int mt7988_setup(struct dsa_switch *ds) /* AN7583 require additional tweak to CONN_CFG */ if (priv->id == ID_AN7583) - mt7530_rmw(priv, AN7583_GEPHY_CONN_CFG, - AN7583_CSR_DPHY_CKIN_SEL | - AN7583_CSR_PHY_CORE_REG_CLK_SEL | - AN7583_CSR_ETHER_AFE_PWD, - AN7583_CSR_DPHY_CKIN_SEL | - AN7583_CSR_PHY_CORE_REG_CLK_SEL | - FIELD_PREP(AN7583_CSR_ETHER_AFE_PWD, 0)); + regmap_update_bits(priv->regmap, AN7583_GEPHY_CONN_CFG, + AN7583_CSR_DPHY_CKIN_SEL | + AN7583_CSR_PHY_CORE_REG_CLK_SEL | + AN7583_CSR_ETHER_AFE_PWD, + AN7583_CSR_DPHY_CKIN_SEL | + AN7583_CSR_PHY_CORE_REG_CLK_SEL | + FIELD_PREP(AN7583_CSR_ETHER_AFE_PWD, 0)); /* Reset the switch PHYs */ - mt7530_write(priv, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST); + regmap_write(priv->regmap, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST); return mt7531_setup_common(ds); } @@ -3434,6 +3400,7 @@ static const struct dsa_switch_ops mt7530_switch_ops = { .setup = mt753x_setup, .teardown = mt753x_teardown, .preferred_default_local_cpu_port = mt753x_preferred_default_local_cpu_port, + .port_change_conduit = mt753x_port_change_conduit, .get_strings = mt7530_get_strings, .get_ethtool_stats = mt7530_get_ethtool_stats, .get_sset_count = mt7530_get_sset_count, @@ -3451,6 +3418,7 @@ static const struct dsa_switch_ops mt7530_switch_ops = { .port_bridge_flags = mt7530_port_bridge_flags, .port_bridge_join = mt7530_port_bridge_join, .port_bridge_leave = mt7530_port_bridge_leave, + .port_fast_age = mt7530_port_fast_age, .port_fdb_add = mt7530_port_fdb_add, .port_fdb_del = mt7530_port_fdb_del, .port_fdb_dump = mt7530_port_fdb_dump, diff --git a/drivers/net/dsa/mt7530.h b/drivers/net/dsa/mt7530.h index 5f1e841f42c0..2bbbe617b52e 100644 --- a/drivers/net/dsa/mt7530.h +++ b/drivers/net/dsa/mt7530.h @@ -6,6 +6,8 @@ #ifndef __MT7530_H #define __MT7530_H +#include <linux/bitfield.h> + #define MT7530_NUM_PORTS 7 #define MT7530_NUM_PHYS 5 #define MT7530_NUM_FDB_RECORDS 2048 @@ -147,20 +149,24 @@ enum mt753x_to_cpu_fw { #define STATIC_ENT 3 #define MT7530_ATA2 0x78 #define ATA2_IVL BIT(15) -#define ATA2_FID(x) (((x) & 0x7) << 12) +#define ATA2_FID_MASK GENMASK(14, 12) +#define ATA2_FID(x) FIELD_PREP(ATA2_FID_MASK, x) /* Register for address table write data */ #define MT7530_ATWD 0x7c /* Register for address table control */ #define MT7530_ATC 0x80 -#define ATC_HASH (((x) & 0xfff) << 16) +#define ATC_HASH_MASK GENMASK(27, 16) +#define ATC_HASH(x) FIELD_PREP(ATC_HASH_MASK, x) #define ATC_BUSY BIT(15) #define ATC_SRCH_END BIT(14) #define ATC_SRCH_HIT BIT(13) #define ATC_INVALID BIT(12) -#define ATC_MAT(x) (((x) & 0xf) << 8) +#define ATC_MAT_MASK GENMASK(11, 8) +#define ATC_MAT(x) FIELD_PREP(ATC_MAT_MASK, x) #define ATC_MAT_MACTAB ATC_MAT(0) +#define ATC_MAT_NON_STATIC_MAC ATC_MAT(4) enum mt7530_fdb_cmd { MT7530_FDB_READ = 0, @@ -172,32 +178,29 @@ enum mt7530_fdb_cmd { /* Registers for table search read address */ #define MT7530_TSRA1 0x84 -#define MAC_BYTE_0 24 -#define MAC_BYTE_1 16 -#define MAC_BYTE_2 8 -#define MAC_BYTE_3 0 -#define MAC_BYTE_MASK 0xff +#define MAC_BYTE_0_MASK GENMASK(31, 24) +#define MAC_BYTE_1_MASK GENMASK(23, 16) +#define MAC_BYTE_2_MASK GENMASK(15, 8) +#define MAC_BYTE_3_MASK GENMASK(7, 0) #define MT7530_TSRA2 0x88 -#define MAC_BYTE_4 24 -#define MAC_BYTE_5 16 -#define CVID 0 -#define CVID_MASK 0xfff +#define MAC_BYTE_4_MASK GENMASK(31, 24) +#define MAC_BYTE_5_MASK GENMASK(23, 16) +#define CVID_MASK GENMASK(11, 0) #define MT7530_ATRD 0x8C -#define AGE_TIMER 24 -#define AGE_TIMER_MASK 0xff -#define PORT_MAP 4 -#define PORT_MAP_MASK 0xff -#define ENT_STATUS 2 -#define ENT_STATUS_MASK 0x3 +#define AGE_TIMER_MASK GENMASK(31, 24) +#define PORT_MAP_MASK GENMASK(11, 4) +#define ENT_STATUS_MASK GENMASK(3, 2) /* Register for vlan table control */ #define MT7530_VTCR 0x90 #define VTCR_BUSY BIT(31) #define VTCR_INVALID BIT(16) -#define VTCR_FUNC(x) (((x) & 0xf) << 12) -#define VTCR_VID ((x) & 0xfff) +#define VTCR_FUNC_MASK GENMASK(15, 12) +#define VTCR_FUNC(x) FIELD_PREP(VTCR_FUNC_MASK, x) +#define VTCR_VID_MASK GENMASK(11, 0) +#define VTCR_VID(x) FIELD_PREP(VTCR_VID_MASK, x) enum mt7530_vlan_cmd { /* Read/Write the specified VID entry from VAWD register based @@ -217,13 +220,13 @@ enum mt7530_vlan_cmd { /* Per VLAN Egress Tag Control */ #define VTAG_EN BIT(28) /* VLAN Member Control */ -#define PORT_MEM(x) (((x) & 0xff) << 16) +#define PORT_MEM_MASK GENMASK(23, 16) +#define PORT_MEM(x) FIELD_PREP(PORT_MEM_MASK, x) /* Filter ID */ -#define FID(x) (((x) & 0x7) << 1) +#define FID_MASK GENMASK(3, 1) +#define FID(x) FIELD_PREP(FID_MASK, x) /* VLAN Entry Valid */ #define VLAN_VALID BIT(0) -#define PORT_MEM_SHFT 16 -#define PORT_MEM_MASK 0xff enum mt7530_fid { FID_STANDALONE = 0, @@ -248,11 +251,11 @@ enum mt7530_vlan_egress_attr { /* Age count */ #define AGE_CNT_MASK GENMASK(19, 12) #define AGE_CNT_MAX 0xff -#define AGE_CNT(x) (AGE_CNT_MASK & ((x) << 12)) +#define AGE_CNT(x) FIELD_PREP(AGE_CNT_MASK, x) /* Age unit */ #define AGE_UNIT_MASK GENMASK(11, 0) #define AGE_UNIT_MAX 0xfff -#define AGE_UNIT(x) (AGE_UNIT_MASK & (x)) +#define AGE_UNIT(x) FIELD_PREP(AGE_UNIT_MASK, x) #define MT753X_ERLCR_P(x) (0x1040 + ((x) * 0x100)) #define ERLCR_CIR_MASK GENMASK(31, 16) @@ -283,30 +286,31 @@ enum mt7530_stp_state { #define MT7530_PCR_P(x) (0x2004 + ((x) * 0x100)) #define PORT_TX_MIR BIT(9) #define PORT_RX_MIR BIT(8) -#define PORT_VLAN(x) ((x) & 0x3) +#define PCR_PORT_VLAN_MASK GENMASK(1, 0) enum mt7530_port_mode { /* Port Matrix Mode: Frames are forwarded by the PCR_MATRIX members. */ - MT7530_PORT_MATRIX_MODE = PORT_VLAN(0), + MT7530_PORT_MATRIX_MODE = 0, /* Fallback Mode: Forward received frames with ingress ports that do * not belong to the VLAN member. Frames whose VID is not listed on * the VLAN table are forwarded by the PCR_MATRIX members. */ - MT7530_PORT_FALLBACK_MODE = PORT_VLAN(1), + MT7530_PORT_FALLBACK_MODE = 1, /* Security Mode: Discard any frame due to ingress membership * violation or VID missed on the VLAN table. */ - MT7530_PORT_SECURITY_MODE = PORT_VLAN(3), + MT7530_PORT_SECURITY_MODE = 3, }; -#define PCR_MATRIX(x) (((x) & 0xff) << 16) -#define PORT_PRI(x) (((x) & 0x7) << 24) -#define EG_TAG(x) (((x) & 0x3) << 28) -#define PCR_MATRIX_MASK PCR_MATRIX(0xff) +#define PCR_MATRIX_MASK GENMASK(23, 16) +#define PCR_MATRIX(x) FIELD_PREP(PCR_MATRIX_MASK, x) +#define PORT_PRI_MASK GENMASK(26, 24) +#define PORT_PRI(x) FIELD_PREP(PORT_PRI_MASK, x) +#define EG_TAG_MASK GENMASK(29, 28) +#define EG_TAG(x) FIELD_PREP(EG_TAG_MASK, x) #define PCR_MATRIX_CLR PCR_MATRIX(0) -#define PCR_PORT_VLAN_MASK PORT_VLAN(3) /* Register for port security control */ #define MT7530_PSC_P(x) (0x200c + ((x) * 0x100)) @@ -315,10 +319,10 @@ enum mt7530_port_mode { /* Register for port vlan control */ #define MT7530_PVC_P(x) (0x2010 + ((x) * 0x100)) #define PORT_SPEC_TAG BIT(5) -#define PVC_EG_TAG(x) (((x) & 0x7) << 8) -#define PVC_EG_TAG_MASK PVC_EG_TAG(7) -#define VLAN_ATTR(x) (((x) & 0x3) << 6) -#define VLAN_ATTR_MASK VLAN_ATTR(3) +#define PVC_EG_TAG_MASK GENMASK(10, 8) +#define PVC_EG_TAG(x) FIELD_PREP(PVC_EG_TAG_MASK, x) +#define VLAN_ATTR_MASK GENMASK(7, 6) +#define VLAN_ATTR(x) FIELD_PREP(VLAN_ATTR_MASK, x) #define ACC_FRM_MASK GENMASK(1, 0) enum mt7530_vlan_port_eg_tag { @@ -338,12 +342,13 @@ enum mt7530_vlan_port_acc_frm { MT7530_VLAN_ACC_UNTAGGED = 2, }; -#define STAG_VPID (((x) & 0xffff) << 16) +#define STAG_VPID_MASK GENMASK(31, 16) +#define STAG_VPID(x) FIELD_PREP(STAG_VPID_MASK, x) /* Register for port port-and-protocol based vlan 1 control */ #define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100)) -#define G0_PORT_VID(x) (((x) & 0xfff) << 0) -#define G0_PORT_VID_MASK G0_PORT_VID(0xfff) +#define G0_PORT_VID_MASK GENMASK(11, 0) +#define G0_PORT_VID(x) FIELD_PREP(G0_PORT_VID_MASK, x) #define G0_PORT_VID_DEF G0_PORT_VID(0) /* Register for port MAC control register */ @@ -419,8 +424,8 @@ enum mt7530_vlan_port_acc_frm { #define MT7531_DIS_CLR BIT(31) #define MT7530_GMACCR 0x30e0 -#define MAX_RX_JUMBO(x) ((x) << 2) #define MAX_RX_JUMBO_MASK GENMASK(5, 2) +#define MAX_RX_JUMBO(x) FIELD_PREP(MAX_RX_JUMBO_MASK, x) #define MAX_RX_PKT_LEN_MASK GENMASK(1, 0) #define MAX_RX_PKT_LEN_1522 0x0 #define MAX_RX_PKT_LEN_1536 0x1 @@ -506,16 +511,16 @@ enum mt7530_vlan_port_acc_frm { /* Register for PHY Indirect Access Control */ #define MT7531_PHY_IAC 0x701C #define MT7531_PHY_ACS_ST BIT(31) -#define MT7531_MDIO_REG_ADDR_MASK (0x1f << 25) -#define MT7531_MDIO_PHY_ADDR_MASK (0x1f << 20) -#define MT7531_MDIO_CMD_MASK (0x3 << 18) -#define MT7531_MDIO_ST_MASK (0x3 << 16) -#define MT7531_MDIO_RW_DATA_MASK (0xffff) -#define MT7531_MDIO_REG_ADDR(x) (((x) & 0x1f) << 25) -#define MT7531_MDIO_DEV_ADDR(x) (((x) & 0x1f) << 25) -#define MT7531_MDIO_PHY_ADDR(x) (((x) & 0x1f) << 20) -#define MT7531_MDIO_CMD(x) (((x) & 0x3) << 18) -#define MT7531_MDIO_ST(x) (((x) & 0x3) << 16) +#define MT7531_MDIO_REG_ADDR_MASK GENMASK(29, 25) +#define MT7531_MDIO_PHY_ADDR_MASK GENMASK(24, 20) +#define MT7531_MDIO_CMD_MASK GENMASK(19, 18) +#define MT7531_MDIO_ST_MASK GENMASK(17, 16) +#define MT7531_MDIO_RW_DATA_MASK GENMASK(15, 0) +#define MT7531_MDIO_REG_ADDR(x) FIELD_PREP(MT7531_MDIO_REG_ADDR_MASK, x) +#define MT7531_MDIO_DEV_ADDR(x) FIELD_PREP(MT7531_MDIO_REG_ADDR_MASK, x) +#define MT7531_MDIO_PHY_ADDR(x) FIELD_PREP(MT7531_MDIO_PHY_ADDR_MASK, x) +#define MT7531_MDIO_CMD(x) FIELD_PREP(MT7531_MDIO_CMD_MASK, x) +#define MT7531_MDIO_ST(x) FIELD_PREP(MT7531_MDIO_ST_MASK, x) enum mt7531_phy_iac_cmd { MT7531_MDIO_ADDR = 0, @@ -543,14 +548,14 @@ enum mt7531_mdio_st { /* Register for RGMII clock phase */ #define MT7531_CLKGEN_CTRL 0x7500 -#define CLK_SKEW_OUT(x) (((x) & 0x3) << 8) #define CLK_SKEW_OUT_MASK GENMASK(9, 8) -#define CLK_SKEW_IN(x) (((x) & 0x3) << 6) +#define CLK_SKEW_OUT(x) FIELD_PREP(CLK_SKEW_OUT_MASK, x) #define CLK_SKEW_IN_MASK GENMASK(7, 6) +#define CLK_SKEW_IN(x) FIELD_PREP(CLK_SKEW_IN_MASK, x) #define RXCLK_NO_DELAY BIT(5) #define TXCLK_NO_REVERSE BIT(4) -#define GP_MODE(x) (((x) & 0x3) << 1) #define GP_MODE_MASK GENMASK(2, 1) +#define GP_MODE(x) FIELD_PREP(GP_MODE_MASK, x) #define GP_CLK_EN BIT(0) enum mt7531_gp_mode { @@ -600,8 +605,10 @@ enum mt7531_xtal_fsel { #define PAD_MCM_SMI_EN BIT(0) #define MT7530_IO_DRV_CR 0x7810 -#define P5_IO_CLK_DRV(x) ((x) & 0x3) -#define P5_IO_DATA_DRV(x) (((x) & 0x3) << 4) +#define P5_IO_CLK_DRV_MASK GENMASK(1, 0) +#define P5_IO_CLK_DRV(x) FIELD_PREP(P5_IO_CLK_DRV_MASK, x) +#define P5_IO_DATA_DRV_MASK GENMASK(5, 4) +#define P5_IO_DATA_DRV(x) FIELD_PREP(P5_IO_DATA_DRV_MASK, x) #define MT7531_CHIP_REV 0x781C @@ -611,15 +618,15 @@ enum mt7531_xtal_fsel { #define SW_PLLGP BIT(0) #define MT7530_P6ECR 0x7830 -#define P6_INTF_MODE_MASK 0x3 -#define P6_INTF_MODE(x) ((x) & 0x3) +#define P6_INTF_MODE_MASK GENMASK(1, 0) +#define P6_INTF_MODE(x) FIELD_PREP(P6_INTF_MODE_MASK, x) #define MT7531_PLLGP_CR0 0x78a8 #define RG_COREPLL_EN BIT(22) -#define RG_COREPLL_POSDIV_S 23 -#define RG_COREPLL_POSDIV_M 0x3800000 -#define RG_COREPLL_SDM_PCW_S 1 -#define RG_COREPLL_SDM_PCW_M 0x3ffffe +#define RG_COREPLL_POSDIV_MASK GENMASK(25, 23) +#define RG_COREPLL_POSDIV(x) FIELD_PREP(RG_COREPLL_POSDIV_MASK, x) +#define RG_COREPLL_SDM_PCW_MASK GENMASK(21, 1) +#define RG_COREPLL_SDM_PCW(x) FIELD_PREP(RG_COREPLL_SDM_PCW_MASK, x) #define RG_COREPLL_SDM_PCW_CHG BIT(0) /* Registers for RGMII and SGMII PLL clock */ @@ -630,10 +637,10 @@ enum mt7531_xtal_fsel { #define MT7530_TRGMII_RCK_CTRL 0x7a00 #define RX_RST BIT(31) #define RXC_DQSISEL BIT(30) -#define DQSI1_TAP_MASK (0x7f << 8) -#define DQSI0_TAP_MASK 0x7f -#define DQSI1_TAP(x) (((x) & 0x7f) << 8) -#define DQSI0_TAP(x) ((x) & 0x7f) +#define DQSI1_TAP_MASK GENMASK(14, 8) +#define DQSI0_TAP_MASK GENMASK(6, 0) +#define DQSI1_TAP(x) FIELD_PREP(DQSI1_TAP_MASK, x) +#define DQSI0_TAP(x) FIELD_PREP(DQSI0_TAP_MASK, x) #define MT7530_TRGMII_RCK_RTT 0x7a04 #define DQS1_GATE BIT(31) @@ -642,8 +649,8 @@ enum mt7531_xtal_fsel { #define MT7530_TRGMII_RD(x) (0x7a10 + (x) * 8) #define BSLIP_EN BIT(31) #define EDGE_CHK BIT(30) -#define RD_TAP_MASK 0x7f -#define RD_TAP(x) ((x) & 0x7f) +#define RD_TAP_MASK GENMASK(6, 0) +#define RD_TAP(x) FIELD_PREP(RD_TAP_MASK, x) #define MT7530_TRGMII_TXCTRL 0x7a40 #define TRAIN_TXEN BIT(31) @@ -651,18 +658,23 @@ enum mt7531_xtal_fsel { #define TX_RST BIT(28) #define MT7530_TRGMII_TD_ODT(i) (0x7a54 + 8 * (i)) -#define TD_DM_DRVP(x) ((x) & 0xf) -#define TD_DM_DRVN(x) (((x) & 0xf) << 4) +#define TD_DM_DRVP_MASK GENMASK(3, 0) +#define TD_DM_DRVP(x) FIELD_PREP(TD_DM_DRVP_MASK, x) +#define TD_DM_DRVN_MASK GENMASK(7, 4) +#define TD_DM_DRVN(x) FIELD_PREP(TD_DM_DRVN_MASK, x) #define MT7530_TRGMII_TCK_CTRL 0x7a78 -#define TCK_TAP(x) (((x) & 0xf) << 8) +#define TCK_TAP_MASK GENMASK(11, 8) +#define TCK_TAP(x) FIELD_PREP(TCK_TAP_MASK, x) #define MT7530_P5RGMIIRXCR 0x7b00 #define CSR_RGMII_EDGE_ALIGN BIT(8) -#define CSR_RGMII_RXC_0DEG_CFG(x) ((x) & 0xf) +#define CSR_RGMII_RXC_0DEG_CFG_MASK GENMASK(3, 0) +#define CSR_RGMII_RXC_0DEG_CFG(x) FIELD_PREP(CSR_RGMII_RXC_0DEG_CFG_MASK, x) #define MT7530_P5RGMIITXCR 0x7b04 -#define CSR_RGMII_TXC_CFG(x) ((x) & 0x1f) +#define CSR_RGMII_TXC_CFG_MASK GENMASK(4, 0) +#define CSR_RGMII_TXC_CFG(x) FIELD_PREP(CSR_RGMII_TXC_CFG_MASK, x) /* Registers for GPIO mode */ #define MT7531_GPIO_MODE0 0x7c0c @@ -671,9 +683,9 @@ enum mt7531_xtal_fsel { #define MT7531_GPIO_MODE1 0x7c10 #define MT7531_GPIO11_RG_RXD2_MASK GENMASK(15, 12) -#define MT7531_EXT_P_MDC_11 (2 << 12) +#define MT7531_EXT_P_MDC_11 FIELD_PREP(MT7531_GPIO11_RG_RXD2_MASK, 2) #define MT7531_GPIO12_RG_RXD3_MASK GENMASK(19, 16) -#define MT7531_EXT_P_MDIO_12 (2 << 16) +#define MT7531_EXT_P_MDIO_12 FIELD_PREP(MT7531_GPIO12_RG_RXD3_MASK, 2) #define MT753X_CPORT_SPTAG_CFG 0x7c10 #define CPORT_SW2FE_STAG_EN BIT(1) @@ -705,7 +717,7 @@ enum mt7531_xtal_fsel { #define MT7530_LED_GPIO_DATA 0x7d18 #define MT7530_CREV 0x7ffc -#define CHIP_NAME_SHIFT 16 +#define CHIP_NAME_MASK GENMASK(31, 16) #define MT7530_ID 0x7530 #define MT7531_CREV 0x781C @@ -717,10 +729,13 @@ enum mt7531_xtal_fsel { #define RG_SYSPLL_EN_NORMAL BIT(15) #define RG_SYSPLL_VODEN BIT(14) #define RG_SYSPLL_LF BIT(13) -#define RG_SYSPLL_RST_DLY(x) (((x) & 0x3) << 12) +#define RG_SYSPLL_RST_DLY_MASK GENMASK(13, 12) +#define RG_SYSPLL_RST_DLY(x) FIELD_PREP(RG_SYSPLL_RST_DLY_MASK, x) #define RG_SYSPLL_LVROD_EN BIT(10) -#define RG_SYSPLL_PREDIV(x) (((x) & 0x3) << 8) -#define RG_SYSPLL_POSDIV(x) (((x) & 0x3) << 5) +#define RG_SYSPLL_PREDIV_MASK GENMASK(9, 8) +#define RG_SYSPLL_PREDIV(x) FIELD_PREP(RG_SYSPLL_PREDIV_MASK, x) +#define RG_SYSPLL_POSDIV_MASK GENMASK(6, 5) +#define RG_SYSPLL_POSDIV(x) FIELD_PREP(RG_SYSPLL_POSDIV_MASK, x) #define RG_SYSPLL_FBKSEL BIT(4) #define RT_SYSPLL_EN_AFE_OLT BIT(0) @@ -732,38 +747,48 @@ enum mt7531_xtal_fsel { #define MT7531_PHY_PLL_OFF BIT(5) #define MT7531_PHY_PLL_BYPASS_MODE BIT(4) -#define MT753X_CTRL_PHY_ADDR(addr) ((addr + 1) & 0x1f) +#define MT753X_CTRL_PHY_ADDR(addr) (((addr) + 1) & (PHY_MAX_ADDR - 1)) #define CORE_PLL_GROUP5 0x404 -#define RG_LCDDS_PCW_NCPO1(x) ((x) & 0xffff) +#define RG_LCDDS_PCW_NCPO1_MASK GENMASK(15, 0) +#define RG_LCDDS_PCW_NCPO1(x) FIELD_PREP(RG_LCDDS_PCW_NCPO1_MASK, x) #define CORE_PLL_GROUP6 0x405 -#define RG_LCDDS_PCW_NCPO0(x) ((x) & 0xffff) +#define RG_LCDDS_PCW_NCPO0_MASK GENMASK(15, 0) +#define RG_LCDDS_PCW_NCPO0(x) FIELD_PREP(RG_LCDDS_PCW_NCPO0_MASK, x) #define CORE_PLL_GROUP7 0x406 #define RG_LCDDS_PWDB BIT(15) #define RG_LCDDS_ISO_EN BIT(13) -#define RG_LCCDS_C(x) (((x) & 0x7) << 4) +#define RG_LCCDS_C_MASK GENMASK(6, 4) +#define RG_LCCDS_C(x) FIELD_PREP(RG_LCCDS_C_MASK, x) #define RG_LCDDS_PCW_NCPO_CHG BIT(3) #define CORE_PLL_GROUP10 0x409 -#define RG_LCDDS_SSC_DELTA(x) ((x) & 0xfff) +#define RG_LCDDS_SSC_DELTA_MASK GENMASK(11, 0) +#define RG_LCDDS_SSC_DELTA(x) FIELD_PREP(RG_LCDDS_SSC_DELTA_MASK, x) #define CORE_PLL_GROUP11 0x40a -#define RG_LCDDS_SSC_DELTA1(x) ((x) & 0xfff) +#define RG_LCDDS_SSC_DELTA1_MASK GENMASK(11, 0) +#define RG_LCDDS_SSC_DELTA1(x) FIELD_PREP(RG_LCDDS_SSC_DELTA1_MASK, x) #define CORE_GSWPLL_GRP1 0x40d -#define RG_GSWPLL_PREDIV(x) (((x) & 0x3) << 14) -#define RG_GSWPLL_POSDIV_200M(x) (((x) & 0x3) << 12) +#define RG_GSWPLL_PREDIV_MASK GENMASK(15, 14) +#define RG_GSWPLL_PREDIV(x) FIELD_PREP(RG_GSWPLL_PREDIV_MASK, x) +#define RG_GSWPLL_POSDIV_200M_MASK GENMASK(13, 12) +#define RG_GSWPLL_POSDIV_200M(x) FIELD_PREP(RG_GSWPLL_POSDIV_200M_MASK, x) #define RG_GSWPLL_EN_PRE BIT(11) #define RG_GSWPLL_FBKSEL BIT(10) #define RG_GSWPLL_BP BIT(9) #define RG_GSWPLL_BR BIT(8) -#define RG_GSWPLL_FBKDIV_200M(x) ((x) & 0xff) +#define RG_GSWPLL_FBKDIV_200M_MASK GENMASK(7, 0) +#define RG_GSWPLL_FBKDIV_200M(x) FIELD_PREP(RG_GSWPLL_FBKDIV_200M_MASK, x) #define CORE_GSWPLL_GRP2 0x40e -#define RG_GSWPLL_POSDIV_500M(x) (((x) & 0x3) << 8) -#define RG_GSWPLL_FBKDIV_500M(x) ((x) & 0xff) +#define RG_GSWPLL_POSDIV_500M_MASK GENMASK(9, 8) +#define RG_GSWPLL_POSDIV_500M(x) FIELD_PREP(RG_GSWPLL_POSDIV_500M_MASK, x) +#define RG_GSWPLL_FBKDIV_500M_MASK GENMASK(7, 0) +#define RG_GSWPLL_FBKDIV_500M(x) FIELD_PREP(RG_GSWPLL_FBKDIV_500M_MASK, x) #define CORE_TRGMII_GSW_CLK_CG 0x410 #define REG_GSWCK_EN BIT(0) @@ -933,18 +958,6 @@ struct mt7530_hw_stats { u8 sizeof_stat; }; -struct mt7530_dummy_poll { - struct mt7530_priv *priv; - u32 reg; -}; - -static inline void INIT_MT7530_DUMMY_POLL(struct mt7530_dummy_poll *p, - struct mt7530_priv *priv, u32 reg) -{ - p->priv = priv; - p->reg = reg; -} - int mt7530_probe_common(struct mt7530_priv *priv); void mt7530_remove_common(struct mt7530_priv *priv); diff --git a/drivers/net/dsa/mv88e6060.c b/drivers/net/dsa/mv88e6060.c index 9c8ac14cd4f5..3bb485a90429 100644 --- a/drivers/net/dsa/mv88e6060.c +++ b/drivers/net/dsa/mv88e6060.c @@ -362,8 +362,9 @@ static const struct of_device_id mv88e6060_of_match[] = { { .compatible = "marvell,mv88e6060", }, - { /* sentinel */ }, + { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mv88e6060_of_match); static struct mdio_driver mv88e6060_driver = { .probe = mv88e6060_probe, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index a4a8c7e11bf4..2a975ecd79c7 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -2457,7 +2457,7 @@ static int mv88e6xxx_get_rxnfc(struct dsa_switch *ds, int port, case ETHTOOL_GRXCLSRULE: err = -ENOENT; policy = idr_find(&chip->policies, fs->location); - if (policy) { + if (policy && policy->port == port) { memcpy(fs, &policy->fs, sizeof(*fs)); err = 0; } @@ -2503,8 +2503,9 @@ static int mv88e6xxx_set_rxnfc(struct dsa_switch *ds, int port, break; case ETHTOOL_SRXCLSRLDEL: err = -ENOENT; - policy = idr_remove(&chip->policies, fs->location); - if (policy) { + policy = idr_find(&chip->policies, fs->location); + if (policy && policy->port == port) { + idr_remove(&chip->policies, fs->location); policy->action = MV88E6XXX_POLICY_ACTION_NORMAL; err = mv88e6xxx_policy_apply(chip, port, policy); devm_kfree(chip->dev, policy); @@ -7581,7 +7582,7 @@ static const struct of_device_id mv88e6xxx_of_match[] = { .compatible = "marvell,mv88e6250", .data = &mv88e6xxx_table[MV88E6250], }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mv88e6xxx_of_match); diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index 4c928983b862..07227ae8bc04 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -129,13 +129,16 @@ err: return ret; } -static void +static int qca8k_mii_write32(struct mii_bus *bus, int phy_id, u32 regnum, u32 val) { - if (qca8k_mii_write_lo(bus, phy_id, regnum, val) < 0) - return; + int ret; - qca8k_mii_write_hi(bus, phy_id, regnum + 1, val); + ret = qca8k_mii_write_lo(bus, phy_id, regnum, val); + if (ret < 0) + return ret; + + return qca8k_mii_write_hi(bus, phy_id, regnum + 1, val); } static int @@ -150,6 +153,8 @@ qca8k_set_page(struct qca8k_priv *priv, u16 page) ret = bus->write(bus, 0x18, 0, page); if (ret < 0) { + /* The switch may or may not have switched pages. */ + *cached_page = 0xffff; dev_err_ratelimited(&bus->dev, "failed to set qca8k page\n"); return ret; @@ -462,7 +467,7 @@ qca8k_write_mii(struct qca8k_priv *priv, uint32_t reg, uint32_t val) if (ret < 0) goto exit; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + ret = qca8k_mii_write32(bus, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -492,7 +497,7 @@ qca8k_regmap_update_bits_mii(struct qca8k_priv *priv, uint32_t reg, val &= ~mask; val |= write_val; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + ret = qca8k_mii_write32(bus, 0x10 | r2, r1, val); exit: mutex_unlock(&bus->mdio_lock); @@ -723,12 +728,15 @@ qca8k_phy_eth_command(struct qca8k_priv *priv, bool read, int phy, } ret = read_poll_timeout(qca8k_phy_eth_busy_wait, ret1, - !(val & QCA8K_MDIO_MASTER_BUSY), 0, + ret1 < 0 || !(val & QCA8K_MDIO_MASTER_BUSY), 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, mgmt_eth_data, read_skb, &val); - if (ret < 0 && ret1 < 0) { + if (ret1 < 0) ret = ret1; + + if (ret < 0) { + kfree_skb(read_skb); goto exit; } @@ -794,19 +802,18 @@ static int qca8k_mdio_busy_wait(struct mii_bus *bus, u32 reg, u32 mask) { u16 r1, r2, page; - u32 val; int ret, ret1; + u32 val; qca8k_split_addr(reg, &r1, &r2, &page); - ret = read_poll_timeout(qca8k_mii_read_hi, ret1, !(val & mask), 0, + ret = read_poll_timeout(qca8k_mii_read_hi, ret1, + ret1 < 0 || !(val & mask), 0, QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC, false, bus, 0x10 | r2, r1 + 1, &val); - /* Check if qca8k_read has failed for a different reason - * before returnting -ETIMEDOUT - */ - if (ret < 0 && ret1 < 0) + /* Preserve an MDIO read error instead of treating it as ready. */ + if (ret1 < 0) return ret1; return ret; @@ -817,8 +824,8 @@ qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) { struct mii_bus *bus = priv->bus; u16 r1, r2, page; + int ret, ret1; u32 val; - int ret; if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) return -EINVAL; @@ -834,17 +841,22 @@ qca8k_mdio_write(struct qca8k_priv *priv, int phy, int regnum, u16 data) ret = qca8k_set_page(priv, page); if (ret) - goto exit; + goto unlock; - qca8k_mii_write32(bus, 0x10 | r2, r1, val); + ret = qca8k_mii_write32(bus, 0x10 | r2, r1, val); + if (ret < 0) + goto exit; ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0); + ret1 = qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0); + if (!ret) + ret = ret1; +unlock: mutex_unlock(&bus->mdio_lock); return ret; @@ -855,8 +867,8 @@ qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) { struct mii_bus *bus = priv->bus; u16 r1, r2, page; + int ret, ret1; u32 val; - int ret; if (regnum >= QCA8K_MDIO_MASTER_MAX_REG) return -EINVAL; @@ -871,9 +883,11 @@ qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) ret = qca8k_set_page(priv, page); if (ret) - goto exit; + goto unlock; - qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, val); + ret = qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, val); + if (ret < 0) + goto exit; ret = qca8k_mdio_busy_wait(bus, QCA8K_MDIO_MASTER_CTRL, QCA8K_MDIO_MASTER_BUSY); @@ -884,8 +898,11 @@ qca8k_mdio_read(struct qca8k_priv *priv, int phy, int regnum) exit: /* even if the busy_wait timeouts try to clear the MASTER_EN */ - qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0); + ret1 = qca8k_mii_write_hi(bus, 0x10 | r2, r1 + 1, 0); + if (!ret) + ret = ret1; +unlock: mutex_unlock(&bus->mdio_lock); if (ret >= 0) @@ -919,12 +936,7 @@ qca8k_internal_mdio_read(struct mii_bus *slave_bus, int phy, int regnum) if (ret >= 0) return ret; - ret = qca8k_mdio_read(priv, phy, regnum); - - if (ret < 0) - return 0xffff; - - return ret; + return qca8k_mdio_read(priv, phy, regnum); } static int @@ -2215,7 +2227,7 @@ static const struct of_device_id qca8k_of_match[] = { { .compatible = "qca,qca8328", .data = &qca8328 }, { .compatible = "qca,qca8334", .data = &qca833x }, { .compatible = "qca,qca8337", .data = &qca833x }, - { /* sentinel */ }, + { /* sentinel */ } }; static struct mdio_driver qca8kmdio_driver = { diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c index 13005f10edb7..ab978cc8367f 100644 --- a/drivers/net/dsa/qca/qca8k-common.c +++ b/drivers/net/dsa/qca/qca8k-common.c @@ -471,10 +471,7 @@ void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) if (port > 0 && port < 6) mask |= QCA8K_PORT_STATUS_LINK_AUTO; - if (enable) - regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); - else - regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); + regmap_assign_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask, enable); } void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, @@ -562,14 +559,8 @@ static int qca8k_port_configure_learning(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = ds->priv; - if (learning) - return regmap_set_bits(priv->regmap, - QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_LEARN); - else - return regmap_clear_bits(priv->regmap, - QCA8K_PORT_LOOKUP_CTRL(port), - QCA8K_PORT_LOOKUP_LEARN); + return regmap_assign_bits(priv->regmap, QCA8K_PORT_LOOKUP_CTRL(port), + QCA8K_PORT_LOOKUP_LEARN, learning); } void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) diff --git a/drivers/net/dsa/realtek/Makefile b/drivers/net/dsa/realtek/Makefile index 72bb42e80c95..0a94a619b2c1 100644 --- a/drivers/net/dsa/realtek/Makefile +++ b/drivers/net/dsa/realtek/Makefile @@ -1,23 +1,15 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_NET_DSA_REALTEK) += realtek_dsa.o -realtek_dsa-objs := rtl83xx.o - -ifdef CONFIG_NET_DSA_REALTEK_MDIO -realtek_dsa-objs += realtek-mdio.o -endif - -ifdef CONFIG_NET_DSA_REALTEK_SMI -realtek_dsa-objs += realtek-smi.o -endif +realtek_dsa-y := rtl83xx.o +realtek_dsa-$(CONFIG_NET_DSA_REALTEK_MDIO) += realtek-mdio.o +realtek_dsa-$(CONFIG_NET_DSA_REALTEK_SMI) += realtek-smi.o obj-$(CONFIG_NET_DSA_REALTEK_RTL8366RB) += rtl8366.o -rtl8366-objs := rtl8366-core.o rtl8366rb.o -ifdef CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS -rtl8366-objs += rtl8366rb-leds.o -endif +rtl8366-y := rtl8366-core.o rtl8366rb.o +rtl8366-$(CONFIG_NET_DSA_REALTEK_RTL8366RB_LEDS) += rtl8366rb-leds.o + obj-$(CONFIG_NET_DSA_REALTEK_RTL8365MB) += rtl8365mb.o -rtl8365mb-objs := rtl8365mb_main.o \ - rtl8365mb_table.o \ - rtl8365mb_vlan.o \ - rtl8365mb_l2.o \ -# end of rtl8365mb-objs +rtl8365mb-y := rtl8365mb_main.o \ + rtl8365mb_table.o \ + rtl8365mb_vlan.o \ + rtl8365mb_l2.o diff --git a/drivers/net/dsa/realtek/realtek.h b/drivers/net/dsa/realtek/realtek.h index 6e0148cee8d8..33f3b4c249fc 100644 --- a/drivers/net/dsa/realtek/realtek.h +++ b/drivers/net/dsa/realtek/realtek.h @@ -97,7 +97,6 @@ struct realtek_priv { int vlan_enabled; int vlan4k_enabled; - char buf[4096]; void *chip_data; /* Per-chip extra variant data */ }; @@ -156,6 +155,10 @@ struct realtek_ops { int (*phy_read)(struct realtek_priv *priv, int phy, int regnum); int (*phy_write)(struct realtek_priv *priv, int phy, int regnum, u16 val); + int (*phy_read_c45)(struct realtek_priv *priv, int phy, int devad, + int regnum); + int (*phy_write_c45)(struct realtek_priv *priv, int phy, int devad, + int regnum, u16 val); }; struct realtek_variant { @@ -166,6 +169,9 @@ struct realtek_variant { u8 cmd_read; u8 cmd_write; size_t chip_data_sz; + /* Regulator supplies to enable at probe, or NULL */ + const char *const *supplies; + int num_supplies; }; /* RTL8366 library helpers */ diff --git a/drivers/net/dsa/realtek/rtl8365mb_main.c b/drivers/net/dsa/realtek/rtl8365mb_main.c index 728231d8f94c..da06f952a053 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_main.c +++ b/drivers/net/dsa/realtek/rtl8365mb_main.c @@ -101,6 +101,7 @@ #include <linux/regmap.h> #include <linux/if_bridge.h> #include <linux/if_vlan.h> +#include <linux/mdio.h> #include <linux/phylink.h> #include "realtek.h" @@ -131,32 +132,32 @@ #define RTL8365MB_MAGIC_VALUE 0x0249 /* Chip reset register */ -#define RTL8365MB_CHIP_RESET_REG 0x1322 -#define RTL8365MB_CHIP_RESET_DW8051_MASK 0x0010 -#define RTL8365MB_CHIP_RESET_SW_MASK 0x0002 -#define RTL8365MB_CHIP_RESET_HW_MASK 0x0001 +#define RTL8365MB_CHIP_RESET_REG 0x1322 +#define RTL8365MB_CHIP_RESET_DW8051_MASK BIT(4) +#define RTL8365MB_CHIP_RESET_SW_MASK BIT(1) +#define RTL8365MB_CHIP_RESET_HW_MASK BIT(0) /* Interrupt polarity register */ #define RTL8365MB_INTR_POLARITY_REG 0x1100 -#define RTL8365MB_INTR_POLARITY_MASK 0x0001 +#define RTL8365MB_INTR_POLARITY_MASK BIT(0) #define RTL8365MB_INTR_POLARITY_HIGH 0 #define RTL8365MB_INTR_POLARITY_LOW 1 /* Interrupt control/status register - enable/check specific interrupt types */ #define RTL8365MB_INTR_CTRL_REG 0x1101 -#define RTL8365MB_INTR_STATUS_REG 0x1102 -#define RTL8365MB_INTR_SLIENT_START_2_MASK 0x1000 -#define RTL8365MB_INTR_SLIENT_START_MASK 0x0800 -#define RTL8365MB_INTR_ACL_ACTION_MASK 0x0200 -#define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK 0x0100 -#define RTL8365MB_INTR_INTERRUPT_8051_MASK 0x0080 -#define RTL8365MB_INTR_LOOP_DETECTION_MASK 0x0040 -#define RTL8365MB_INTR_GREEN_TIMER_MASK 0x0020 -#define RTL8365MB_INTR_SPECIAL_CONGEST_MASK 0x0010 -#define RTL8365MB_INTR_SPEED_CHANGE_MASK 0x0008 -#define RTL8365MB_INTR_LEARN_OVER_MASK 0x0004 -#define RTL8365MB_INTR_METER_EXCEEDED_MASK 0x0002 -#define RTL8365MB_INTR_LINK_CHANGE_MASK 0x0001 +#define RTL8365MB_INTR_STATUS_REG 0x1102 +#define RTL8365MB_INTR_SLIENT_START_2_MASK BIT(12) +#define RTL8365MB_INTR_SLIENT_START_MASK BIT(11) +#define RTL8365MB_INTR_ACL_ACTION_MASK BIT(9) +#define RTL8365MB_INTR_CABLE_DIAG_FIN_MASK BIT(8) +#define RTL8365MB_INTR_INTERRUPT_8051_MASK BIT(7) +#define RTL8365MB_INTR_LOOP_DETECTION_MASK BIT(6) +#define RTL8365MB_INTR_GREEN_TIMER_MASK BIT(5) +#define RTL8365MB_INTR_SPECIAL_CONGEST_MASK BIT(4) +#define RTL8365MB_INTR_SPEED_CHANGE_MASK BIT(3) +#define RTL8365MB_INTR_LEARN_OVER_MASK BIT(2) +#define RTL8365MB_INTR_METER_EXCEEDED_MASK BIT(1) +#define RTL8365MB_INTR_LINK_CHANGE_MASK BIT(0) #define RTL8365MB_INTR_ALL_MASK \ (RTL8365MB_INTR_SLIENT_START_2_MASK | \ RTL8365MB_INTR_SLIENT_START_MASK | \ @@ -171,20 +172,22 @@ RTL8365MB_INTR_METER_EXCEEDED_MASK | \ RTL8365MB_INTR_LINK_CHANGE_MASK) +#define RTL8365MB_PORT_MASK GENMASK(10, 0) + /* Per-port interrupt type status registers */ #define RTL8365MB_PORT_LINKDOWN_IND_REG 0x1106 -#define RTL8365MB_PORT_LINKDOWN_IND_MASK 0x07FF +#define RTL8365MB_PORT_LINKDOWN_IND_MASK RTL8365MB_PORT_MASK #define RTL8365MB_PORT_LINKUP_IND_REG 0x1107 -#define RTL8365MB_PORT_LINKUP_IND_MASK 0x07FF +#define RTL8365MB_PORT_LINKUP_IND_MASK RTL8365MB_PORT_MASK /* PHY indirect access registers */ #define RTL8365MB_INDIRECT_ACCESS_CTRL_REG 0x1F00 -#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK 0x0002 -#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 -#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 -#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK 0x0001 -#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK BIT(1) +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ 0 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE 1 +#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK BIT(0) +#define RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE 1 #define RTL8365MB_INDIRECT_ACCESS_STATUS_REG 0x1F01 #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG 0x1F02 #define RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK GENMASK(4, 0) @@ -196,12 +199,24 @@ /* PHY OCP address prefix register */ #define RTL8365MB_GPHY_OCP_MSB_0_REG 0x1D15 -#define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK 0x0FC0 -#define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK 0xFC00 +#define RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK GENMASK(11, 6) +#define RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK GENMASK(15, 10) + +/* The full 16-bit OCP address is split across two registers: bits [15:10] are + * the prefix (RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK above), and bits [9:1] go into + * the ADDRESS register as two fields, [5:1] and [9:6]. Bit 0 is always 0 - PHY + * OCP registers are 2-byte aligned. + */ +#define RTL8365MB_PHY_OCP_ADDR_5_1_MASK GENMASK(5, 1) +#define RTL8365MB_PHY_OCP_ADDR_9_6_MASK GENMASK(9, 6) /* The PHY OCP addresses of PHY registers 0~31 start here */ #define RTL8365MB_PHY_OCP_ADDR_PHYREG_BASE 0xA400 +#define RTL8365MB_PHY_OCP_ADDR_EEE_ABLE 0xA5C4 +#define RTL8365MB_PHY_OCP_ADDR_EEE_ADV 0xA5D0 +#define RTL8365MB_PHY_OCP_ADDR_EEE_LPABLE 0xA5D2 + /* External interface port mode values - used in DIGITAL_INTERFACE_SELECT */ #define RTL8365MB_EXT_PORT_MODE_DISABLE 0 #define RTL8365MB_EXT_PORT_MODE_RGMII 1 @@ -239,8 +254,8 @@ (_extint) == 1 ? RTL8365MB_EXT_RGMXF_REG1 : \ (_extint) == 2 ? RTL8365MB_EXT_RGMXF_REG2 : \ 0x0) -#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK 0x0007 -#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK 0x0008 +#define RTL8365MB_EXT_RGMXF_RXDELAY_MASK GENMASK(2, 0) +#define RTL8365MB_EXT_RGMXF_TXDELAY_MASK BIT(3) /* External interface line rate bypass register - one bit per external * interface, indexed by the external port number with port 5 (the first @@ -258,28 +273,28 @@ */ #define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL0_REG 0x00CF #define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_REG 0x00D0 -#define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_MASK 0x0007 +#define RTL8365MB_INGRESSBW_PORT6_RATE_CTRL1_MASK GENMASK(2, 0) #define RTL8365MB_PORT6_EGRESSBW_CTRL0_REG 0x0398 #define RTL8365MB_PORT6_EGRESSBW_CTRL1_REG 0x0399 -#define RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK 0x0007 +#define RTL8365MB_PORT6_EGRESSBW_CTRL1_MASK GENMASK(2, 0) /* SerDes indirect access registers */ #define RTL8365MB_SDS_INDACS_CMD_REG 0x6600 -#define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK 0x0100 -#define RTL8365MB_SDS_INDACS_CMD_RUN_MASK 0x0080 -#define RTL8365MB_SDS_INDACS_CMD_WR_MASK 0x0040 +#define RTL8365MB_SDS_INDACS_CMD_BUSY_MASK BIT(8) +#define RTL8365MB_SDS_INDACS_CMD_RUN_MASK BIT(7) +#define RTL8365MB_SDS_INDACS_CMD_WR_MASK BIT(6) #define RTL8365MB_SDS_INDACS_ADR_REG 0x6601 #define RTL8365MB_SDS_INDACS_DATA_REG 0x6602 /* SerDes miscellaneous configuration register */ #define RTL8365MB_SDS_MISC_REG 0x1D11 -#define RTL8365MB_SDS_MISC_SGMII_RXFC_MASK 0x4000 -#define RTL8365MB_SDS_MISC_SGMII_TXFC_MASK 0x2000 -#define RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK 0x0800 -#define RTL8365MB_SDS_MISC_SGMII_FDUP_MASK 0x0400 -#define RTL8365MB_SDS_MISC_SGMII_LINK_MASK 0x0200 -#define RTL8365MB_SDS_MISC_SGMII_SPD_MASK 0x0180 -#define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK 0x0040 +#define RTL8365MB_SDS_MISC_SGMII_RXFC_MASK BIT(14) +#define RTL8365MB_SDS_MISC_SGMII_TXFC_MASK BIT(13) +#define RTL8365MB_SDS_MISC_MAC8_SEL_HSGMII_MASK BIT(11) +#define RTL8365MB_SDS_MISC_SGMII_FDUP_MASK BIT(10) +#define RTL8365MB_SDS_MISC_SGMII_LINK_MASK BIT(9) +#define RTL8365MB_SDS_MISC_SGMII_SPD_MASK GENMASK(8, 7) +#define RTL8365MB_SDS_MISC_MAC8_SEL_SGMII_MASK BIT(6) /* SerDes internal registers, accessed via the SDS_INDACS registers. The BMCR * data path reset holds BMCR_ANENABLE | BMCR_ISOLATE while toggling the @@ -290,12 +305,12 @@ #define RTL8365MB_SDS_BMCR_DPRST_PHASE1 (BMCR_ANENABLE | BMCR_ISOLATE | 0x1) #define RTL8365MB_SDS_BMCR_DPRST_PHASE2 (BMCR_ANENABLE | BMCR_ISOLATE | 0x3) #define RTL8365MB_SDS_REG_NWAY 0x0002 -#define RTL8365MB_SDS_NWAY_EN_MASK 0x0200 -#define RTL8365MB_SDS_NWAY_RESTART_MASK 0x0100 +#define RTL8365MB_SDS_NWAY_EN_MASK BIT(9) +#define RTL8365MB_SDS_NWAY_RESTART_MASK BIT(8) #define RTL8365MB_SDS_REG_RESET 0x0003 #define RTL8365MB_SDS_RESET_DEASSERT 0x7106 #define RTL8365MB_SDS_REG_LINK_STATUS 0x003d -#define RTL8365MB_SDS_LINK_STATUS_LINK_MASK 0x0010 +#define RTL8365MB_SDS_LINK_STATUS_LINK_MASK BIT(4) /* The embedded SerDes can only be muxed to external interface 1 (MAC8), * which is port 6. @@ -322,7 +337,7 @@ * the firmware would otherwise do. */ #define RTL8365MB_MISC_CFG0_REG 0x130C -#define RTL8365MB_MISC_CFG0_DW8051_EN_MASK 0x0020 +#define RTL8365MB_MISC_CFG0_DW8051_EN_MASK BIT(5) /* External interface port speed values - used in DIGITAL_INTERFACE_FORCE */ #define RTL8365MB_PORT_SPEED_10M 0 @@ -338,31 +353,31 @@ (_extint) == 1 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG1 : \ (_extint) == 2 ? RTL8365MB_DIGITAL_INTERFACE_FORCE_REG2 : \ 0x0) -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK 0x1000 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK 0x0080 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK 0x0040 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK 0x0020 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK 0x0010 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK 0x0004 -#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK 0x0003 +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_EN_MASK BIT(12) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_NWAY_MASK BIT(7) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_TXPAUSE_MASK BIT(6) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_RXPAUSE_MASK BIT(5) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_LINK_MASK BIT(4) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_DUPLEX_MASK BIT(2) +#define RTL8365MB_DIGITAL_INTERFACE_FORCE_SPEED_MASK GENMASK(1, 0) /* CPU port mask register - controls which ports are treated as CPU ports */ #define RTL8365MB_CPU_PORT_MASK_REG 0x1219 -#define RTL8365MB_CPU_PORT_MASK_MASK 0x07FF +#define RTL8365MB_CPU_PORT_MASK_MASK RTL8365MB_PORT_MASK /* CPU control register */ #define RTL8365MB_CPU_CTRL_REG 0x121A -#define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK 0x0400 -#define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK 0x0200 -#define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK 0x0080 -#define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK 0x0040 -#define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK 0x0038 -#define RTL8365MB_CPU_CTRL_INSERTMODE_MASK 0x0006 -#define RTL8365MB_CPU_CTRL_EN_MASK 0x0001 +#define RTL8365MB_CPU_CTRL_TRAP_PORT_EXT_MASK BIT(10) +#define RTL8365MB_CPU_CTRL_TAG_FORMAT_MASK BIT(9) +#define RTL8365MB_CPU_CTRL_RXBYTECOUNT_MASK BIT(7) +#define RTL8365MB_CPU_CTRL_TAG_POSITION_MASK BIT(6) +#define RTL8365MB_CPU_CTRL_TRAP_PORT_MASK GENMASK(5, 3) +#define RTL8365MB_CPU_CTRL_INSERTMODE_MASK GENMASK(2, 1) +#define RTL8365MB_CPU_CTRL_EN_MASK BIT(0) /* Maximum packet length register */ #define RTL8365MB_CFG0_MAX_LEN_REG 0x088C -#define RTL8365MB_CFG0_MAX_LEN_MASK 0x3FFF +#define RTL8365MB_CFG0_MAX_LEN_MASK GENMASK(13, 0) #define RTL8365MB_CFG0_MAX_LEN_MAX 0x3FFF /* Port learning limit registers */ @@ -374,7 +389,7 @@ #define RTL8365MB_PORT_ISOLATION_REG_BASE 0x08A2 #define RTL8365MB_PORT_ISOLATION_REG(_physport) \ (RTL8365MB_PORT_ISOLATION_REG_BASE + (_physport)) -#define RTL8365MB_PORT_ISOLATION_MASK 0x07FF +#define RTL8365MB_PORT_ISOLATION_MASK RTL8365MB_PORT_MASK /* Extended filter ID registers - used to key forwarding database with IVL */ #define RTL8365MB_EFID_MASK GENMASK(2, 0) @@ -394,16 +409,16 @@ (0x3 << RTL8365MB_MSTI_CTRL_PORT_STATE_OFFSET((_physport))) /* Unknown unicast DA flooding port mask */ -#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890 -#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK 0x07FF +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_REG 0x0890 +#define RTL8365MB_UNKNOWN_UNICAST_FLOODING_PMASK_MASK RTL8365MB_PORT_MASK /* Unknown multicast DA flooding port mask */ -#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891 -#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK 0x07FF +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_REG 0x0891 +#define RTL8365MB_UNKNOWN_MULTICAST_FLOODING_PMASK_MASK RTL8365MB_PORT_MASK /* Broadcast flooding port mask */ -#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892 -#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK 0x07FF +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_REG 0x0892 +#define RTL8365MB_UNKNOWN_BROADCAST_FLOODING_PMASK_MASK RTL8365MB_PORT_MASK #define RTL8365MB_SUPPORTED_BRIDGE_FLAGS \ (BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD) @@ -412,19 +427,19 @@ #define RTL8365MB_PORT_MISC_CFG_REG_BASE 0x000E #define RTL8365MB_PORT_MISC_CFG_REG(_p) \ (RTL8365MB_PORT_MISC_CFG_REG_BASE + ((_p) << 5)) -#define RTL8365MB_PORT_MISC_CFG_SMALL_TAG_IPG_MASK 0x8000 -#define RTL8365MB_PORT_MISC_CFG_TX_ITFSP_MODE_MASK 0x4000 -#define RTL8365MB_PORT_MISC_CFG_FLOWCTRL_INDEP_MASK 0x2000 -#define RTL8365MB_PORT_MISC_CFG_DOT1Q_REMARK_ENABLE_MASK 0x1000 -#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_FLOWCTRL_MASK 0x0800 -#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_IFG_MASK 0x0400 -#define RTL8365MB_PORT_MISC_CFG_RX_SPC_MASK 0x0200 -#define RTL8365MB_PORT_MISC_CFG_CRC_SKIP_MASK 0x0100 -#define RTL8365MB_PORT_MISC_CFG_PKTGEN_TX_FIRST_MASK 0x0080 -#define RTL8365MB_PORT_MISC_CFG_MAC_LOOPBACK_MASK 0x0040 +#define RTL8365MB_PORT_MISC_CFG_SMALL_TAG_IPG_MASK BIT(15) +#define RTL8365MB_PORT_MISC_CFG_TX_ITFSP_MODE_MASK BIT(14) +#define RTL8365MB_PORT_MISC_CFG_FLOWCTRL_INDEP_MASK BIT(13) +#define RTL8365MB_PORT_MISC_CFG_DOT1Q_REMARK_ENABLE_MASK BIT(12) +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_FLOWCTRL_MASK BIT(11) +#define RTL8365MB_PORT_MISC_CFG_INGRESSBW_IFG_MASK BIT(10) +#define RTL8365MB_PORT_MISC_CFG_RX_SPC_MASK BIT(9) +#define RTL8365MB_PORT_MISC_CFG_CRC_SKIP_MASK BIT(8) +#define RTL8365MB_PORT_MISC_CFG_PKTGEN_TX_FIRST_MASK BIT(7) +#define RTL8365MB_PORT_MISC_CFG_MAC_LOOPBACK_MASK BIT(6) /* See &rtl8365mb_vlan_egress_mode */ -#define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK 0x0030 -#define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK 0x000F +#define RTL8365MB_PORT_MISC_CFG_VLAN_EGRESS_MODE_MASK GENMASK(5, 4) +#define RTL8365MB_PORT_MISC_CFG_CONGESTION_SUSTAIN_TIME_MASK GENMASK(3, 0) /** * enum rtl8365mb_vlan_egress_mode - port VLAN egress mode @@ -446,7 +461,7 @@ enum rtl8365mb_vlan_egress_mode { /* VLAN control register */ #define RTL8365MB_VLAN_CTRL_REG 0x07A8 -#define RTL8365MB_VLAN_CTRL_EN_MASK 0x0001 +#define RTL8365MB_VLAN_CTRL_EN_MASK BIT(0) /* VLAN ingress filter register */ #define RTL8365MB_VLAN_INGRESS_REG 0x07A9 @@ -470,8 +485,8 @@ enum rtl8365mb_vlan_egress_mode { (((RTL8365MB_MIB_ADDRESS_PORT_OFFSET) * (_p) + (_x)) >> 2) #define RTL8365MB_MIB_CTRL0_REG 0x1005 -#define RTL8365MB_MIB_CTRL0_RESET_MASK 0x0002 -#define RTL8365MB_MIB_CTRL0_BUSY_MASK 0x0001 +#define RTL8365MB_MIB_CTRL0_RESET_MASK BIT(1) +#define RTL8365MB_MIB_CTRL0_BUSY_MASK BIT(0) /* The DSA callback .get_stats64 runs in atomic context, so we are not allowed * to block. On the other hand, accessing MIB counters absolutely requires us to @@ -871,6 +886,8 @@ static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, u32 ocp_addr) { + u16 ocp_addr_lo = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_5_1_MASK, ocp_addr); + u16 ocp_addr_hi = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_9_6_MASK, ocp_addr); u32 val; int ret; @@ -887,9 +904,9 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, val = RTL8365MB_PHY_BASE; val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_PHYNUM_MASK, phy); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_5_1_MASK, - ocp_addr >> 1); + ocp_addr_lo); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, - ocp_addr >> 6); + ocp_addr_hi); ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) @@ -1040,6 +1057,66 @@ static int rtl8365mb_phy_write(struct realtek_priv *priv, int phy, int regnum, return 0; } +static int rtl8365mb_phy_read_c45(struct realtek_priv *priv, int phy, int devad, + int regnum) +{ + u32 ocp_addr; + u16 val; + int ret; + + if (phy > RTL8365MB_PHYADDRMAX) + return -EINVAL; + + if (devad == MDIO_MMD_PCS && regnum == MDIO_PCS_EEE_ABLE) + ocp_addr = RTL8365MB_PHY_OCP_ADDR_EEE_ABLE; + else if (devad == MDIO_MMD_AN && regnum == MDIO_AN_EEE_ADV) + ocp_addr = RTL8365MB_PHY_OCP_ADDR_EEE_ADV; + else if (devad == MDIO_MMD_AN && regnum == MDIO_AN_EEE_LPABLE) + ocp_addr = RTL8365MB_PHY_OCP_ADDR_EEE_LPABLE; + else + /* Only the EEE registers are mapped; others read as 0, as the + * hardware does, so the generic MMD code is not tripped up by + * an error. + */ + return 0; + + ret = rtl8365mb_phy_ocp_read(priv, phy, ocp_addr, &val); + if (ret) { + dev_err(priv->dev, + "failed to read PHY%d OCP %04x, ret %d\n", phy, ocp_addr, + ret); + return ret; + } + + return val; +} + +static int rtl8365mb_phy_write_c45(struct realtek_priv *priv, int phy, + int devad, int regnum, u16 val) +{ + int ret; + + if (phy > RTL8365MB_PHYADDRMAX) + return -EINVAL; + + /* Only the EEE advertisement register is writable; writes to other + * registers are ignored, as the hardware does. + */ + if (devad != MDIO_MMD_AN || regnum != MDIO_AN_EEE_ADV) + return 0; + + ret = rtl8365mb_phy_ocp_write(priv, phy, RTL8365MB_PHY_OCP_ADDR_EEE_ADV, + val); + if (ret) { + dev_err(priv->dev, + "failed to write PHY%d OCP %04x, ret %d\n", phy, + RTL8365MB_PHY_OCP_ADDR_EEE_ADV, ret); + return ret; + } + + return 0; +} + static const struct rtl8365mb_extint * rtl8365mb_get_port_extint(struct realtek_priv *priv, int port) { @@ -1639,6 +1716,14 @@ static void rtl8365mb_phylink_get_caps(struct dsa_switch *ds, int port, */ __set_bit(PHY_INTERFACE_MODE_GMII, config->supported_interfaces); + + /* Integrated PHYs support EEE at 100M/1G; the hardware manages + * LPI on its own, so just advertise LPI awareness to phylink. + */ + memcpy(config->lpi_interfaces, config->supported_interfaces, + sizeof(config->lpi_interfaces)); + config->lpi_capabilities = MAC_100FD | MAC_1000FD; + config->eee_enabled_default = true; return; } @@ -3275,18 +3360,66 @@ static int rtl8365mb_detect(struct realtek_priv *priv) return 0; } +static int rtl8365mb_phylink_mac_enable_tx_lpi(struct phylink_config *config, + u32 timer, bool tx_clock_stop) +{ + /* The hardware manages LPI itself; there is no MAC-level LPI control. + * This callback only signals LPI awareness to phylink. + */ + return 0; +} + +static void rtl8365mb_phylink_mac_disable_tx_lpi(struct phylink_config *config) +{ +} + static const struct phylink_mac_ops rtl8365mb_phylink_mac_ops = { .mac_select_pcs = rtl8365mb_phylink_mac_select_pcs, .mac_config = rtl8365mb_phylink_mac_config, .mac_link_down = rtl8365mb_phylink_mac_link_down, .mac_link_up = rtl8365mb_phylink_mac_link_up, + .mac_enable_tx_lpi = rtl8365mb_phylink_mac_enable_tx_lpi, + .mac_disable_tx_lpi = rtl8365mb_phylink_mac_disable_tx_lpi, }; +static bool rtl8365mb_support_eee(struct dsa_switch *ds, int port) +{ + /* Only integrated-PHY ports support EEE, not the external RGMII ports. */ + return !rtl8365mb_get_port_extint(ds->priv, port); +} + +static int rtl8365mb_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_keee *e) +{ + struct realtek_priv *priv = ds->priv; + + /* The only LPI timing control (tx_lpi_timer) is a single global + * per-speed register shared by all ports, so it cannot be set from this + * per-port callback; leave it at its reset default. Per-port EEE is + * driven through the PHY advertisement. Reject the per-port TX LPI knobs + * rather than silently ignoring them. + */ + if (!e->tx_lpi_enabled) { + dev_err(priv->dev, "disabling EEE TX LPI is not supported\n"); + return -EOPNOTSUPP; + } + + if (e->tx_lpi_timer) { + dev_err(priv->dev, + "setting the EEE TX LPI timer is not supported\n"); + return -EOPNOTSUPP; + } + + return 0; +} + static const struct dsa_switch_ops rtl8365mb_switch_ops = { .get_tag_protocol = rtl8365mb_get_tag_protocol, .change_tag_protocol = rtl8365mb_change_tag_protocol, .setup = rtl8365mb_setup, .teardown = rtl8365mb_teardown, + .support_eee = rtl8365mb_support_eee, + .set_mac_eee = rtl8365mb_set_mac_eee, .phylink_get_caps = rtl8365mb_phylink_get_caps, .port_bridge_join = rtl83xx_port_bridge_join, .port_bridge_leave = rtl83xx_port_bridge_leave, @@ -3332,6 +3465,12 @@ static const struct realtek_ops rtl8365mb_ops = { .l2_flush = rtl8365mb_l2_flush, .phy_read = rtl8365mb_phy_read, .phy_write = rtl8365mb_phy_write, + .phy_read_c45 = rtl8365mb_phy_read_c45, + .phy_write_c45 = rtl8365mb_phy_write_c45, +}; + +static const char *const rtl8365mb_supplies[] = { + "avddh", "avddl", "dvddio", "dvddio1", "dvddl", "pllvddl", }; const struct realtek_variant rtl8365mb_variant = { @@ -3342,11 +3481,13 @@ const struct realtek_variant rtl8365mb_variant = { .cmd_read = 0xb9, .cmd_write = 0xb8, .chip_data_sz = sizeof(struct rtl8365mb), + .supplies = rtl8365mb_supplies, + .num_supplies = ARRAY_SIZE(rtl8365mb_supplies), }; static const struct of_device_id rtl8365mb_of_match[] = { { .compatible = "realtek,rtl8365mb", .data = &rtl8365mb_variant, }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rtl8365mb_of_match); diff --git a/drivers/net/dsa/realtek/rtl8365mb_vlan.c b/drivers/net/dsa/realtek/rtl8365mb_vlan.c index 64dd8d90a5ee..8d49ffadce92 100644 --- a/drivers/net/dsa/realtek/rtl8365mb_vlan.c +++ b/drivers/net/dsa/realtek/rtl8365mb_vlan.c @@ -106,8 +106,8 @@ #define RTL8365MB_VLAN_PVID_CTRL_BASE 0x0700 #define RTL8365MB_VLAN_PVID_CTRL_REG(_p) \ (RTL8365MB_VLAN_PVID_CTRL_BASE + ((_p) >> 1)) -#define RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK 0x001F -#define RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK 0x1F00 +#define RTL8365MB_VLAN_PVID_CTRL_PORT0_MCIDX_MASK GENMASK(4, 0) +#define RTL8365MB_VLAN_PVID_CTRL_PORT1_MCIDX_MASK GENMASK(12, 8) #define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_OFFSET(_p) \ (((_p) & 1) << 3) #define RTL8365MB_VLAN_PVID_CTRL_PORT_MCIDX_MASK(_p) \ diff --git a/drivers/net/dsa/realtek/rtl8366rb.c b/drivers/net/dsa/realtek/rtl8366rb.c index f11831b66de8..093a06c599d4 100644 --- a/drivers/net/dsa/realtek/rtl8366rb.c +++ b/drivers/net/dsa/realtek/rtl8366rb.c @@ -1839,7 +1839,7 @@ const struct realtek_variant rtl8366rb_variant = { static const struct of_device_id rtl8366rb_of_match[] = { { .compatible = "realtek,rtl8366rb", .data = &rtl8366rb_variant, }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rtl8366rb_of_match); diff --git a/drivers/net/dsa/realtek/rtl83xx.c b/drivers/net/dsa/realtek/rtl83xx.c index a96beea72ef5..ad42c05d0a88 100644 --- a/drivers/net/dsa/realtek/rtl83xx.c +++ b/drivers/net/dsa/realtek/rtl83xx.c @@ -2,6 +2,7 @@ #include <linux/module.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/of_mdio.h> #include <linux/if_bridge.h> #include <linux/etherdevice.h> @@ -61,6 +62,22 @@ static int rtl83xx_user_mdio_write(struct mii_bus *bus, int addr, int regnum, return priv->ops->phy_write(priv, addr, regnum, val); } +static int rtl83xx_user_mdio_read_c45(struct mii_bus *bus, int addr, int devad, + int regnum) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_read_c45(priv, addr, devad, regnum); +} + +static int rtl83xx_user_mdio_write_c45(struct mii_bus *bus, int addr, int devad, + int regnum, u16 val) +{ + struct realtek_priv *priv = bus->priv; + + return priv->ops->phy_write_c45(priv, addr, devad, regnum, val); +} + /** * rtl83xx_setup_user_mdio() - register the user mii bus driver * @ds: DSA switch associated with this user_mii_bus @@ -94,6 +111,10 @@ int rtl83xx_setup_user_mdio(struct dsa_switch *ds) bus->name = "Realtek user MII"; bus->read = rtl83xx_user_mdio_read; bus->write = rtl83xx_user_mdio_write; + if (priv->ops->phy_read_c45) + bus->read_c45 = rtl83xx_user_mdio_read_c45; + if (priv->ops->phy_write_c45) + bus->write_c45 = rtl83xx_user_mdio_write_c45; snprintf(bus->id, MII_BUS_ID_SIZE, "%s:user_mii", dev_name(priv->dev)); bus->parent = priv->dev; @@ -195,17 +216,24 @@ rtl83xx_probe(struct device *dev, priv->leds_disabled = of_property_read_bool(dev->of_node, "realtek,disable-leds"); - /* TODO: if power is software controlled, set up any regulators here */ + /* Enable the supplies before the reset line is requested and driven, + * so the chip is powered before its pins are driven. + */ + if (var->num_supplies) { + ret = devm_regulator_bulk_get_enable(dev, var->num_supplies, + var->supplies); + if (ret) + return dev_err_ptr_probe(dev, ret, "failed to enable supplies\n"); + } + priv->reset_ctl = devm_reset_control_get_optional(dev, NULL); if (IS_ERR(priv->reset_ctl)) return dev_err_cast_probe(dev, priv->reset_ctl, "failed to get reset control\n"); priv->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(priv->reset)) { - dev_err(dev, "failed to get RESET GPIO\n"); - return ERR_CAST(priv->reset); - } + if (IS_ERR(priv->reset)) + return dev_err_cast_probe(dev, priv->reset, "failed to get RESET GPIO\n"); dev_set_drvdata(dev, priv); @@ -216,6 +244,11 @@ rtl83xx_probe(struct device *dev, rtl83xx_reset_deassert(priv); msleep(REALTEK_HW_START_DELAY); dev_dbg(dev, "deasserted RESET\n"); + } else if (var->num_supplies) { + /* Powered but no reset line: still wait for the chip to boot + * before the first register access. + */ + msleep(REALTEK_HW_START_DELAY); } return priv; diff --git a/drivers/net/dsa/rzn1_a5psw.c b/drivers/net/dsa/rzn1_a5psw.c index 4d857e3be10b..85ff1fd2bbfb 100644 --- a/drivers/net/dsa/rzn1_a5psw.c +++ b/drivers/net/dsa/rzn1_a5psw.c @@ -1299,7 +1299,7 @@ static void a5psw_shutdown(struct platform_device *pdev) static const struct of_device_id a5psw_of_mtable[] = { { .compatible = "renesas,rzn1-a5psw", }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, a5psw_of_mtable); diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 1653e83d9123..8984ed55ad59 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -3374,7 +3374,7 @@ static const struct of_device_id sja1105_dt_ids[] = { { .compatible = "nxp,sja1110b", .data = &sja1110b_info }, { .compatible = "nxp,sja1110c", .data = &sja1110c_info }, { .compatible = "nxp,sja1110d", .data = &sja1110d_info }, - { /* sentinel */ }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sja1105_dt_ids); diff --git a/drivers/net/dsa/vitesse-vsc73xx-spi.c b/drivers/net/dsa/vitesse-vsc73xx-spi.c index 85b9a0f51dd8..7b784320c41e 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-spi.c +++ b/drivers/net/dsa/vitesse-vsc73xx-spi.c @@ -153,10 +153,15 @@ static int vsc73xx_spi_probe(struct spi_device *spi) ret = spi_setup(spi); if (ret < 0) { dev_err(dev, "spi setup failed.\n"); + spi_dev_put(vsc_spi->spi); return ret; } - return vsc73xx_probe(&vsc_spi->vsc); + ret = vsc73xx_probe(&vsc_spi->vsc); + if (ret) + spi_dev_put(vsc_spi->spi); + + return ret; } static void vsc73xx_spi_remove(struct spi_device *spi) @@ -167,6 +172,8 @@ static void vsc73xx_spi_remove(struct spi_device *spi) return; vsc73xx_remove(&vsc_spi->vsc); + + spi_dev_put(vsc_spi->spi); } static void vsc73xx_spi_shutdown(struct spi_device *spi) diff --git a/drivers/net/dsa/yt921x.c b/drivers/net/dsa/yt921x.c deleted file mode 100644 index 159b16606f6c..000000000000 --- a/drivers/net/dsa/yt921x.c +++ /dev/null @@ -1,4982 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Driver for Motorcomm YT921x Switch - * - * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII, - * be sure to do your own checks before porting to another chip. - * - * Copyright (c) 2025 David Yang - */ - -#include <linux/dcbnl.h> -#include <linux/etherdevice.h> -#include <linux/if_bridge.h> -#include <linux/if_hsr.h> -#include <linux/if_vlan.h> -#include <linux/iopoll.h> -#include <linux/mdio.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_mdio.h> -#include <linux/of_net.h> -#include <linux/sort.h> - -#include <net/dsa.h> -#include <net/dscp.h> -#include <net/ieee8021q.h> -#include <net/pkt_cls.h> - -#include "yt921x.h" - -struct yt921x_mib_desc { - unsigned int size; - unsigned int offset; - const char *name; -}; - -#define MIB_DESC(_size, _offset, _name) \ - {_size, _offset, _name} - -/* Must agree with yt921x_mib - * - * Unstructured fields (name != NULL) will appear in get_ethtool_stats(), - * structured go to their *_stats() methods, but we need their sizes and offsets - * to perform 32bit MIB overflow wraparound. - */ -static const struct yt921x_mib_desc yt921x_mib_descs[] = { - MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL), - MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL), - - MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"), - MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL), - - MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL), - - MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL), - MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL), - MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"), - MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"), -}; - -struct yt921x_info { - const char *name; - u16 major; - /* Unknown, seems to be plain enumeration */ - u8 mode; - u8 extmode; - /* Ports with integral GbE PHYs, not including MCU Port 10 */ - u16 internal_mask; - /* TODO: see comments in yt921x_dsa_phylink_get_caps() */ - u16 external_mask; -}; - -#define YT921X_PORT_MASK_INTn(port) BIT(port) -#define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0) -#define YT921X_PORT_MASK_EXT0 BIT(8) -#define YT921X_PORT_MASK_EXT1 BIT(9) - -static const struct yt921x_info yt921x_infos[] = { - { - "YT9215SC", YT9215_MAJOR, 1, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, - }, - { - "YT9215S", YT9215_MAJOR, 2, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, - }, - { - "YT9215RB", YT9215_MAJOR, 3, 0, - YT921X_PORT_MASK_INT0_n(5), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, - }, - { - "YT9214NB", YT9215_MAJOR, 3, 2, - YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, - }, - { - "YT9213NB", YT9215_MAJOR, 3, 3, - YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), - YT921X_PORT_MASK_EXT1, - }, - { - "YT9218N", YT9218_MAJOR, 0, 0, - YT921X_PORT_MASK_INT0_n(8), - 0, - }, - { - "YT9218MB", YT9218_MAJOR, 1, 0, - YT921X_PORT_MASK_INT0_n(8), - YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, - }, - {} -}; - -#define YT921X_NAME "yt921x" - -#define YT921X_VID_UNWARE 4095 - -#define YT921X_POLL_SLEEP_US 10000 -#define YT921X_POLL_TIMEOUT_US 100000 - -/* The interval should be small enough to avoid overflow of 32bit MIBs. - * - * Until we can read MIBs from stats64 call directly (i.e. sleep - * there), we have to poll stats more frequently then it is actually needed. - * For overflow protection, normally, 100 sec interval should have been OK. - */ -#define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ) - -struct yt921x_reg_mdio { - struct mii_bus *bus; - int addr; - /* SWITCH_ID_1 / SWITCH_ID_0 of the device - * - * This is a way to multiplex multiple devices on the same MII phyaddr - * and should be configurable in DT. However, MDIO core simply doesn't - * allow multiple devices over one reg addr, so this is a fixed value - * for now until a solution is found. - * - * Keep this because we need switchid to form MII regaddrs anyway. - */ - unsigned char switchid; -}; - -/* TODO: SPI/I2C */ - -#define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds) -#define to_device(priv) ((priv)->ds.dev) - -static u32 ethaddr_hi4_to_u32(const unsigned char *addr) -{ - return (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; -} - -static u32 ethaddr_lo2_to_u32(const unsigned char *addr) -{ - return (addr[4] << 8) | addr[5]; -} - -static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp) -{ - WARN_ON(!mutex_is_locked(&priv->reg_lock)); - - return priv->reg_ops->read(priv->reg_ctx, reg, valp); -} - -static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val) -{ - WARN_ON(!mutex_is_locked(&priv->reg_lock)); - - return priv->reg_ops->write(priv->reg_ctx, reg, val); -} - -static int -yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp) -{ - u32 val; - int res; - int ret; - - ret = read_poll_timeout(yt921x_reg_read, res, - res || (val & mask) == *valp, - YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US, - false, priv, reg, &val); - if (ret) - return ret; - if (res) - return res; - - *valp = val; - return 0; -} - -static int -yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val) -{ - int res; - u32 v; - u32 u; - - res = yt921x_reg_read(priv, reg, &v); - if (res) - return res; - - u = v; - u &= ~mask; - u |= val; - if (u == v) - return 0; - - return yt921x_reg_write(priv, reg, u); -} - -static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask) -{ - return yt921x_reg_update_bits(priv, reg, 0, mask); -} - -static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask) -{ - return yt921x_reg_update_bits(priv, reg, mask, 0); -} - -static int -yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set) -{ - return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask); -} - -/* Some multi-word registers, like VLANn_CTRL, should be treated as a single - * long register. More specifically, writes to parts of its words won't become - * visible, until the last word is written. - * - * Here we require full read and write operations over these registers to - * eliminate potential issues, although partial reads/writes are also possible. - */ - -static void update_ctrls_unaligned(u32 *lo, u32 *hi, u64 mask, u64 val) -{ - *lo &= ~lower_32_bits(mask); - *hi &= ~upper_32_bits(mask); - *lo |= lower_32_bits(val); - *hi |= upper_32_bits(val); -} - -static int -yt921x_regs_read(struct yt921x_priv *priv, u32 reg, u32 *vals, - unsigned int num_regs) -{ - int res; - - for (unsigned int i = 0; i < num_regs; i++) { - res = yt921x_reg_read(priv, reg + 4 * i, &vals[i]); - if (res) - return res; - } - - return 0; -} - -static int -yt921x_regs_write(struct yt921x_priv *priv, u32 reg, const u32 *vals, - unsigned int num_regs) -{ - int res; - - for (unsigned int i = 0; i < num_regs; i++) { - res = yt921x_reg_write(priv, reg + 4 * i, vals[i]); - if (res) - return res; - } - - return 0; -} - -static int -yt921x_regs_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, - const u32 *vals, unsigned int num_regs) -{ - bool changed = false; - u32 vs[4]; - int res; - - BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); - - res = yt921x_regs_read(priv, reg, vs, num_regs); - if (res) - return res; - - for (unsigned int i = 0; i < num_regs; i++) { - u32 u = vs[i]; - - u &= ~masks[i]; - u |= vals[i]; - if (u != vs[i]) - changed = true; - - vs[i] = u; - } - - if (!changed) - return 0; - - return yt921x_regs_write(priv, reg, vs, num_regs); -} - -static int -yt921x_regs_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, - unsigned int num_regs) -{ - bool changed = false; - u32 vs[4]; - int res; - - BUILD_BUG_ON(num_regs > ARRAY_SIZE(vs)); - - res = yt921x_regs_read(priv, reg, vs, num_regs); - if (res) - return res; - - for (unsigned int i = 0; i < num_regs; i++) { - u32 u = vs[i]; - - u &= ~masks[i]; - if (u != vs[i]) - changed = true; - - vs[i] = u; - } - - if (!changed) - return 0; - - return yt921x_regs_write(priv, reg, vs, num_regs); -} - -static int -yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) -{ - return yt921x_regs_write(priv, reg, vals, 2); -} - -static int -yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks, - const u32 *vals) -{ - return yt921x_regs_update_bits(priv, reg, masks, vals, 2); -} - -static int -yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, const u32 *masks) -{ - return yt921x_regs_clear_bits(priv, reg, masks, 2); -} - -static int -yt921x_reg96_write(struct yt921x_priv *priv, u32 reg, const u32 *vals) -{ - return yt921x_regs_write(priv, reg, vals, 3); -} - -static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) -{ - struct yt921x_reg_mdio *mdio = context; - struct mii_bus *bus = mdio->bus; - int addr = mdio->addr; - u32 reg_addr; - u32 reg_data; - u32 val; - int res; - - /* Hold the mdio bus lock to avoid (un)locking for 4 times */ - mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - - reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | - YT921X_SMI_READ; - res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); - if (res) - goto end; - res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); - if (res) - goto end; - - reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | - YT921X_SMI_READ; - res = __mdiobus_read(bus, addr, reg_data); - if (res < 0) - goto end; - val = (u16)res; - res = __mdiobus_read(bus, addr, reg_data); - if (res < 0) - goto end; - val = (val << 16) | (u16)res; - - *valp = val; - res = 0; - -end: - mutex_unlock(&bus->mdio_lock); - return res; -} - -static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val) -{ - struct yt921x_reg_mdio *mdio = context; - struct mii_bus *bus = mdio->bus; - int addr = mdio->addr; - u32 reg_addr; - u32 reg_data; - int res; - - mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); - - reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | - YT921X_SMI_WRITE; - res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); - if (res) - goto end; - res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); - if (res) - goto end; - - reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | - YT921X_SMI_WRITE; - res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16)); - if (res) - goto end; - res = __mdiobus_write(bus, addr, reg_data, (u16)val); - if (res) - goto end; - - res = 0; - -end: - mutex_unlock(&bus->mdio_lock); - return res; -} - -static const struct yt921x_reg_ops yt921x_reg_ops_mdio = { - .read = yt921x_reg_mdio_read, - .write = yt921x_reg_mdio_write, -}; - -/* TODO: SPI/I2C */ - -static int yt921x_intif_wait(struct yt921x_priv *priv) -{ - u32 val = 0; - - return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, - &val); -} - -static int -yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) -{ - struct device *dev = to_device(priv); - u32 mask; - u32 ctrl; - u32 val; - int res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_READ; - res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); - if (res) - return res; - - if ((u16)val != val) - dev_info(dev, - "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", - __func__, port, reg, val); - *valp = (u16)val; - return 0; -} - -static int -yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) -{ - u32 mask; - u32 ctrl; - int res; - - res = yt921x_intif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_WRITE; - res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - return yt921x_intif_wait(priv); -} - -static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) -{ - struct yt921x_priv *priv = mbus->priv; - u16 val; - int res; - - if (port >= YT921X_PORT_NUM) - return U16_MAX; - - mutex_lock(&priv->reg_lock); - res = yt921x_intif_read(priv, port, reg, &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - return val; -} - -static int -yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) -{ - struct yt921x_priv *priv = mbus->priv; - int res; - - if (port >= YT921X_PORT_NUM) - return -ENODEV; - - mutex_lock(&priv->reg_lock); - res = yt921x_intif_write(priv, port, reg, data); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) -{ - struct device *dev = to_device(priv); - struct mii_bus *mbus; - int res; - - mbus = devm_mdiobus_alloc(dev); - if (!mbus) - return -ENOMEM; - - mbus->name = "YT921x internal MDIO bus"; - snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); - mbus->priv = priv; - mbus->read = yt921x_mbus_int_read; - mbus->write = yt921x_mbus_int_write; - mbus->parent = dev; - mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); - - res = devm_of_mdiobus_register(dev, mbus, mnp); - if (res) - return res; - - priv->mbus_int = mbus; - - return 0; -} - -static int yt921x_extif_wait(struct yt921x_priv *priv) -{ - u32 val = 0; - - return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, - &val); -} - -static int -yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) -{ - struct device *dev = to_device(priv); - u32 mask; - u32 ctrl; - u32 val; - int res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; - res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); - if (res) - return res; - - if ((u16)val != val) - dev_info(dev, - "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", - __func__, port, reg, val); - *valp = (u16)val; - return 0; -} - -static int -yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) -{ - u32 mask; - u32 ctrl; - int res; - - res = yt921x_extif_wait(priv); - if (res) - return res; - - mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | - YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; - ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | - YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; - res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); - if (res) - return res; - - return yt921x_extif_wait(priv); -} - -static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) -{ - struct yt921x_priv *priv = mbus->priv; - u16 val; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_extif_read(priv, port, reg, &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - return val; -} - -static int -yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) -{ - struct yt921x_priv *priv = mbus->priv; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_extif_write(priv, port, reg, data); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) -{ - struct device *dev = to_device(priv); - struct mii_bus *mbus; - int res; - - mbus = devm_mdiobus_alloc(dev); - if (!mbus) - return -ENOMEM; - - mbus->name = "YT921x external MDIO bus"; - snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); - mbus->priv = priv; - /* TODO: c45? */ - mbus->read = yt921x_mbus_ext_read; - mbus->write = yt921x_mbus_ext_write; - mbus->parent = dev; - - res = devm_of_mdiobus_register(dev, mbus, mnp); - if (res) - return res; - - priv->mbus_ext = mbus; - - return 0; -} - -/* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */ -static int yt921x_read_mib(struct yt921x_priv *priv, int port) -{ - struct yt921x_port *pp = &priv->ports[port]; - struct device *dev = to_device(priv); - struct yt921x_mib *mib = &pp->mib; - int res = 0; - - /* Reading of yt921x_port::mib is not protected by a lock and it's vain - * to keep its consistency, since we have to read registers one by one - * and there is no way to make a snapshot of MIB stats. - * - * Writing (by this function only) is and should be protected by - * reg_lock. - */ - - for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { - const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; - u32 reg = YT921X_MIBn_DATA0(port) + desc->offset; - u64 *valp = &((u64 *)mib)[i]; - u32 val0; - u64 val; - - res = yt921x_reg_read(priv, reg, &val0); - if (res) - break; - - if (desc->size <= 1) { - u64 old_val = *valp; - - val = (old_val & ~(u64)U32_MAX) | val0; - if (val < old_val) - val += 1ull << 32; - } else { - u32 val1; - - res = yt921x_reg_read(priv, reg + 4, &val1); - if (res) - break; - val = ((u64)val1 << 32) | val0; - } - - WRITE_ONCE(*valp, val); - } - - pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte + - mib->rx_128_255byte + mib->rx_256_511byte + - mib->rx_512_1023byte + mib->rx_1024_1518byte + - mib->rx_jumbo; - pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte + - mib->tx_128_255byte + mib->tx_256_511byte + - mib->tx_512_1023byte + mib->tx_1024_1518byte + - mib->tx_jumbo; - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "read stats for", - port, res); - return res; -} - -static void yt921x_poll_mib(struct work_struct *work) -{ - struct yt921x_port *pp = container_of_const(work, struct yt921x_port, - mib_read.work); - struct yt921x_priv *priv = (void *)(pp - pp->index) - - offsetof(struct yt921x_priv, ports); - unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES; - int port = pp->index; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - if (res) - delay *= 4; - - schedule_delayed_work(&pp->mib_read, delay); -} - -static void -yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, - uint8_t *data) -{ - if (stringset != ETH_SS_STATS) - return; - - for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { - const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; - - if (desc->name) - ethtool_puts(&data, desc->name); - } -} - -static void -yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - size_t j; - - mutex_lock(&priv->reg_lock); - yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - - j = 0; - for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { - const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; - - if (!desc->name) - continue; - - data[j] = ((u64 *)mib)[i]; - j++; - } -} - -static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset) -{ - int cnt = 0; - - if (sset != ETH_SS_STATS) - return 0; - - for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { - const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; - - if (desc->name) - cnt++; - } - - return cnt; -} - -static void -yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port, - struct ethtool_eth_mac_stats *mac_stats) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - - mutex_lock(&priv->reg_lock); - yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - - mac_stats->FramesTransmittedOK = pp->tx_frames; - mac_stats->SingleCollisionFrames = mib->tx_single_collisions; - mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions; - mac_stats->FramesReceivedOK = pp->rx_frames; - mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors; - mac_stats->AlignmentErrors = mib->rx_alignment_errors; - mac_stats->OctetsTransmittedOK = mib->tx_good_bytes; - mac_stats->FramesWithDeferredXmissions = mib->tx_deferred; - mac_stats->LateCollisions = mib->tx_late_collisions; - mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors; - /* mac_stats->FramesLostDueToIntMACXmitError */ - /* mac_stats->CarrierSenseErrors */ - mac_stats->OctetsReceivedOK = mib->rx_good_bytes; - /* mac_stats->FramesLostDueToIntMACRcvError */ - mac_stats->MulticastFramesXmittedOK = mib->tx_multicast; - mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast; - /* mac_stats->FramesWithExcessiveDeferral */ - mac_stats->MulticastFramesReceivedOK = mib->rx_multicast; - mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast; - /* mac_stats->InRangeLengthErrors */ - /* mac_stats->OutOfRangeLengthField */ - mac_stats->FrameTooLongErrors = mib->rx_oversize_errors; -} - -static void -yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port, - struct ethtool_eth_ctrl_stats *ctrl_stats) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - - mutex_lock(&priv->reg_lock); - yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - - ctrl_stats->MACControlFramesTransmitted = mib->tx_pause; - ctrl_stats->MACControlFramesReceived = mib->rx_pause; - /* ctrl_stats->UnsupportedOpcodesReceived */ -} - -static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = { - { 0, 64 }, - { 65, 127 }, - { 128, 255 }, - { 256, 511 }, - { 512, 1023 }, - { 1024, 1518 }, - { 1519, YT921X_FRAME_SIZE_MAX }, - {} -}; - -static void -yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port, - struct ethtool_rmon_stats *rmon_stats, - const struct ethtool_rmon_hist_range **ranges) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - - mutex_lock(&priv->reg_lock); - yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - - *ranges = yt921x_rmon_ranges; - - rmon_stats->undersize_pkts = mib->rx_undersize_errors; - rmon_stats->oversize_pkts = mib->rx_oversize_errors; - rmon_stats->fragments = mib->rx_alignment_errors; - /* rmon_stats->jabbers */ - - rmon_stats->hist[0] = mib->rx_64byte; - rmon_stats->hist[1] = mib->rx_65_127byte; - rmon_stats->hist[2] = mib->rx_128_255byte; - rmon_stats->hist[3] = mib->rx_256_511byte; - rmon_stats->hist[4] = mib->rx_512_1023byte; - rmon_stats->hist[5] = mib->rx_1024_1518byte; - rmon_stats->hist[6] = mib->rx_jumbo; - - rmon_stats->hist_tx[0] = mib->tx_64byte; - rmon_stats->hist_tx[1] = mib->tx_65_127byte; - rmon_stats->hist_tx[2] = mib->tx_128_255byte; - rmon_stats->hist_tx[3] = mib->tx_256_511byte; - rmon_stats->hist_tx[4] = mib->tx_512_1023byte; - rmon_stats->hist_tx[5] = mib->tx_1024_1518byte; - rmon_stats->hist_tx[6] = mib->tx_jumbo; -} - -static void -yt921x_dsa_get_stats64(struct dsa_switch *ds, int port, - struct rtnl_link_stats64 *stats) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - - stats->rx_length_errors = mib->rx_undersize_errors + - mib->rx_fragment_errors; - stats->rx_over_errors = mib->rx_oversize_errors; - stats->rx_crc_errors = mib->rx_crc_errors; - stats->rx_frame_errors = mib->rx_alignment_errors; - /* stats->rx_fifo_errors */ - /* stats->rx_missed_errors */ - - stats->tx_aborted_errors = mib->tx_aborted_errors; - /* stats->tx_carrier_errors */ - stats->tx_fifo_errors = mib->tx_undersize_errors; - /* stats->tx_heartbeat_errors */ - stats->tx_window_errors = mib->tx_late_collisions; - - stats->rx_packets = pp->rx_frames; - stats->tx_packets = pp->tx_frames; - stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets; - stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets; - stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors + - stats->rx_crc_errors + stats->rx_frame_errors; - stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors + - stats->tx_window_errors; - stats->rx_dropped = mib->rx_dropped; - /* stats->tx_dropped */ - stats->multicast = mib->rx_multicast; - stats->collisions = mib->tx_collisions; -} - -static void -yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port, - struct ethtool_pause_stats *pause_stats) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_port *pp = &priv->ports[port]; - struct yt921x_mib *mib = &pp->mib; - - mutex_lock(&priv->reg_lock); - yt921x_read_mib(priv, port); - mutex_unlock(&priv->reg_lock); - - pause_stats->tx_pause_frames = mib->tx_pause; - pause_stats->rx_pause_frames = mib->rx_pause; -} - -static int -yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e) -{ - /* Poor datasheet for EEE operations; don't ask if you are confused */ - - bool enable = e->eee_enabled; - u16 new_mask; - int res; - - /* Enable / disable global EEE */ - new_mask = priv->eee_ports_mask; - new_mask &= ~BIT(port); - new_mask |= !enable ? 0 : BIT(port); - - if (!!new_mask != !!priv->eee_ports_mask) { - res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC, - YT921X_PON_STRAP_EEE, !!new_mask); - if (res) - return res; - res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL, - YT921X_PON_STRAP_EEE, !!new_mask); - if (res) - return res; - } - - priv->eee_ports_mask = new_mask; - - /* Enable / disable port EEE */ - res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL, - YT921X_EEE_CTRL_ENn(port), enable); - if (res) - return res; - res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port), - YT921X_EEE_VAL_DATA, enable); - if (res) - return res; - - return 0; -} - -static int -yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_set_eee(priv, port, e); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_mtu_fetch(struct yt921x_priv *priv, int port) -{ - struct dsa_port *dp = dsa_to_port(&priv->ds, port); - - return dp->user ? READ_ONCE(dp->user->mtu) : ETH_DATA_LEN; -} - -static int -yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) -{ - /* Only serves as packet filter, since the frame size is always set to - * maximum after reset - */ - - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct dsa_port *dp = dsa_to_port(ds, port); - int frame_size; - int res; - - frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN; - if (dsa_port_is_cpu(dp)) - frame_size += YT921X_TAG_LEN; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port), - YT921X_MAC_FRAME_SIZE_M, - YT921X_MAC_FRAME_SIZE(frame_size)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) -{ - /* Only called for user ports, exclude tag len here */ - return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; -} - -/* v * 2^e */ -static u64 ldexpu64(u64 v, int e) -{ - return e >= 0 ? v << e : v >> -e; -} - -/* slot (ns) * rate (/s) / 10^9 (ns/s) = 2^C * token * 4^unit */ -static u32 rate2token(u64 rate, unsigned int slot_ns, int unit, int C) -{ - int e = 2 * unit + C + YT921X_TOKEN_RATE_C; - - return div_u64(ldexpu64(slot_ns * rate, -e), 1000000000); -} - -static u64 token2rate(u32 token, unsigned int slot_ns, int unit, int C) -{ - int e = 2 * unit + C + YT921X_TOKEN_RATE_C; - - return div_u64(ldexpu64(mul_u32_u32(1000000000, token), e), slot_ns); -} - -/* burst = 2^C * token * 4^unit */ -static u32 burst2token(u64 burst, int unit, int C) -{ - return ldexpu64(burst, -(2 * unit + C)); -} - -static u64 token2burst(u32 token, int unit, int C) -{ - return ldexpu64(token, 2 * unit + C); -} - -struct yt921x_marker { - u32 cir; - u32 cbs; - u32 ebs; - int unit; - bool pkt_mode; -}; - -#define YT921X_MARKER_PKT_MODE BIT(0) -#define YT921X_MARKER_SINGLE_BUCKET BIT(1) - -static int -yt921x_marker_tfm(struct yt921x_marker *marker, u64 rate, u64 burst, - unsigned int flags, unsigned int slot_ns, u32 cir_max, - u32 cbs_max, int unit_max, struct yt921x_priv *priv, int port, - struct netlink_ext_ack *extack) -{ - const int C = flags & YT921X_MARKER_PKT_MODE ? YT921X_TOKEN_PKT_C : - YT921X_TOKEN_BYTE_C; - struct device *dev = to_device(priv); - struct yt921x_marker m; - u64 burst_est; - u64 burst_sug; - u64 burst_max; - u64 rate_max; - - m.unit = unit_max; - rate_max = token2rate(cir_max, slot_ns, m.unit, C); - burst_max = token2burst(cbs_max, m.unit, C); - - /* Check for unusual values */ - if (rate > rate_max || burst > burst_max) { - NL_SET_ERR_MSG_MOD(extack, "Unexpected tremendous rate"); - return -ERANGE; - } - - /* Check for matching burst */ - burst_est = div_u64(slot_ns * rate, 1000000000); - burst_sug = burst_est; - if (flags & YT921X_MARKER_PKT_MODE) - burst_sug++; - else - burst_sug += ETH_HLEN + yt921x_mtu_fetch(priv, port) + - ETH_FCS_LEN; - if (burst_sug > burst) - NL_SET_ERR_MSG_FMT_MOD(extack, - "Consider match rate %llu with burst at least %llu", - rate, burst_sug); - - /* Select unit */ - for (; m.unit > 0; m.unit--) { - if (rate > (rate_max >> 2) || burst > (burst_max >> 2)) - break; - rate_max >>= 2; - burst_max >>= 2; - } - - /* Calculate information rate and bucket size */ - m.cir = rate2token(rate, slot_ns, m.unit, C); - if (!m.cir) - m.cir = 1; - else if (WARN_ON(m.cir > cir_max)) - m.cir = cir_max; - m.cbs = burst2token(burst, m.unit, C); - if (!m.cbs) - m.cbs = 1; - else if (WARN_ON(m.cbs > cbs_max)) - m.cbs = cbs_max; - - /* Cut EBS */ - m.ebs = 0; - if (!(flags & YT921X_MARKER_SINGLE_BUCKET)) { - /* We don't have a chance to adjust rate when MTU is changed */ - if (flags & YT921X_MARKER_PKT_MODE) - burst_est++; - else - burst_est += YT921X_FRAME_SIZE_MAX; - - if (burst_est < burst) { - u32 pbs = m.cbs; - - m.cbs = burst2token(burst_est, m.unit, C); - if (!m.cbs) - m.cbs = 1; - else if (WARN_ON(m.cbs > cbs_max)) - m.cbs = cbs_max; - - if (pbs > m.cbs) - m.ebs = pbs - m.cbs; - } - } - - dev_dbg(dev, - "slot %u ns, rate %llu, burst %llu -> unit %d, cir %u, cbs %u, ebs %u\n", - slot_ns, rate, burst, m.unit, m.cir, m.cbs, m.ebs); - - m.pkt_mode = flags & YT921X_MARKER_PKT_MODE; - *marker = m; - return 0; -} - -static int -yt921x_marker_tfm_police(struct yt921x_marker *marker, - const struct flow_action_police *police, - unsigned int flags, struct yt921x_priv *priv, int port, - struct netlink_ext_ack *extack) -{ - bool pkt_mode = !!police->rate_pkt_ps; - u64 burst; - u64 rate; - - rate = pkt_mode ? police->rate_pkt_ps : police->rate_bytes_ps; - burst = pkt_mode ? police->burst_pkt : police->burst; - if (pkt_mode) - flags |= YT921X_MARKER_PKT_MODE; - - return yt921x_marker_tfm(marker, rate, burst, flags, - priv->meter_slot_ns, YT921X_METER_CIR_MAX, - YT921X_METER_CBS_MAX, YT921X_METER_UNIT_MAX, - priv, port, extack); -} - -static int -yt921x_marker_tfm_shape(struct yt921x_marker *marker, u64 rate, u64 burst, - unsigned int flags, struct yt921x_priv *priv, int port, - struct netlink_ext_ack *extack) -{ - return yt921x_marker_tfm(marker, rate, burst, flags, - priv->port_shape_slot_ns, YT921X_SHAPE_CIR_MAX, - YT921X_SHAPE_CBS_MAX, YT921X_SHAPE_UNIT_MAX, - priv, port, extack); -} - -static int -yt921x_police_validate(const struct flow_action_police *police, - const struct flow_action *action, - const struct flow_action_entry *act, - struct netlink_ext_ack *extack) -{ - if (police->exceed.act_id != FLOW_ACTION_DROP) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when exceed action is not drop"); - return -EOPNOTSUPP; - } - - if (police->notexceed.act_id != FLOW_ACTION_PIPE && - police->notexceed.act_id != FLOW_ACTION_ACCEPT) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when conform action is not pipe or ok"); - return -EOPNOTSUPP; - } - - if (police->notexceed.act_id == FLOW_ACTION_ACCEPT && action && act && - !flow_action_is_last_entry(action, act)) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when conform action is ok, but action is not last"); - return -EOPNOTSUPP; - } - - /* mtu defaults to unlimited but we got 2040 here, don't know why */ - if (police->peakrate_bytes_ps || police->avrate || police->overhead) { - NL_SET_ERR_MSG_MOD(extack, - "Offload not supported when peakrate/avrate/overhead is configured"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int -yt921x_meter_config(struct yt921x_priv *priv, unsigned int id, - const struct yt921x_marker *marker) -{ - u32 ctrls[3]; - - ctrls[0] = 0; - ctrls[1] = YT921X_METER_CTRLb_CIR(marker->cir); - ctrls[2] = YT921X_METER_CTRLc_UNIT(marker->unit) | - YT921X_METER_CTRLc_DROP_R | - YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN | - YT921X_METER_CTRLc_METER_EN; - if (marker->pkt_mode) - ctrls[2] |= YT921X_METER_CTRLc_PKT_MODE; - update_ctrls_unaligned(&ctrls[0], &ctrls[1], - YT921X_METER_CTRLab_EBS_M, - YT921X_METER_CTRLab_EBS(marker->ebs)); - update_ctrls_unaligned(&ctrls[1], &ctrls[2], - YT921X_METER_CTRLbc_CBS_M, - YT921X_METER_CTRLbc_CBS(marker->cbs)); - - return yt921x_reg96_write(priv, YT921X_METERn_CTRL(id), ctrls); -} - -static void yt921x_dsa_port_policer_del(struct dsa_switch *ds, int port) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), 0); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "delete policer on", - port, res); -} - -static int -yt921x_dsa_port_policer_add(struct dsa_switch *ds, int port, - const struct flow_action_police *police, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_marker marker; - u32 ctrl; - int res; - - res = yt921x_police_validate(police, NULL, NULL, extack); - if (res) - return res; - - res = yt921x_marker_tfm_police(&marker, police, 0, priv, port, extack); - if (res) - return res; - - mutex_lock(&priv->reg_lock); - res = yt921x_meter_config(priv, port + YT921X_METER_NUM, &marker); - if (res) - goto end; - - ctrl = YT921X_PORT_METER_ID(port) | YT921X_PORT_METER_EN; - res = yt921x_reg_write(priv, YT921X_PORTn_METER(port), ctrl); -end: - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_setup_tc_tbf_port(struct dsa_switch *ds, int port, - const struct tc_tbf_qopt_offload *qopt) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct netlink_ext_ack *extack = qopt->extack; - u32 ctrls[2]; - int res; - - if (qopt->parent != TC_H_ROOT) - return -EOPNOTSUPP; - - switch (qopt->command) { - case TC_TBF_STATS: - /* Unfortunately the convention for TC_*_STATS is a mess, - * neither 0 nor -EOPNOTSUPP is perfect here. - */ - return -EOPNOTSUPP; - case TC_TBF_DESTROY: - ctrls[0] = 0; - ctrls[1] = 0; - break; - case TC_TBF_REPLACE: { - const struct tc_tbf_qopt_offload_replace_params *p; - struct yt921x_marker marker; - - p = &qopt->replace_params; - - res = yt921x_marker_tfm_shape(&marker, p->rate.rate_bytes_ps, - p->max_size, - YT921X_MARKER_SINGLE_BUCKET, - priv, port, extack); - if (res) - return res; - - ctrls[0] = YT921X_PORT_SHAPE_CTRLa_CIR(marker.cir) | - YT921X_PORT_SHAPE_CTRLa_CBS(marker.cbs); - ctrls[1] = YT921X_PORT_SHAPE_CTRLb_UNIT(marker.unit) | - YT921X_PORT_SHAPE_CTRLb_EN; - break; - } - default: - return -EOPNOTSUPP; - } - - mutex_lock(&priv->reg_lock); - res = yt921x_reg64_write(priv, YT921X_PORTn_SHAPE_CTRL(port), ctrls); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_setup_tc(struct dsa_switch *ds, int port, - enum tc_setup_type type, void *type_data) -{ - switch (type) { - case TC_SETUP_QDISC_TBF: { - const struct tc_tbf_qopt_offload *qopt = type_data; - - return yt921x_dsa_port_setup_tc_tbf_port(ds, port, qopt); - } - default: - return -EOPNOTSUPP; - } -} - -/* ACL: 48 blocks * 8 entries - * - * One rule can span multiple entries, but within a block. - */ - -static void -yt921x_acl_entry_set(struct yt921x_acl_entry *entry, unsigned int offset, - u32 flags, bool set) -{ - if (set) - entry->key[offset] |= flags; - entry->mask[offset] |= flags; -} - -static unsigned int -yt921x_acl_entries_set_is_fragment(struct yt921x_acl_entry *entries, - unsigned int size, bool set) -{ - for (unsigned int i = 0; i < size; i++) - switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { - case YT921X_ACL_TYPE_IPV4_DA: - case YT921X_ACL_TYPE_IPV4_SA: - yt921x_acl_entry_set(&entries[i], 1, - YT921X_ACL_BINb_IPV4_FRAG, set); - return size; - case YT921X_ACL_TYPE_IPV6_DA3: - case YT921X_ACL_TYPE_IPV6_SA3: - yt921x_acl_entry_set(&entries[i], 1, - YT921X_ACL_BINb_IPV6_xA3_FRAG, - set); - return size; - case YT921X_ACL_TYPE_MISC: - yt921x_acl_entry_set(&entries[i], 1, - YT921X_ACL_BINb_MISC_FRAG, set); - return size; - case YT921X_ACL_TYPE_L4: - yt921x_acl_entry_set(&entries[i], 1, - YT921X_ACL_BINb_L4_FRAG, set); - return size; - } - - if (size >= YT921X_ACL_ENT_PER_BLK) - return 0; - - entries[size] = (typeof(*entries)){}; - entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); - yt921x_acl_entry_set(&entries[size], 1, YT921X_ACL_BINb_MISC_FRAG, set); - - return size + 1; -} - -static unsigned int -yt921x_acl_entries_set_first_frag(struct yt921x_acl_entry *entries, - unsigned int size, bool set) -{ - for (unsigned int i = 0; i < size; i++) - switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { - case YT921X_ACL_TYPE_IPV6_DA2: - case YT921X_ACL_TYPE_IPV6_SA2: - yt921x_acl_entry_set(&entries[i], 1, - YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG, - set); - return size; - case YT921X_ACL_TYPE_MISC: - yt921x_acl_entry_set(&entries[i], 0, - YT921X_ACL_BINa_MISC_FIRST_FRAG, - set); - return size; - } - - if (size >= YT921X_ACL_ENT_PER_BLK) - return 0; - - entries[size] = (typeof(*entries)){}; - entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); - yt921x_acl_entry_set(&entries[size], 0, - YT921X_ACL_BINa_MISC_FIRST_FRAG, set); - - return size + 1; -} - -static unsigned int -yt921x_acl_entries_set_l3_type(struct yt921x_acl_entry *entries, - unsigned int size, enum yt921x_l3_type type) -{ - for (unsigned int i = 0; i < size; i++) - switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { - case YT921X_ACL_TYPE_MAC_DA0: - case YT921X_ACL_TYPE_MAC_SA0: - entries[i].key[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE(type); - entries[i].mask[1] |= YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M; - return size; - case YT921X_ACL_TYPE_MISC: - entries[i].key[0] |= YT921X_ACL_BINa_MISC_L3_TYPE(type); - entries[i].mask[0] |= YT921X_ACL_BINa_MISC_L3_TYPE_M; - return size; - } - - if (size >= YT921X_ACL_ENT_PER_BLK) - return 0; - - entries[size] = (typeof(*entries)){}; - entries[size].key[0] = YT921X_ACL_BINa_MISC_L3_TYPE(type); - entries[size].key[1] = YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); - entries[size].mask[0] = YT921X_ACL_BINa_MISC_L3_TYPE_M; - - return size + 1; -} - -static unsigned int -yt921x_acl_entries_set_l4_type(struct yt921x_acl_entry *entries, - unsigned int size, enum yt921x_l4_type type) -{ - for (unsigned int i = 0; i < size; i++) - switch (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1])) { - case YT921X_ACL_TYPE_IPV4_DA: - case YT921X_ACL_TYPE_IPV4_SA: - entries[i].key[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE(type); - entries[i].mask[1] |= YT921X_ACL_BINb_IPV4_L4_TYPE_M; - return size; - case YT921X_ACL_TYPE_IPV6_DA0: - case YT921X_ACL_TYPE_IPV6_DA1: - case YT921X_ACL_TYPE_IPV6_DA2: - case YT921X_ACL_TYPE_IPV6_DA3: - case YT921X_ACL_TYPE_IPV6_SA0: - case YT921X_ACL_TYPE_IPV6_SA1: - case YT921X_ACL_TYPE_IPV6_SA2: - case YT921X_ACL_TYPE_IPV6_SA3: - entries[i].key[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE(type); - entries[i].mask[1] |= YT921X_ACL_BINb_IPV6_L4_TYPE_M; - return size; - case YT921X_ACL_TYPE_L4: - entries[i].key[1] |= YT921X_ACL_BINb_L4_TYPE(type); - entries[i].mask[1] |= YT921X_ACL_BINb_L4_TYPE_M; - return size; - case YT921X_ACL_TYPE_MISC: - entries[i].key[1] |= YT921X_ACL_BINb_MISC_L4_TYPE(type); - entries[i].mask[1] |= YT921X_ACL_BINb_MISC_L4_TYPE_M; - return size; - } - - if (size >= YT921X_ACL_ENT_PER_BLK) - return 0; - - entries[size] = (typeof(*entries)){}; - entries[size].key[1] = YT921X_ACL_BINb_MISC_L4_TYPE(type) | - YT921X_ACL_KEYb_TYPE(YT921X_ACL_TYPE_MISC); - entries[size].mask[1] = YT921X_ACL_BINb_MISC_L4_TYPE_M; - - return size + 1; -} - -static struct yt921x_acl_entry * -yt921x_acl_entries_new(struct yt921x_acl_entry *entries, unsigned int *sizep, - u32 type) -{ - unsigned int size = *sizep; - - if (size >= YT921X_ACL_ENT_PER_BLK) - return NULL; - - entries[size] = (typeof(*entries)){}; - entries[size].key[1] = YT921X_ACL_KEYb_TYPE(type); - - (*sizep)++; - return &entries[size]; -} - -static struct yt921x_acl_entry * -yt921x_acl_entries_find(struct yt921x_acl_entry *entries, unsigned int *sizep, - u32 type) -{ - for (unsigned int i = 0; i < *sizep; i++) - if (FIELD_GET(YT921X_ACL_KEYb_TYPE_M, entries[i].key[1]) == - type) - return &entries[i]; - return yt921x_acl_entries_new(entries, sizep, type); -} - -static void -yt921x_acl_rule_set_ports(struct yt921x_acl_rule *aclrule, u16 ord, - u16 ports_mask) -{ - struct yt921x_acl_entry *entries = aclrule->entries; - - for (unsigned int i = 0; i < hweight8(aclrule->mask); i++) { - entries[i].key[1] |= YT921X_ACL_KEYb_SPORTS(ports_mask) | - YT921X_ACL_KEYb_ORD(ord); - } -} - -struct yt921x_acl_rule_ext { - struct yt921x_acl_rule r; - - struct yt921x_marker marker; -}; - -static int -yt921x_acl_rule_ext_parse_flow_entries(struct yt921x_acl_rule_ext *ruleext, - const struct flow_cls_offload *cls) -{ - const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); - struct yt921x_acl_entry *entries = ruleext->r.entries; - struct netlink_ext_ack *extack = cls->common.extack; - const struct flow_dissector *dissector; - struct yt921x_acl_entry *entry; - unsigned int size = 0; - bool use_dport; - bool use_sport; - - /* Incomplete and probably won't, since it supports custom u32 filters. - * New adapters are welcome. - */ - dissector = rule->match.dissector; - if (dissector->used_keys & - ~(BIT_ULL(FLOW_DISSECTOR_KEY_CONTROL) | - BIT_ULL(FLOW_DISSECTOR_KEY_BASIC) | - BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_PORTS) | - BIT_ULL(FLOW_DISSECTOR_KEY_PORTS_RANGE) | - BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS) | - BIT_ULL(FLOW_DISSECTOR_KEY_IP) | - BIT_ULL(FLOW_DISSECTOR_KEY_TCP))) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported keys used"); - return -EOPNOTSUPP; - } - - /* Entries */ - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) { - struct flow_match_ipv4_addrs match; - - flow_rule_match_ipv4_addrs(rule, &match); - - if (match.mask->dst) { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_IPV4_DA); - if (!entry) - goto err; - - entry->key[0] |= ntohl(match.key->dst); - entry->mask[0] |= ntohl(match.mask->dst); - } - - if (match.mask->src) { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_IPV4_SA); - if (!entry) - goto err; - - entry->key[0] |= ntohl(match.key->src); - entry->mask[0] |= ntohl(match.mask->src); - } - } - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS)) { - struct flow_match_ipv6_addrs match; - - flow_rule_match_ipv6_addrs(rule, &match); - - for (unsigned int i = 0; i < 4; i++) { - if (!match.mask->dst.s6_addr32[i]) - continue; - - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_IPV6_DA0 + i); - if (!entry) - goto err; - - entry->key[0] |= ntohl(match.key->dst.s6_addr32[i]); - entry->mask[0] |= ntohl(match.mask->dst.s6_addr32[i]); - } - - for (unsigned int i = 0; i < 4; i++) { - if (!match.mask->src.s6_addr32[i]) - continue; - - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_IPV6_SA0 + i); - if (!entry) - goto err; - - entry->key[0] |= ntohl(match.key->src.s6_addr32[i]); - entry->mask[0] |= ntohl(match.mask->src.s6_addr32[i]); - } - } - - use_dport = false; - use_sport = false; - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) { - struct flow_match_ports match; - - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_L4); - if (!entry) - goto err; - - flow_rule_match_ports(rule, &match); - - use_dport = !!match.mask->dst; - use_sport = !!match.mask->src; - - entry->key[0] |= (ntohs(match.key->dst) << 16) | - ntohs(match.key->src); - entry->mask[0] |= (ntohs(match.mask->dst) << 16) | - ntohs(match.mask->src); - } - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE)) { - struct flow_match_ports_range match; - - entry = yt921x_acl_entries_find(entries, &size, - YT921X_ACL_TYPE_L4); - if (!entry) - goto err; - - flow_rule_match_ports_range(rule, &match); - - if ((use_dport && match.mask->tp.dst) || - (use_sport && match.mask->tp.src)) { - NL_SET_ERR_MSG_MOD(extack, - "Port mask and range are mutually exclusive"); - return -EINVAL; - } - - if (match.mask->tp.dst) { - entry->key[0] |= ntohs(match.key->tp_min.dst) << 16; - entry->key[1] |= YT921X_ACL_KEYb_L4_DPORT_RANGE_EN; - entry->mask[0] |= ntohs(match.key->tp_max.dst) << 16; - } - - if (match.mask->tp.src) { - entry->key[0] |= ntohs(match.key->tp_min.src); - entry->key[1] |= YT921X_ACL_KEYb_L4_SPORT_RANGE_EN; - entry->mask[0] |= ntohs(match.key->tp_max.src); - } - } - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { - struct flow_match_eth_addrs match; - u32 mask; - - flow_rule_match_eth_addrs(rule, &match); - - mask = ethaddr_hi4_to_u32(match.mask->dst); - if (mask) { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_MAC_DA0); - if (!entry) - goto err; - - entry->key[0] |= ethaddr_hi4_to_u32(match.key->dst); - entry->mask[0] |= mask; - } - - mask = ethaddr_hi4_to_u32(match.mask->src); - if (mask) { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_MAC_SA0); - if (!entry) - goto err; - - entry->key[0] |= ethaddr_hi4_to_u32(match.key->src); - entry->mask[0] |= mask; - } - - mask = (ethaddr_lo2_to_u32(match.mask->dst) << 16) | - ethaddr_lo2_to_u32(match.mask->src); - if (mask) { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_MAC_DA1_SA1); - if (!entry) - goto err; - - entry->key[0] |= (ethaddr_lo2_to_u32(match.key->dst) << 16) | - ethaddr_lo2_to_u32(match.key->src); - entry->mask[0] |= mask; - } - } - - /* Entries + Misc */ - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) { - struct flow_match_basic match; - - flow_rule_match_basic(rule, &match); - - if (match.mask->n_proto) { - enum yt921x_l3_type l3type = YT921X_L3_TYPE_OTHER; - - if (match.mask->n_proto == htons(~0)) - switch (match.key->n_proto) { - case htons(ETH_P_IP): - l3type = YT921X_L3_TYPE_IPV4; - break; - case htons(ETH_P_IPV6): - l3type = YT921X_L3_TYPE_IPV6; - break; - case htons(ETH_P_ARP): - l3type = YT921X_L3_TYPE_ARP; - break; - case htons(ETH_P_LLDP): - l3type = YT921X_L3_TYPE_LLDP; - break; - case htons(ETH_P_PAE): - l3type = YT921X_L3_TYPE_PAE; - break; - case htons(ETH_P_CFM): - l3type = YT921X_L3_TYPE_ERP; - break; - } - - if (l3type != YT921X_L3_TYPE_OTHER) { - size = yt921x_acl_entries_set_l3_type(entries, - size, - l3type); - if (!size) - goto err; - } else { - entry = yt921x_acl_entries_new(entries, &size, - YT921X_ACL_TYPE_ETHERTYPE); - if (!entry) - goto err; - - entry->key[0] |= ntohs(match.key->n_proto); - entry->mask[0] |= ntohs(match.mask->n_proto); - } - } - - if (match.mask->ip_proto) { - enum yt921x_l4_type l4type = YT921X_L4_TYPE_OTHER; - - if (match.mask->ip_proto == (u8)~0) - switch (match.key->ip_proto) { - case IPPROTO_TCP: - l4type = YT921X_L4_TYPE_TCP; - break; - case IPPROTO_UDP: - l4type = YT921X_L4_TYPE_UDP; - break; - case IPPROTO_UDPLITE: - l4type = YT921X_L4_TYPE_UDPLITE; - break; - case IPPROTO_ICMP: - l4type = YT921X_L4_TYPE_ICMP; - break; - case IPPROTO_IGMP: - l4type = YT921X_L4_TYPE_IGMP; - break; - } - - if (l4type != YT921X_L4_TYPE_OTHER) { - size = yt921x_acl_entries_set_l4_type(entries, - size, - l4type); - if (!size) - goto err; - } else { - entry = yt921x_acl_entries_find(entries, &size, - YT921X_ACL_TYPE_MISC); - if (!entry) - goto err; - - entry->key[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.key->ip_proto); - entry->mask[0] |= YT921X_ACL_BINa_MISC_IP_PROTO(match.mask->ip_proto); - } - } - } - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) { - u32 supp_flags = FLOW_DIS_IS_FRAGMENT | FLOW_DIS_FIRST_FRAG; - struct flow_match_control match; - - flow_rule_match_control(rule, &match); - if (!flow_rule_is_supp_control_flags(supp_flags, - match.mask->flags, extack)) - return -EOPNOTSUPP; - - if (match.mask->flags & FLOW_DIS_IS_FRAGMENT) { - bool set = match.key->flags & FLOW_DIS_IS_FRAGMENT; - - size = yt921x_acl_entries_set_is_fragment(entries, size, - set); - if (!size) - goto err; - } - if (match.mask->flags & FLOW_DIS_FIRST_FRAG) { - bool set = match.key->flags & FLOW_DIS_FIRST_FRAG; - - size = yt921x_acl_entries_set_first_frag(entries, size, - set); - if (!size) - goto err; - } - } - - /* Misc only */ - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_IP)) { - struct flow_match_ip match; - - flow_rule_match_ip(rule, &match); - if (match.mask->ttl) { - NL_SET_ERR_MSG_MOD(extack, - "Matching on TTL not supported"); - return -EOPNOTSUPP; - } - - if (match.mask->tos) { - entry = yt921x_acl_entries_find(entries, &size, - YT921X_ACL_TYPE_MISC); - if (!entry) - goto err; - - entry->key[0] |= YT921X_ACL_BINa_MISC_TOS(match.key->tos); - entry->mask[0] |= YT921X_ACL_BINa_MISC_TOS(match.mask->tos); - } - } - - if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_TCP)) { - struct flow_match_tcp match; - - flow_rule_match_tcp(rule, &match); - if (match.mask->flags & htons(~0xff)) { - NL_SET_ERR_MSG_MOD(extack, "Unsupported TCP flags"); - return -EOPNOTSUPP; - } - - if (match.mask->flags) { - entry = yt921x_acl_entries_find(entries, &size, - YT921X_ACL_TYPE_MISC); - if (!entry) - goto err; - - entry->key[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.key->flags)); - entry->mask[0] |= YT921X_ACL_BINa_MISC_TCP_FLAGS(ntohs(match.mask->flags)); - } - } - - if (!size) { - NL_SET_ERR_MSG_MOD(extack, "Empty rule generated, this should not happen"); - return -EOPNOTSUPP; - } - - ruleext->r.mask = (1 << size) - 1; - return 0; - -err: - NL_SET_ERR_MSG_MOD(extack, "Rule too complex"); - return -EOPNOTSUPP; -} - -static int -yt921x_acl_rule_ext_parse_flow_action(struct yt921x_acl_rule_ext *ruleext, - const struct flow_cls_offload *cls, - struct yt921x_priv *priv, int port) -{ - const struct flow_rule *rule = flow_cls_offload_flow_rule(cls); - const struct flow_action *flow_action = &rule->action; - struct netlink_ext_ack *extack = cls->common.extack; - enum flow_action_id redir_act = NUM_FLOW_ACTIONS; - const struct flow_action_entry *act; - u32 *action = ruleext->r.action; - bool seen_priority = false; - const char *reason = NULL; - bool seen_police = false; - unsigned int i; - int res; - - memset(action, 0, 3 * sizeof(*action)); - flow_action_for_each(i, act, flow_action) - switch (act->id) { - case FLOW_ACTION_ACCEPT: - case FLOW_ACTION_DROP: - case FLOW_ACTION_REDIRECT: - if (redir_act != NUM_FLOW_ACTIONS && - redir_act != act->id) { - reason = "Different redirect actions"; - goto fallback; - } - redir_act = act->id; - - switch (act->id) { - case FLOW_ACTION_ACCEPT: - action[2] |= YT921X_ACL_ACTc_FWD_EN | - YT921X_ACL_ACTc_FWD_FWD; - break; - case FLOW_ACTION_DROP: - action[2] |= YT921X_ACL_ACTc_FWD_EN | - YT921X_ACL_ACTc_FWD_REDIR; - break; - case FLOW_ACTION_REDIRECT: { - struct dsa_port *to_dp; - - to_dp = dsa_port_from_netdev(act->dev); - if (IS_ERR(to_dp) || to_dp->ds != &priv->ds) { - reason = "Redirect to non-local port"; - goto fallback; - } - - action[2] |= YT921X_ACL_ACTc_FWD_EN | - YT921X_ACL_ACTc_FWD_REDIR | - YT921X_ACL_ACTc_FWD_REDIR_DPORTn(to_dp->index); - break; - } - default: - break; - } - break; - case FLOW_ACTION_PRIORITY: - if (seen_priority) { - action[0] &= ~YT921X_ACL_ACTa_PRIO_EN; - action[1] &= ~YT921X_ACL_ACTb_PRIO_M; - - reason = "Multiple priority actions"; - goto fallback; - } - seen_priority = true; - - if (act->priority >= YT921X_PRIO_NUM) { - NL_SET_ERR_MSG_MOD(extack, - "Priority value is too high"); - return -EOPNOTSUPP; - } - action[0] |= YT921X_ACL_ACTa_PRIO_EN; - action[1] |= YT921X_ACL_ACTb_PRIO(act->priority); - break; - case FLOW_ACTION_POLICE: { - const struct flow_action_police *police = &act->police; - - if (seen_police) { - action[0] &= ~YT921X_ACL_ACTa_METER_EN; - - reason = "Multiple police actions"; - goto fallback; - } - seen_police = true; - - res = yt921x_police_validate(police, flow_action, act, - extack); - if (res) - return res; - - res = yt921x_marker_tfm_police(&ruleext->marker, police, - 0, priv, port, extack); - if (res) - return res; - - action[0] |= YT921X_ACL_ACTa_METER_EN; - break; - } - default: -fallback: - if (cls->common.skip_sw) { - NL_SET_ERR_MSG_FMT_MOD(extack, - "Action not supported when skip_sw: %s", - reason); - return -EOPNOTSUPP; - } - fallthrough; - case FLOW_ACTION_TRAP: - redir_act = FLOW_ACTION_TRAP; - - action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & - ~YT921X_ACL_ACTc_FWD_M; - action[2] |= YT921X_ACL_ACTc_FWD_EN | - YT921X_ACL_ACTc_FWD_TRAP; - break; - } - - ruleext->r.sw_assisted = !cls->common.skip_sw; - return 0; -} - -static int -yt921x_acl_rule_ext_parse_flow(struct yt921x_acl_rule_ext *ruleext, int port, - const struct flow_cls_offload *cls, bool ingress, - struct yt921x_priv *priv) -{ - struct netlink_ext_ack *extack = cls->common.extack; - int res; - - if (!ingress) { - NL_SET_ERR_MSG_MOD(extack, "Only ingress is supported"); - return -EOPNOTSUPP; - } - - if (cls->common.chain_index) { - NL_SET_ERR_MSG(extack, "Only chain 0 is supported"); - return -EOPNOTSUPP; - } - - res = yt921x_acl_rule_ext_parse_flow_action(ruleext, cls, priv, port); - if (res) - return res; - res = yt921x_acl_rule_ext_parse_flow_entries(ruleext, cls); - if (res) - return res; - - yt921x_acl_rule_set_ports(&ruleext->r, 0, BIT(port)); - ruleext->r.tag = cls->cookie; - ruleext->r.type = TC_SETUP_CLSFLOWER; - return 0; -} - -static unsigned int -yt921x_acl_find(const struct yt921x_priv *priv, enum tc_setup_type type, - unsigned long tag) -{ - for (unsigned int blkid = 0; blkid < YT921X_ACL_BLK_NUM; blkid++) { - const struct yt921x_acl_blk *aclblk = priv->acl_blks[blkid]; - - if (!aclblk) - continue; - - for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) - if (aclblk->rules[i] && aclblk->rules[i]->tag == tag && - aclblk->rules[i]->type == type) - return YT921X_ACL_ENT_PER_BLK * blkid + i; - } - - return UINT_MAX; -} - -static unsigned int -yt921x_acl_reserve(struct yt921x_priv *priv, unsigned int entscnt, - struct netlink_ext_ack *extack) -{ - int candidates[YT921X_ACL_ENT_PER_BLK + 1]; - unsigned int acl_used_cnt = 0; - - if (WARN_ON(entscnt > YT921X_ACL_ENT_PER_BLK)) - return UINT_MAX; - - for (unsigned int i = 0; i < ARRAY_SIZE(candidates); i++) - candidates[i] = -1; - for (unsigned int i = YT921X_ACL_BLK_NUM; i-- > 0;) { - unsigned int blk_used_cnt = hweight8(priv->acl_masks[i]); - - candidates[blk_used_cnt] = i; - acl_used_cnt += blk_used_cnt; - } - - if (acl_used_cnt >= YT921X_ACL_NUM) { - NL_SET_ERR_MSG_MOD(extack, "ACL entry limit reached"); - return UINT_MAX; - } - if (acl_used_cnt + entscnt <= YT921X_ACL_NUM) - for (unsigned int i = YT921X_ACL_ENT_PER_BLK - entscnt + 1; - i-- > 0;) - if (candidates[i] >= 0) - return YT921X_ACL_ENT_PER_BLK * candidates[i] + - ffz(priv->acl_masks[candidates[i]]); - - NL_SET_ERR_MSG_MOD(extack, - "ACL entry allocation failed, simplify your rules or remove existing rules"); - return UINT_MAX; -} - -static int -yt921x_acl_commit(struct yt921x_priv *priv, unsigned int entid, u8 entsmask) -{ - const struct yt921x_acl_rule *aclrule; - const struct yt921x_acl_blk *aclblk; - unsigned int blkid; - unsigned int binid; - unsigned long mask; - u32 zeros[3] = {}; - unsigned int i; - unsigned int o; - u32 ctrl; - int res; - - blkid = entid / YT921X_ACL_ENT_PER_BLK; - binid = entid % YT921X_ACL_ENT_PER_BLK; - aclblk = priv->acl_blks[blkid]; - aclrule = aclblk->rules[binid]; - - /* Write actions */ - res = yt921x_reg96_write(priv, YT921X_ACLn_ACT(entid), - aclrule ? aclrule->action : zeros); - if (res) - return res; - - /* Select the block */ - ctrl = YT921X_ACL_BLK_CMD_MODIFY | YT921X_ACL_BLK_CMD_BLKID(blkid); - res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); - if (res) - return res; - - /* Write keys and masks */ - ctrl = 0; - for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) - ctrl |= YT921X_ACL_BLK_KEEP_KEEPn(i); - - mask = entsmask; - i = 0; - for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) { - res = yt921x_reg64_write(priv, YT921X_ACLn_KEYm(blkid, o), - aclrule ? aclrule->entries[i].key : - zeros); - if (res) - return res; - - res = yt921x_reg64_write(priv, YT921X_ACLn_MASKm(blkid, o), - aclrule ? aclrule->entries[i].mask : - zeros); - if (res) - return res; - - ctrl &= ~YT921X_ACL_BLK_KEEP_KEEPn(o); - i++; - } - - res = yt921x_reg_write(priv, YT921X_ACL_BLK_KEEP, ctrl); - if (res) - return res; - - ctrl = 0; - for (unsigned int i = 0; i < YT921X_ACL_ENT_PER_BLK; i++) { - const struct yt921x_acl_rule *other = aclblk->rules[i]; - - if (!other) - continue; - - mask = other->mask; - for_each_set_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) - ctrl |= YT921X_ACL_ENTRY_ENm(o) | - YT921X_ACL_ENTRY_GRPIDm(o, i); - } - res = yt921x_reg_write(priv, YT921X_ACLn_ENTRY(blkid), ctrl); - if (res) - return res; - - /* Commit the block */ - ctrl = YT921X_ACL_BLK_CMD_BLKID(blkid); - res = yt921x_reg_write(priv, YT921X_ACL_BLK_CMD, ctrl); - if (res) - return res; - - return 0; -} - -static int -yt921x_acl_del(struct yt921x_priv *priv, enum tc_setup_type type, - unsigned long tag) -{ - struct yt921x_acl_rule *aclrule; - struct yt921x_acl_blk *aclblk; - unsigned int binid; - unsigned int blkid; - unsigned int entid; - int res; - - entid = yt921x_acl_find(priv, type, tag); - if (entid == UINT_MAX) - return -ENOENT; - - blkid = entid / YT921X_ACL_ENT_PER_BLK; - binid = entid % YT921X_ACL_ENT_PER_BLK; - aclblk = priv->acl_blks[blkid]; - aclrule = aclblk->rules[binid]; - - aclblk->rules[binid] = NULL; - res = yt921x_acl_commit(priv, entid, aclrule->mask); - /* the kernel never rolls back on failure */ - - if (aclrule->action[0] & YT921X_ACL_ACTa_METER_EN) - clear_bit(FIELD_GET(YT921X_ACL_ACTa_METER_ID_M, - aclrule->action[0]), - priv->meters_map); - priv->acl_masks[blkid] &= ~aclrule->mask; - kvfree(aclrule); - if (!priv->acl_masks[blkid]) { - kvfree(aclblk); - priv->acl_blks[blkid] = NULL; - } - return res; -} - -static int -yt921x_acl_add(struct yt921x_priv *priv, - const struct yt921x_acl_rule_ext *ruleext, - struct netlink_ext_ack *extack) -{ - unsigned int entscnt = hweight8(ruleext->r.mask); - struct yt921x_acl_rule *aclrule; - struct yt921x_acl_blk *aclblk; - bool use_trap = false; - unsigned int meterid; - unsigned long mask; - unsigned int binid; - unsigned int blkid; - unsigned int entid; - unsigned int o; - int res; - - /* Allocate resources */ - entid = yt921x_acl_reserve(priv, entscnt, extack); - if (entid == UINT_MAX) - return -EOPNOTSUPP; - - if (!(ruleext->r.action[0] & YT921X_ACL_ACTa_METER_EN)) { - meterid = YT921X_METER_NUM; - } else { - meterid = find_first_zero_bit(priv->meters_map, - YT921X_METER_NUM); - if (meterid < YT921X_METER_NUM) { - res = yt921x_meter_config(priv, meterid, - &ruleext->marker); - if (res) - return res; - } else if (ruleext->r.sw_assisted) { - use_trap = true; - } else { - NL_SET_ERR_MSG_MOD(extack, - "No more meters available"); - return -EOPNOTSUPP; - } - } - - /* Prepare acl block ctrlblk */ - blkid = entid / YT921X_ACL_ENT_PER_BLK; - binid = entid % YT921X_ACL_ENT_PER_BLK; - aclblk = priv->acl_blks[blkid]; - if (!aclblk) { - aclblk = kvzalloc_obj(*aclblk); - if (!aclblk) - return -ENOMEM; - priv->acl_blks[blkid] = aclblk; - } - - /* Prepare acl rule ctrlblk */ - aclrule = kvmemdup(&ruleext->r, - offsetof(struct yt921x_acl_rule, entries[entscnt]), - GFP_KERNEL); - if (!aclrule) { - res = -ENOMEM; - goto err; - } - - /* Replace the placeholder resource IDs */ - aclrule->mask = 0; - mask = priv->acl_masks[blkid]; - for_each_clear_bit(o, &mask, YT921X_ACL_ENT_PER_BLK) { - aclrule->mask |= BIT(o); - entscnt--; - if (!entscnt) - break; - } - - if (use_trap) { - aclrule->action[2] &= ~YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M & - ~YT921X_ACL_ACTc_FWD_M; - aclrule->action[2] |= YT921X_ACL_ACTc_FWD_EN | - YT921X_ACL_ACTc_FWD_TRAP; - } - if (meterid < YT921X_METER_NUM) - aclrule->action[0] |= YT921X_ACL_ACTa_METER_ID(meterid); - else - aclrule->action[0] &= ~YT921X_ACL_ACTa_METER_EN; - - /* Write rules */ - aclblk->rules[binid] = aclrule; - res = yt921x_acl_commit(priv, entid, aclrule->mask); - if (res) { - aclblk->rules[binid] = NULL; - kvfree(aclrule); - goto err; - } - - if (meterid < YT921X_METER_NUM) - set_bit(meterid, priv->meters_map); - priv->acl_masks[blkid] |= aclrule->mask; - return 0; - -err: - if (!priv->acl_masks[blkid]) { - kvfree(aclblk); - priv->acl_blks[blkid] = NULL; - } - return res; -} - -static int -yt921x_dsa_cls_flower_del(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_acl_del(priv, TC_SETUP_CLSFLOWER, cls->cookie); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_cls_flower_add(struct dsa_switch *ds, int port, - struct flow_cls_offload *cls, bool ingress) -{ - struct netlink_ext_ack *extack = cls->common.extack; - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct yt921x_acl_rule_ext ruleext; - int res; - - res = yt921x_acl_rule_ext_parse_flow(&ruleext, port, cls, ingress, - priv); - if (res) - return res; - - mutex_lock(&priv->reg_lock); - res = yt921x_acl_add(priv, &ruleext, extack); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress) -{ - u32 mask; - - if (ingress) - mask = YT921X_MIRROR_IGR_PORTn(port); - else - mask = YT921X_MIRROR_EGR_PORTn(port); - return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask); -} - -static int -yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress, - int to_local_port, struct netlink_ext_ack *extack) -{ - u32 srcs; - u32 ctrl; - u32 val; - u32 dst; - int res; - - if (ingress) - srcs = YT921X_MIRROR_IGR_PORTn(port); - else - srcs = YT921X_MIRROR_EGR_PORTn(port); - dst = YT921X_MIRROR_PORT(to_local_port); - - res = yt921x_reg_read(priv, YT921X_MIRROR, &val); - if (res) - return res; - - /* other mirror tasks & different dst port -> conflict */ - if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M | - YT921X_MIRROR_IGR_PORTS_M)) && - (val & YT921X_MIRROR_PORT_M) != dst) { - NL_SET_ERR_MSG_MOD(extack, - "Sniffer port is already configured, delete existing rules & retry"); - return -EBUSY; - } - - ctrl = val & ~YT921X_MIRROR_PORT_M; - ctrl |= srcs; - ctrl |= dst; - - if (ctrl == val) - return 0; - - return yt921x_reg_write(priv, YT921X_MIRROR, ctrl); -} - -static void -yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_mirror_del(priv, port, mirror->ingress); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "unmirror", - port, res); -} - -static int -yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress, struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_mirror_add(priv, port, ingress, - mirror->to_local_port, extack); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag, - struct netlink_ext_ack *extack) -{ - u32 val; - int res; - - /* Hash Mode is global. Make sure the same Hash Mode is set to all the - * 2 possible lags. - * If we are the unique LAG we can set whatever hash mode we want. - * To change hash mode it's needed to remove all LAG and change the mode - * with the latest. - */ - if (unique_lag) { - res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl); - if (res) - return res; - } else { - res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val); - if (res) - return res; - - if (val != ctrl) { - NL_SET_ERR_MSG_MOD(extack, - "Mismatched Hash Mode across different lags is not supported"); - return -EOPNOTSUPP; - } - } - - return 0; -} - -static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask) -{ - unsigned long targets_mask = ports_mask; - unsigned int cnt; - u32 ctrl; - int port; - int res; - - cnt = 0; - for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { - ctrl = YT921X_LAG_MEMBER_PORT(port); - res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt), - ctrl); - if (res) - return res; - - cnt++; - } - - ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) | - YT921X_LAG_GROUP_MEMBER_NUM(cnt); - return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl); -} - -static int -yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct dsa_port *dp; - u32 ctrl; - int res; - - if (!lag.id) - return -EINVAL; - - ctrl = 0; - dsa_lag_foreach_port(dp, ds->dst, &lag) - ctrl |= BIT(dp->index); - - mutex_lock(&priv->reg_lock); - res = yt921x_lag_set(priv, lag.id - 1, ctrl); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag, - struct netdev_lag_upper_info *info, - struct netlink_ext_ack *extack) -{ - unsigned int members; - struct dsa_port *dp; - - if (!lag.id) - return -EINVAL; - - members = 0; - dsa_lag_foreach_port(dp, ds->dst, &lag) - /* Includes the port joining the LAG */ - members++; - - if (members > YT921X_LAG_PORT_NUM) { - NL_SET_ERR_MSG_MOD(extack, - "Cannot offload more than 4 LAG ports"); - return -EOPNOTSUPP; - } - - if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { - NL_SET_ERR_MSG_MOD(extack, - "Can only offload LAG using hash TX type"); - return -EOPNOTSUPP; - } - - if (info->hash_type != NETDEV_LAG_HASH_L2 && - info->hash_type != NETDEV_LAG_HASH_L23 && - info->hash_type != NETDEV_LAG_HASH_L34) { - NL_SET_ERR_MSG_MOD(extack, - "Can only offload L2 or L2+L3 or L3+L4 TX hash"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int -yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag, - struct netdev_lag_upper_info *info, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct dsa_port *dp; - bool unique_lag; - unsigned int i; - u32 ctrl; - int res; - - res = yt921x_dsa_port_lag_check(ds, lag, info, extack); - if (res) - return res; - - ctrl = 0; - switch (info->hash_type) { - case NETDEV_LAG_HASH_L34: - ctrl |= YT921X_LAG_HASH_IP_DST; - ctrl |= YT921X_LAG_HASH_IP_SRC; - ctrl |= YT921X_LAG_HASH_IP_PROTO; - - ctrl |= YT921X_LAG_HASH_L4_DPORT; - ctrl |= YT921X_LAG_HASH_L4_SPORT; - break; - case NETDEV_LAG_HASH_L23: - ctrl |= YT921X_LAG_HASH_MAC_DA; - ctrl |= YT921X_LAG_HASH_MAC_SA; - - ctrl |= YT921X_LAG_HASH_IP_DST; - ctrl |= YT921X_LAG_HASH_IP_SRC; - ctrl |= YT921X_LAG_HASH_IP_PROTO; - break; - case NETDEV_LAG_HASH_L2: - ctrl |= YT921X_LAG_HASH_MAC_DA; - ctrl |= YT921X_LAG_HASH_MAC_SA; - break; - default: - return -EOPNOTSUPP; - } - - /* Check if we are the unique configured LAG */ - unique_lag = true; - dsa_lags_foreach_id(i, ds->dst) - if (i != lag.id && dsa_lag_by_id(ds->dst, i)) { - unique_lag = false; - break; - } - - mutex_lock(&priv->reg_lock); - do { - res = yt921x_lag_hash(priv, ctrl, unique_lag, extack); - if (res) - break; - - ctrl = 0; - dsa_lag_foreach_port(dp, ds->dst, &lag) - ctrl |= BIT(dp->index); - res = yt921x_lag_set(priv, lag.id - 1, ctrl); - } while (0); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp) -{ - struct device *dev = to_device(priv); - u32 val = YT921X_FDB_RESULT_DONE; - int res; - - res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE, - &val); - if (res) { - dev_err(dev, "FDB probably stuck\n"); - return res; - } - - *valp = val; - return 0; -} - -static int -yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, - u16 vid, u32 ctrl1) -{ - u32 ctrl; - int res; - - ctrl = ethaddr_hi4_to_u32(addr); - res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); - if (res) - return res; - - ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr); - return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); -} - -static int -yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, - u16 *indexp) -{ - u32 ctrl; - u32 val; - int res; - - res = yt921x_fdb_in01(priv, addr, vid, 0); - if (res) - return res; - - ctrl = 0; - res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); - if (res) - return res; - - ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - res = yt921x_fdb_wait(priv, &val); - if (res) - return res; - if (val & YT921X_FDB_RESULT_NOTFOUND) { - *indexp = YT921X_FDB_NUM; - return 0; - } - - *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); - return 0; -} - -static int -yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp, - u16 *ports_maskp, u16 *indexp, u8 *statusp) -{ - struct device *dev = to_device(priv); - u16 index; - u32 data0; - u32 data1; - u32 data2; - u32 val; - int res; - - res = yt921x_fdb_wait(priv, &val); - if (res) - return res; - if (val & YT921X_FDB_RESULT_NOTFOUND) { - *ports_maskp = 0; - return 0; - } - index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); - - res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1); - if (res) - return res; - if ((data1 & YT921X_FDB_IO1_STATUS_M) == - YT921X_FDB_IO1_STATUS_INVALID) { - *ports_maskp = 0; - return 0; - } - - res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2); - if (res) - return res; - - addr[0] = data0 >> 24; - addr[1] = data0 >> 16; - addr[2] = data0 >> 8; - addr[3] = data0; - addr[4] = data1 >> 8; - addr[5] = data1; - *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1); - *indexp = index; - *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2); - *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1); - - dev_dbg(dev, - "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n", - __func__, *indexp, addr[0], addr[1], addr[2], addr[3], - addr[4], addr[5], *vidp, *ports_maskp, *statusp); - return 0; -} - -static int -yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask, - dsa_fdb_dump_cb_t *cb, void *data) -{ - unsigned char addr[ETH_ALEN]; - u8 status; - u16 pmask; - u16 index; - u32 ctrl; - u16 vid; - int res; - - ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX | - YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status); - if (res) - return res; - if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { - res = cb(addr, vid, - status == YT921X_FDB_ENTRY_STATUS_STATIC, data); - if (res) - return res; - } - - ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); - res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); - if (res) - return res; - - index = 0; - do { - ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | - YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT | - YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, - &status); - if (res) - return res; - if (!pmask) - break; - - if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { - res = cb(addr, vid, - status == YT921X_FDB_ENTRY_STATUS_STATIC, - data); - if (res) - return res; - } - - /* Never call GET_NEXT with 4095, otherwise it will hang - * forever until a reset! - */ - } while (index < YT921X_FDB_NUM - 1); - - return 0; -} - -static int -yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid, - bool flush_static) -{ - u32 ctrl; - u32 val; - int res; - - if (vid < 4096) { - ctrl = YT921X_FDB_IO1_FID(vid); - res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); - if (res) - return res; - } - - ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); - res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); - if (res) - return res; - - ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START; - if (vid >= 4096) - ctrl |= YT921X_FDB_OP_FLUSH_PORT; - else - ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID; - if (flush_static) - ctrl |= YT921X_FDB_OP_FLUSH_STATIC; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - res = yt921x_fdb_wait(priv, &val); - if (res) - return res; - - return 0; -} - -static int -yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static) -{ - return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static); -} - -static int -yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1, - u16 ctrl2) -{ - u32 ctrl; - u32 val; - int res; - - res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2); - if (res) - return res; - - ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | - YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - return yt921x_fdb_wait(priv, &val); -} - -static int -yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, - u16 ports_mask) -{ - u32 ctrl; - u32 val; - int res; - - ctrl = YT921X_FDB_IO1_STATUS_STATIC; - res = yt921x_fdb_in01(priv, addr, vid, ctrl); - if (res) - return res; - - ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); - res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); - if (res) - return res; - - ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - return yt921x_fdb_wait(priv, &val); -} - -static int -yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr, - u16 vid, u16 ports_mask) -{ - u16 index; - u32 ctrl1; - u32 ctrl2; - u32 ctrl; - u32 val2; - u32 val; - int res; - - /* Check for presence */ - res = yt921x_fdb_has(priv, addr, vid, &index); - if (res) - return res; - if (index >= YT921X_FDB_NUM) - return 0; - - /* Check if action required */ - res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); - if (res) - return res; - - ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask); - if (ctrl2 == val2) - return 0; - if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) { - ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START; - res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); - if (res) - return res; - - return yt921x_fdb_wait(priv, &val); - } - - res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1); - if (res) - return res; - - return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); -} - -static int -yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, - u16 ports_mask) -{ - u16 index; - u32 ctrl1; - u32 ctrl2; - u32 val1; - u32 val2; - int res; - - /* Check for presence */ - res = yt921x_fdb_has(priv, addr, vid, &index); - if (res) - return res; - if (index >= YT921X_FDB_NUM) - return yt921x_fdb_add(priv, addr, vid, ports_mask); - - /* Check if action required */ - res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1); - if (res) - return res; - res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); - if (res) - return res; - - ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M; - ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC; - ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask); - if (ctrl1 == val1 && ctrl2 == val2) - return 0; - - return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); -} - -static int -yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" - * only wants to see unicast - */ - res = yt921x_fdb_dump(priv, BIT(port), cb, data); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_fdb_flush_port(priv, port, false); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for", - port, res); -} - -static int -yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 ctrl; - int res; - - /* AGEING reg is set in 5s step */ - ctrl = clamp(msecs / 5000, 1, U16_MAX); - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_write(priv, YT921X_AGEING, ctrl); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, struct dsa_db db) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, struct dsa_db db) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_fdb_join(priv, addr, vid, BIT(port)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - const unsigned char *addr = mdb->addr; - u16 vid = mdb->vid; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb, - struct dsa_db db) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - const unsigned char *addr = mdb->addr; - u16 vid = mdb->vid; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_fdb_join(priv, addr, vid, BIT(port)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware) -{ - u32 ctrl; - - /* Abuse SVLAN for PCP parsing without polluting the FDB - it just works - * despite YT921X_VLAN_CTRL_SVLAN_EN never being set - */ - if (!vlan_aware) - ctrl = YT921X_PORT_IGR_TPIDn_STAG(0); - else - ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0); - return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl); -} - -static int -yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid) -{ - u32 mask; - u32 ctrl; - - mask = YT921X_PORT_VLAN_CTRL_CVID_M; - ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid); - return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port), - mask, ctrl); -} - -static int -yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering) -{ - struct dsa_port *dp = dsa_to_port(&priv->ds, port); - struct net_device *bdev; - u16 pvid; - u32 mask; - u32 ctrl; - int res; - - bdev = dsa_port_bridge_dev_get(dp); - - if (!bdev || !vlan_filtering) - pvid = YT921X_VID_UNWARE; - else - br_vlan_get_pvid(bdev, &pvid); - res = yt921x_port_set_pvid(priv, port, pvid); - if (res) - return res; - - mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED | - YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; - ctrl = 0; - /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */ - if (vlan_filtering && !pvid) - ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; - res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), - mask, ctrl); - if (res) - return res; - - res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER, - YT921X_VLAN_IGR_FILTER_PORTn(port), - vlan_filtering); - if (res) - return res; - - res = yt921x_vlan_aware_set(priv, port, vlan_filtering); - if (res) - return res; - - return 0; -} - -static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) -{ - u32 masks[2]; - - masks[0] = YT921X_VLAN_CTRLa_PORTn(port); - masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); - - return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks); -} - -static int -yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) -{ - u32 masks[2]; - u32 ctrls[2]; - - masks[0] = YT921X_VLAN_CTRLa_PORTn(port) | - YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask); - ctrls[0] = masks[0]; - - masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); - ctrls[1] = untagged ? masks[1] : 0; - - return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), - masks, ctrls); -} - -static int -yt921x_pvid_clear(struct yt921x_priv *priv, int port) -{ - struct dsa_port *dp = dsa_to_port(&priv->ds, port); - bool vlan_filtering; - u32 mask; - int res; - - vlan_filtering = dsa_port_is_vlan_filtering(dp); - - res = yt921x_port_set_pvid(priv, port, - vlan_filtering ? 0 : YT921X_VID_UNWARE); - if (res) - return res; - - if (vlan_filtering) { - mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; - res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), - mask); - if (res) - return res; - } - - return 0; -} - -static int -yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid) -{ - struct dsa_port *dp = dsa_to_port(&priv->ds, port); - bool vlan_filtering; - u32 mask; - int res; - - vlan_filtering = dsa_port_is_vlan_filtering(dp); - - if (vlan_filtering) { - res = yt921x_port_set_pvid(priv, port, vid); - if (res) - return res; - } - - mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; - res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask); - if (res) - return res; - - return 0; -} - -static int -yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port, - bool vlan_filtering, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - if (dsa_is_cpu_port(ds, port)) - return 0; - - mutex_lock(&priv->reg_lock); - res = yt921x_vlan_filtering(priv, port, vlan_filtering); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u16 vid = vlan->vid; - u16 pvid; - int res; - - if (dsa_is_cpu_port(ds, port)) - return 0; - - mutex_lock(&priv->reg_lock); - do { - struct dsa_port *dp = dsa_to_port(ds, port); - struct net_device *bdev; - - res = yt921x_vlan_del(priv, port, vid); - if (res) - break; - - bdev = dsa_port_bridge_dev_get(dp); - if (bdev) { - br_vlan_get_pvid(bdev, &pvid); - if (pvid == vid) - res = yt921x_pvid_clear(priv, port); - } - } while (0); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u16 vid = vlan->vid; - u16 pvid; - int res; - - /* CPU port is supposed to be a member of every VLAN; see - * yt921x_vlan_add() and yt921x_port_setup() - */ - if (dsa_is_cpu_port(ds, port)) - return 0; - - mutex_lock(&priv->reg_lock); - do { - struct dsa_port *dp = dsa_to_port(ds, port); - struct net_device *bdev; - - res = yt921x_vlan_add(priv, port, vid, - vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); - if (res) - break; - - bdev = dsa_port_bridge_dev_get(dp); - if (bdev) { - if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { - res = yt921x_pvid_set(priv, port, vid); - } else { - br_vlan_get_pvid(bdev, &pvid); - if (pvid == vid) - res = yt921x_pvid_clear(priv, port); - } - } - } while (0); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_userport_standalone(struct yt921x_priv *priv, int port) -{ - u32 mask; - u32 ctrl; - int res; - - ctrl = ~priv->cpu_ports_mask; - res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl); - if (res) - return res; - - /* Turn off FDB learning to prevent FDB pollution */ - mask = YT921X_PORT_LEARN_DIS; - res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask); - if (res) - return res; - - /* Turn off VLAN awareness */ - res = yt921x_vlan_aware_set(priv, port, false); - if (res) - return res; - - /* Unrelated since learning is off and all packets are trapped; - * set it anyway - */ - res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE); - if (res) - return res; - - return 0; -} - -static int yt921x_userport_bridge(struct yt921x_priv *priv, int port) -{ - u32 mask; - int res; - - mask = YT921X_PORT_LEARN_DIS; - res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask); - if (res) - return res; - - return 0; -} - -static int yt921x_isolate(struct yt921x_priv *priv, int port) -{ - u32 mask; - int res; - - mask = BIT(port); - for (int i = 0; i < YT921X_PORT_NUM; i++) { - if ((BIT(i) & priv->cpu_ports_mask) || i == port) - continue; - - res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i), - mask); - if (res) - return res; - } - - return 0; -} - -/* Make sure to include the CPU port in ports_mask, or your bridge will - * not have it. - */ -static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask) -{ - unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask; - u32 isolated_mask; - u32 ctrl; - int port; - int res; - - isolated_mask = 0; - for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { - struct yt921x_port *pp = &priv->ports[port]; - - if (pp->isolated) - isolated_mask |= BIT(port); - } - - /* Block from non-cpu bridge ports ... */ - for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { - struct yt921x_port *pp = &priv->ports[port]; - - /* to non-bridge ports */ - ctrl = ~ports_mask; - /* to isolated ports when isolated */ - if (pp->isolated) - ctrl |= isolated_mask; - /* to itself when non-hairpin */ - if (!pp->hairpin) - ctrl |= BIT(port); - else - ctrl &= ~BIT(port); - - res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), - ctrl); - if (res) - return res; - } - - return 0; -} - -static int yt921x_bridge_leave(struct yt921x_priv *priv, int port) -{ - int res; - - res = yt921x_userport_standalone(priv, port); - if (res) - return res; - - res = yt921x_isolate(priv, port); - if (res) - return res; - - return 0; -} - -static int -yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask) -{ - int res; - - res = yt921x_userport_bridge(priv, port); - if (res) - return res; - - res = yt921x_bridge(priv, ports_mask); - if (res) - return res; - - return 0; -} - -static int -yt921x_bridge_flags(struct yt921x_priv *priv, int port, - struct switchdev_brport_flags flags) -{ - struct yt921x_port *pp = &priv->ports[port]; - bool do_flush; - u32 mask; - int res; - - if (flags.mask & BR_LEARNING) { - bool learning = flags.val & BR_LEARNING; - - mask = YT921X_PORT_LEARN_DIS; - res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port), - mask, !learning); - if (res) - return res; - } - - /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP - * is set - */ - - /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */ - - do_flush = false; - if (flags.mask & BR_HAIRPIN_MODE) { - pp->hairpin = flags.val & BR_HAIRPIN_MODE; - do_flush = true; - } - if (flags.mask & BR_ISOLATED) { - pp->isolated = flags.val & BR_ISOLATED; - do_flush = true; - } - if (do_flush) { - struct dsa_switch *ds = &priv->ds; - struct dsa_port *dp = dsa_to_port(ds, port); - struct net_device *bdev; - - bdev = dsa_port_bridge_dev_get(dp); - if (bdev) { - u32 ports_mask; - - ports_mask = dsa_bridge_ports(ds, bdev); - ports_mask |= priv->cpu_ports_mask; - res = yt921x_bridge(priv, ports_mask); - if (res) - return res; - } - } - - return 0; -} - -static int -yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) -{ - if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD | - BR_MCAST_FLOOD | BR_ISOLATED)) - return -EINVAL; - return 0; -} - -static int -yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port, - struct switchdev_brport_flags flags, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - if (dsa_is_cpu_port(ds, port)) - return 0; - - mutex_lock(&priv->reg_lock); - res = yt921x_bridge_flags(priv, port, flags); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static void -yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - int res; - - if (dsa_is_cpu_port(ds, port)) - return; - - mutex_lock(&priv->reg_lock); - res = yt921x_bridge_leave(priv, port); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", - port, res); -} - -static int -yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload, - struct netlink_ext_ack *extack) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u16 ports_mask; - int res; - - if (dsa_is_cpu_port(ds, port)) - return 0; - - ports_mask = dsa_bridge_ports(ds, bridge.dev); - ports_mask |= priv->cpu_ports_mask; - - mutex_lock(&priv->reg_lock); - res = yt921x_bridge_join(priv, port, ports_mask); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port, - const struct switchdev_mst_state *st) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 mask; - u32 ctrl; - int res; - - mask = YT921X_STP_PORTn_M(port); - switch (st->state) { - case BR_STATE_DISABLED: - ctrl = YT921X_STP_PORTn_DISABLED(port); - break; - case BR_STATE_LISTENING: - case BR_STATE_LEARNING: - ctrl = YT921X_STP_PORTn_LEARNING(port); - break; - case BR_STATE_FORWARDING: - default: - ctrl = YT921X_STP_PORTn_FORWARD(port); - break; - case BR_STATE_BLOCKING: - ctrl = YT921X_STP_PORTn_BLOCKING(port); - break; - } - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int -yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, - const struct switchdev_vlan_msti *msti) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 masks[2]; - u32 ctrls[2]; - int res; - - if (!msti->vid) - return -EINVAL; - if (!msti->msti || msti->msti >= YT921X_MSTI_NUM) - return -EINVAL; - - masks[0] = 0; - ctrls[0] = 0; - masks[1] = YT921X_VLAN_CTRLb_STP_ID_M; - ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti); - - mutex_lock(&priv->reg_lock); - res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid), - masks, ctrls); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static void -yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct dsa_port *dp = dsa_to_port(ds, port); - struct device *dev = to_device(priv); - bool learning; - u32 mask; - u32 ctrl; - int res; - - mask = YT921X_STP_PORTn_M(port); - learning = false; - switch (state) { - case BR_STATE_DISABLED: - ctrl = YT921X_STP_PORTn_DISABLED(port); - break; - case BR_STATE_LISTENING: - ctrl = YT921X_STP_PORTn_LEARNING(port); - break; - case BR_STATE_LEARNING: - ctrl = YT921X_STP_PORTn_LEARNING(port); - learning = dp->learning; - break; - case BR_STATE_FORWARDING: - default: - ctrl = YT921X_STP_PORTn_FORWARD(port); - learning = dp->learning; - break; - case BR_STATE_BLOCKING: - ctrl = YT921X_STP_PORTn_BLOCKING(port); - break; - } - - mutex_lock(&priv->reg_lock); - do { - res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl); - if (res) - break; - - mask = YT921X_PORT_LEARN_DIS; - ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0; - res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), - mask, ctrl); - } while (0); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for", - port, res); -} - -static int __maybe_unused -yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 val; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - - return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val); -} - -static int __maybe_unused -yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 mask; - u32 ctrl; - int res; - - if (prio >= YT921X_PRIO_NUM) - return -EINVAL; - - mutex_lock(&priv->reg_lock); - mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN; - ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN; - res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int __maybe_unused appprios_cmp(const void *a, const void *b) -{ - return ((const u8 *)b)[1] - ((const u8 *)a)[1]; -} - -static int __maybe_unused -yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, - int *nselp) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u8 appprios[2][2] = {}; - int nsel; - u32 val; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - - appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP; - appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7; - appprios[1][0] = DCB_APP_SEL_PCP; - appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7; - sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp, - NULL); - - nsel = 0; - for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) { - sel[nsel] = appprios[i][0]; - nsel++; - } - *nselp = nsel; - - return 0; -} - -static int __maybe_unused -yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, - int nsel) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - u32 ctrl; - int res; - - if (nsel > YT921X_APP_SEL_NUM) - return -EINVAL; - - ctrl = 0; - for (int i = 0; i < nsel; i++) { - switch (sel[i]) { - case IEEE_8021QAZ_APP_SEL_DSCP: - ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP, - 7 - i); - break; - case DCB_APP_SEL_PCP: - ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP, - 7 - i); - ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP, - 7 - i); - break; - default: - dev_err(dev, - "Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n", - i + 1); - return -EOPNOTSUPP; - } - } - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_port_down(struct yt921x_priv *priv, int port) -{ - u32 mask; - int res; - - mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; - res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); - if (res) - return res; - - if (yt921x_port_is_external(port)) { - mask = YT921X_SERDES_LINK; - res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); - if (res) - return res; - - mask = YT921X_XMII_LINK; - res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); - if (res) - return res; - } - - return 0; -} - -static int -yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, - phy_interface_t interface, int speed, int duplex, - bool tx_pause, bool rx_pause) -{ - u32 mask; - u32 ctrl; - int res; - - switch (speed) { - case SPEED_10: - ctrl = YT921X_PORT_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_PORT_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_PORT_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_PORT_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_PORT_SPEED_10000; - break; - default: - return -EINVAL; - } - if (duplex == DUPLEX_FULL) - ctrl |= YT921X_PORT_DUPLEX_FULL; - if (tx_pause) - ctrl |= YT921X_PORT_TX_PAUSE; - if (rx_pause) - ctrl |= YT921X_PORT_RX_PAUSE; - ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; - res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl); - if (res) - return res; - - if (yt921x_port_is_external(port)) { - mask = YT921X_SERDES_SPEED_M; - switch (speed) { - case SPEED_10: - ctrl = YT921X_SERDES_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_SERDES_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_SERDES_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_SERDES_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_SERDES_SPEED_10000; - break; - default: - return -EINVAL; - } - mask |= YT921X_SERDES_DUPLEX_FULL; - if (duplex == DUPLEX_FULL) - ctrl |= YT921X_SERDES_DUPLEX_FULL; - mask |= YT921X_SERDES_TX_PAUSE; - if (tx_pause) - ctrl |= YT921X_SERDES_TX_PAUSE; - mask |= YT921X_SERDES_RX_PAUSE; - if (rx_pause) - ctrl |= YT921X_SERDES_RX_PAUSE; - mask |= YT921X_SERDES_LINK; - ctrl |= YT921X_SERDES_LINK; - res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), - mask, ctrl); - if (res) - return res; - - mask = YT921X_XMII_LINK; - res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); - if (res) - return res; - - switch (speed) { - case SPEED_10: - ctrl = YT921X_MDIO_POLLING_SPEED_10; - break; - case SPEED_100: - ctrl = YT921X_MDIO_POLLING_SPEED_100; - break; - case SPEED_1000: - ctrl = YT921X_MDIO_POLLING_SPEED_1000; - break; - case SPEED_2500: - ctrl = YT921X_MDIO_POLLING_SPEED_2500; - break; - case SPEED_10000: - ctrl = YT921X_MDIO_POLLING_SPEED_10000; - break; - default: - return -EINVAL; - } - if (duplex == DUPLEX_FULL) - ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL; - ctrl |= YT921X_MDIO_POLLING_LINK; - res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl); - if (res) - return res; - } - - return 0; -} - -static int -yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, - phy_interface_t interface) -{ - struct device *dev = to_device(priv); - u32 mask; - u32 ctrl; - int res; - - if (!yt921x_port_is_external(port)) { - if (interface != PHY_INTERFACE_MODE_INTERNAL) { - dev_err(dev, "Wrong mode %d on port %d\n", - interface, port); - return -EINVAL; - } - return 0; - } - - switch (interface) { - /* SERDES */ - case PHY_INTERFACE_MODE_SGMII: - case PHY_INTERFACE_MODE_100BASEX: - case PHY_INTERFACE_MODE_1000BASEX: - case PHY_INTERFACE_MODE_2500BASEX: - mask = YT921X_SERDES_CTRL_PORTn(port); - res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); - if (res) - return res; - - mask = YT921X_XMII_CTRL_PORTn(port); - res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask); - if (res) - return res; - - mask = YT921X_SERDES_MODE_M; - switch (interface) { - case PHY_INTERFACE_MODE_SGMII: - ctrl = YT921X_SERDES_MODE_SGMII; - break; - case PHY_INTERFACE_MODE_100BASEX: - ctrl = YT921X_SERDES_MODE_100BASEX; - break; - case PHY_INTERFACE_MODE_1000BASEX: - ctrl = YT921X_SERDES_MODE_1000BASEX; - break; - case PHY_INTERFACE_MODE_2500BASEX: - ctrl = YT921X_SERDES_MODE_2500BASEX; - break; - default: - return -EINVAL; - } - res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), - mask, ctrl); - if (res) - return res; - - break; - /* add XMII support here */ - default: - return -EINVAL; - } - - return 0; -} - -static void -yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, - phy_interface_t interface) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct yt921x_priv *priv = to_yt921x_priv(dp->ds); - int port = dp->index; - int res; - - /* No need to sync; port control block is hold until device remove */ - cancel_delayed_work(&priv->ports[port].mib_read); - - mutex_lock(&priv->reg_lock); - res = yt921x_port_down(priv, port); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", - port, res); -} - -static void -yt921x_phylink_mac_link_up(struct phylink_config *config, - struct phy_device *phydev, unsigned int mode, - phy_interface_t interface, int speed, int duplex, - bool tx_pause, bool rx_pause) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct yt921x_priv *priv = to_yt921x_priv(dp->ds); - int port = dp->index; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_port_up(priv, port, mode, interface, speed, duplex, - tx_pause, rx_pause); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", - port, res); - - schedule_delayed_work(&priv->ports[port].mib_read, 0); -} - -static void -yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode, - const struct phylink_link_state *state) -{ - struct dsa_port *dp = dsa_phylink_to_port(config); - struct yt921x_priv *priv = to_yt921x_priv(dp->ds); - int port = dp->index; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_port_config(priv, port, mode, state->interface); - mutex_unlock(&priv->reg_lock); - - if (res) - dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", - port, res); -} - -static void -yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, - struct phylink_config *config) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - const struct yt921x_info *info = priv->info; - - config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | - MAC_10 | MAC_100 | MAC_1000; - - if (info->internal_mask & BIT(port)) { - /* Port 10 for MCU should probably go here too. But since that - * is untested yet, turn it down for the moment by letting it - * fall to the default branch. - */ - __set_bit(PHY_INTERFACE_MODE_INTERNAL, - config->supported_interfaces); - } else if (info->external_mask & BIT(port)) { - /* TODO: external ports may support SERDES only, XMII only, or - * SERDES + XMII depending on the chip. However, we can't get - * the accurate config table due to lack of document, thus - * we simply declare SERDES + XMII and rely on the correctness - * of devicetree for now. - */ - - /* SERDES */ - __set_bit(PHY_INTERFACE_MODE_SGMII, - config->supported_interfaces); - /* REVSGMII (SGMII in PHY role) should go here, once - * PHY_INTERFACE_MODE_REVSGMII is introduced. - */ - __set_bit(PHY_INTERFACE_MODE_100BASEX, - config->supported_interfaces); - __set_bit(PHY_INTERFACE_MODE_1000BASEX, - config->supported_interfaces); - __set_bit(PHY_INTERFACE_MODE_2500BASEX, - config->supported_interfaces); - config->mac_capabilities |= MAC_2500FD; - - /* XMII */ - - /* Not tested. To add support for XMII: - * - Add proper interface modes below - * - Handle them in yt921x_port_config() - */ - } - /* no such port: empty supported_interfaces causes phylink to turn it - * down - */ -} - -static int yt921x_port_setup(struct yt921x_priv *priv, int port) -{ - struct dsa_switch *ds = &priv->ds; - u32 ctrl; - int res; - - res = yt921x_userport_standalone(priv, port); - if (res) - return res; - - /* Clear prio order (even if DCB is not enabled) to avoid unsolicited - * priorities - */ - res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0); - if (res) - return res; - - if (dsa_is_cpu_port(ds, port)) { - /* Egress of CPU port is supposed to be completely controlled - * via tagging, so set to oneway isolated (drop all packets - * without tag). - */ - ctrl = ~(u32)0; - res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), - ctrl); - if (res) - return res; - - /* To simplify FDB "isolation" simulation, we also disable - * learning on the CPU port, and let software identify packets - * towarding CPU (either trapped or a static FDB entry is - * matched, no matter which bridge that entry is for), which is - * already done by yt921x_userport_standalone(). As a result, - * VLAN-awareness becomes unrelated on the CPU port (set to - * VLAN-unaware by the way). - */ - } - - return 0; -} - -static enum dsa_tag_protocol -yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, - enum dsa_tag_protocol m) -{ - return DSA_TAG_PROTO_YT921X; -} - -static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_port_setup(priv, port); - mutex_unlock(&priv->reg_lock); - - return res; -} - -/* Not "port" - DSCP mapping is global */ -static int __maybe_unused -yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 val; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - - return FIELD_GET(YT921X_IPM_PRIO_M, val); -} - -static int __maybe_unused -yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - u32 val; - int res; - - mutex_lock(&priv->reg_lock); - /* During a "dcb app replace" command, the new app table entry will be - * added first, then the old one will be deleted. But the hardware only - * supports one QoS class per DSCP value (duh), so if we blindly delete - * the app table entry for this DSCP value, we end up deleting the - * entry with the new priority. Avoid that by checking whether user - * space wants to delete the priority which is currently configured, or - * something else which is no longer current. - */ - res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); - if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio) - res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), - YT921X_IPM_PRIO(IEEE8021Q_TT_BK)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int __maybe_unused -yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - int res; - - if (prio >= YT921X_PRIO_NUM) - return -EINVAL; - - mutex_lock(&priv->reg_lock); - res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), - YT921X_IPM_PRIO(prio)); - mutex_unlock(&priv->reg_lock); - - return res; -} - -static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp) -{ - u32 val = YT921X_EDATA_DATA_IDLE; - int res; - - res = yt921x_reg_wait(priv, YT921X_EDATA_DATA, - YT921X_EDATA_DATA_STATUS_M, &val); - if (res) - return res; - - *valp = val; - return 0; -} - -static int -yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp) -{ - u32 ctrl; - u32 val; - int res; - - ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ; - res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl); - if (res) - return res; - res = yt921x_edata_wait(priv, &val); - if (res) - return res; - - *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val); - return 0; -} - -static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp) -{ - u32 val; - int res; - - res = yt921x_edata_wait(priv, &val); - if (res) - return res; - return yt921x_edata_read_cont(priv, addr, valp); -} - -static int yt921x_chip_detect(struct yt921x_priv *priv) -{ - struct device *dev = to_device(priv); - const struct yt921x_info *info; - u8 extmode; - u32 chipid; - u32 major; - u32 mode; - u32 val; - int res; - - res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); - if (res) - return res; - - major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); - - for (info = yt921x_infos; info->name; info++) - if (info->major == major) - break; - if (!info->name) { - dev_err(dev, "Unexpected chipid 0x%x\n", chipid); - return -ENODEV; - } - - res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); - if (res) - return res; - res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode); - if (res) - return res; - - for (; info->name; info++) - if (info->major == major && info->mode == mode && - info->extmode == extmode) - break; - if (!info->name) { - dev_err(dev, - "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n", - chipid, mode, extmode); - return -ENODEV; - } - - res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val); - if (res) - return res; - switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) { - case 0: - priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6; - break; - case YT921X_SYS_CLK_143M: - priv->cycle_ns = 7; - break; - default: - priv->cycle_ns = 8; - } - - /* Print chipid here since we are interested in lower 16 bits */ - dev_info(dev, - "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n", - info->name, chipid, mode, extmode); - - priv->info = info; - - return 0; -} - -static int yt921x_chip_reset(struct yt921x_priv *priv) -{ - struct device *dev = to_device(priv); - u16 eth_p_tag; - u32 val; - int res; - - res = yt921x_chip_detect(priv); - if (res) - return res; - - /* Reset */ - res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW); - if (res) - return res; - - /* RST_HW is almost same as GPIO hard reset, so we need this delay. */ - fsleep(YT921X_RST_DELAY_US); - - val = 0; - res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val); - if (res) - return res; - - /* Check for tag EtherType; do it after reset in case you messed it up - * before. - */ - res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val); - if (res) - return res; - eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val); - if (eth_p_tag != ETH_P_YT921X) { - dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag, - ETH_P_YT921X); - /* Despite being possible, we choose not to set CPU_TAG_TPID, - * since there is no way it can be different unless you have the - * wrong chip. - */ - return -EINVAL; - } - - return 0; -} - -static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) -{ - struct dsa_switch *ds = &priv->ds; - unsigned long cpu_ports_mask; - u32 ctrls[2]; - u32 ctrl; - int port; - int res; - - /* Enable DSA */ - priv->cpu_ports_mask = dsa_cpu_ports(ds); - - ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | - YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); - res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); - if (res) - return res; - - /* Setup software switch */ - ctrl = YT921X_CPU_COPY_TO_EXT_CPU; - res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl); - if (res) - return res; - - ctrl = GENMASK(10, 0); - res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl); - if (res) - return res; - - /* YT921x does not support native DSA port bridging, so we use port - * isolation to emulate it. However, be especially careful that port - * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from - * another bridge) is matched and the destination port (in another - * bridge) is blocked, the packet will be dropped instead of flooding to - * the "bridged" ports, thus we need to trap and handle those packets by - * software. - * - * If there is no more than one bridge, we might be able to drop them - * directly given some conditions are met, but we trap them in all cases - * for now. - */ - ctrl = 0; - for (int i = 0; i < YT921X_PORT_NUM; i++) - ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i); - /* Except for CPU ports, if any packets are sent via CPU ports without - * tag, they should be dropped. - */ - cpu_ports_mask = priv->cpu_ports_mask; - for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) { - ctrl &= ~YT921X_ACT_UNK_ACTn_M(port); - ctrl |= YT921X_ACT_UNK_ACTn_DROP(port); - } - res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl); - if (res) - return res; - res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl); - if (res) - return res; - - /* Tagged VID 0 should be treated as untagged, which confuses the - * hardware a lot - */ - ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M; - ctrls[1] = 0; - res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls); - if (res) - return res; - - return 0; -} - -static int yt921x_chip_setup_tc(struct yt921x_priv *priv) -{ - unsigned int op_ns; - u32 ctrl; - int res; - - op_ns = 8 * priv->cycle_ns; - - ctrl = max(priv->meter_slot_ns / op_ns, YT921X_METER_SLOT_MIN); - res = yt921x_reg_write(priv, YT921X_METER_SLOT, ctrl); - if (res) - return res; - priv->meter_slot_ns = ctrl * op_ns; - - ctrl = max(priv->port_shape_slot_ns / op_ns, - YT921X_PORT_SHAPE_SLOT_MIN); - res = yt921x_reg_write(priv, YT921X_PORT_SHAPE_SLOT, ctrl); - if (res) - return res; - priv->port_shape_slot_ns = ctrl * op_ns; - - return 0; -} - -static int yt921x_chip_setup_acl(struct yt921x_priv *priv) -{ - u32 ctrl; - int res; - - ctrl = YT921X_ACL_PERMIT_UNMATCH_PORTS_M; - res = yt921x_reg_write(priv, YT921X_ACL_PERMIT_UNMATCH, ctrl); - if (res) - return res; - - ctrl = YT921X_ACL_PORT_PORTS_M; - res = yt921x_reg_write(priv, YT921X_ACL_PORT, ctrl); - if (res) - return res; - - return 0; -} - -static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv) -{ - u32 ctrl; - int res; - - /* DSCP to internal priorities */ - for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) { - int prio = ietf_dscp_to_ieee8021q_tt(dscp); - - if (prio < 0) - return prio; - - res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), - YT921X_IPM_PRIO(prio)); - if (res) - return res; - } - - /* 802.1Q QoS to internal priorities */ - for (u8 pcp = 0; pcp < 8; pcp++) - for (u8 dei = 0; dei < 2; dei++) { - ctrl = YT921X_IPM_PRIO(pcp); - if (dei) - /* "Red" almost means drop, so it's not that - * useful. Note that tc police does not support - * Three-Color very well - */ - ctrl |= YT921X_IPM_COLOR_YELLOW; - - for (u8 svlan = 0; svlan < 2; svlan++) { - u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp); - - res = yt921x_reg_write(priv, reg, ctrl); - if (res) - return res; - } - } - - return 0; -} - -static int yt921x_chip_setup(struct yt921x_priv *priv) -{ - u32 ctrl; - int res; - - ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER; - res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl); - if (res) - return res; - - res = yt921x_chip_setup_dsa(priv); - if (res) - return res; - - res = yt921x_chip_setup_tc(priv); - if (res) - return res; - - res = yt921x_chip_setup_acl(priv); - if (res) - return res; - -#if IS_ENABLED(CONFIG_DCB) - res = yt921x_chip_setup_qos(priv); - if (res) - return res; -#endif - - /* Clear MIB */ - ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT; - res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl); - if (res) - return res; - - /* Miscellaneous */ - res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP); - if (res) - return res; - - return 0; -} - -static int yt921x_dsa_setup(struct dsa_switch *ds) -{ - struct yt921x_priv *priv = to_yt921x_priv(ds); - struct device *dev = to_device(priv); - struct device_node *np = dev->of_node; - struct device_node *child; - int res; - - mutex_lock(&priv->reg_lock); - res = yt921x_chip_reset(priv); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - - /* Register the internal mdio bus. Nodes for internal ports should have - * proper phy-handle pointing to their PHYs. Not enabling the internal - * bus is possible, though pretty wired, if internal ports are not used. - */ - child = of_get_child_by_name(np, "mdio"); - if (child) { - res = yt921x_mbus_int_init(priv, child); - of_node_put(child); - if (res) - return res; - } - - /* External mdio bus is optional */ - child = of_get_child_by_name(np, "mdio-external"); - if (child) { - res = yt921x_mbus_ext_init(priv, child); - of_node_put(child); - if (res) - return res; - - dev_err(dev, "Untested external mdio bus\n"); - return -ENODEV; - } - - mutex_lock(&priv->reg_lock); - res = yt921x_chip_setup(priv); - mutex_unlock(&priv->reg_lock); - - if (res) - return res; - - return 0; -} - -static const struct phylink_mac_ops yt921x_phylink_mac_ops = { - .mac_link_down = yt921x_phylink_mac_link_down, - .mac_link_up = yt921x_phylink_mac_link_up, - .mac_config = yt921x_phylink_mac_config, -}; - -static const struct dsa_switch_ops yt921x_dsa_switch_ops = { - /* mib */ - .get_strings = yt921x_dsa_get_strings, - .get_ethtool_stats = yt921x_dsa_get_ethtool_stats, - .get_sset_count = yt921x_dsa_get_sset_count, - .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats, - .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats, - .get_rmon_stats = yt921x_dsa_get_rmon_stats, - .get_stats64 = yt921x_dsa_get_stats64, - .get_pause_stats = yt921x_dsa_get_pause_stats, - /* eee */ - .support_eee = dsa_supports_eee, - .set_mac_eee = yt921x_dsa_set_mac_eee, - /* mtu */ - .port_change_mtu = yt921x_dsa_port_change_mtu, - .port_max_mtu = yt921x_dsa_port_max_mtu, - /* rate */ - .port_policer_del = yt921x_dsa_port_policer_del, - .port_policer_add = yt921x_dsa_port_policer_add, - .port_setup_tc = yt921x_dsa_port_setup_tc, - /* acl */ - .cls_flower_del = yt921x_dsa_cls_flower_del, - .cls_flower_add = yt921x_dsa_cls_flower_add, - /* hsr */ - .port_hsr_leave = dsa_port_simple_hsr_leave, - .port_hsr_join = dsa_port_simple_hsr_join, - /* mirror */ - .port_mirror_del = yt921x_dsa_port_mirror_del, - .port_mirror_add = yt921x_dsa_port_mirror_add, - /* lag */ - .port_lag_leave = yt921x_dsa_port_lag_leave, - .port_lag_join = yt921x_dsa_port_lag_join, - /* fdb */ - .port_fdb_dump = yt921x_dsa_port_fdb_dump, - .port_fast_age = yt921x_dsa_port_fast_age, - .set_ageing_time = yt921x_dsa_set_ageing_time, - .port_fdb_del = yt921x_dsa_port_fdb_del, - .port_fdb_add = yt921x_dsa_port_fdb_add, - .port_mdb_del = yt921x_dsa_port_mdb_del, - .port_mdb_add = yt921x_dsa_port_mdb_add, - /* vlan */ - .port_vlan_filtering = yt921x_dsa_port_vlan_filtering, - .port_vlan_del = yt921x_dsa_port_vlan_del, - .port_vlan_add = yt921x_dsa_port_vlan_add, - /* bridge */ - .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags, - .port_bridge_flags = yt921x_dsa_port_bridge_flags, - .port_bridge_leave = yt921x_dsa_port_bridge_leave, - .port_bridge_join = yt921x_dsa_port_bridge_join, - /* mst */ - .port_mst_state_set = yt921x_dsa_port_mst_state_set, - .vlan_msti_set = yt921x_dsa_vlan_msti_set, - .port_stp_state_set = yt921x_dsa_port_stp_state_set, -#if IS_ENABLED(CONFIG_DCB) - /* dcb */ - .port_get_default_prio = yt921x_dsa_port_get_default_prio, - .port_set_default_prio = yt921x_dsa_port_set_default_prio, - .port_get_apptrust = yt921x_dsa_port_get_apptrust, - .port_set_apptrust = yt921x_dsa_port_set_apptrust, -#endif - /* port */ - .get_tag_protocol = yt921x_dsa_get_tag_protocol, - .phylink_get_caps = yt921x_dsa_phylink_get_caps, - .port_setup = yt921x_dsa_port_setup, -#if IS_ENABLED(CONFIG_DCB) - /* dscp */ - .port_get_dscp_prio = yt921x_dsa_port_get_dscp_prio, - .port_del_dscp_prio = yt921x_dsa_port_del_dscp_prio, - .port_add_dscp_prio = yt921x_dsa_port_add_dscp_prio, -#endif - /* chip */ - .setup = yt921x_dsa_setup, -}; - -static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) -{ - struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); - - if (!priv) - return; - - dsa_switch_shutdown(&priv->ds); -} - -static void yt921x_mdio_remove(struct mdio_device *mdiodev) -{ - struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); - - if (!priv) - return; - - for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) { - struct yt921x_port *pp = &priv->ports[i]; - - disable_delayed_work_sync(&pp->mib_read); - } - - dsa_unregister_switch(&priv->ds); - - for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) { - struct yt921x_acl_blk *aclblk = priv->acl_blks[i]; - - if (!aclblk) - continue; - for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) { - struct yt921x_acl_rule *aclrule = aclblk->rules[j]; - - if (!aclrule) - continue; - kvfree(aclrule); - } - kvfree(aclblk); - } - mutex_destroy(&priv->reg_lock); -} - -static int yt921x_mdio_probe(struct mdio_device *mdiodev) -{ - struct device *dev = &mdiodev->dev; - struct yt921x_reg_mdio *mdio; - struct yt921x_priv *priv; - struct dsa_switch *ds; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL); - if (!mdio) - return -ENOMEM; - - mdio->bus = mdiodev->bus; - mdio->addr = mdiodev->addr; - mdio->switchid = 0; - - mutex_init(&priv->reg_lock); - - priv->reg_ops = &yt921x_reg_ops_mdio; - priv->reg_ctx = mdio; - - for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) { - struct yt921x_port *pp = &priv->ports[i]; - - pp->index = i; - INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib); - } - - ds = &priv->ds; - ds->dev = dev; - ds->assisted_learning_on_cpu_port = true; - ds->dscp_prio_mapping_is_global = true; - ds->priv = priv; - ds->ops = &yt921x_dsa_switch_ops; - ds->ageing_time_min = 1 * 5000; - ds->ageing_time_max = U16_MAX * 5000; - ds->phylink_mac_ops = &yt921x_phylink_mac_ops; - ds->num_lag_ids = YT921X_LAG_NUM; - ds->num_ports = YT921X_PORT_NUM; - - mdiodev_set_drvdata(mdiodev, priv); - - return dsa_register_switch(ds); -} - -static const struct of_device_id yt921x_of_match[] = { - { .compatible = "motorcomm,yt9215" }, - {} -}; -MODULE_DEVICE_TABLE(of, yt921x_of_match); - -static struct mdio_driver yt921x_mdio_driver = { - .probe = yt921x_mdio_probe, - .remove = yt921x_mdio_remove, - .shutdown = yt921x_mdio_shutdown, - .mdiodrv.driver = { - .name = YT921X_NAME, - .of_match_table = yt921x_of_match, - }, -}; - -mdio_module_driver(yt921x_mdio_driver); - -MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>"); -MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/yt921x.h b/drivers/net/dsa/yt921x.h deleted file mode 100644 index 5f3b99e189c4..000000000000 --- a/drivers/net/dsa/yt921x.h +++ /dev/null @@ -1,977 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * Copyright (c) 2025 David Yang - */ - -#ifndef __YT921X_H -#define __YT921X_H - -#include <net/dsa.h> - -#define YT921X_SMI_SWITCHID_M GENMASK(3, 2) -#define YT921X_SMI_SWITCHID(x) FIELD_PREP(YT921X_SMI_SWITCHID_M, (x)) -#define YT921X_SMI_AD BIT(1) -#define YT921X_SMI_ADDR 0 -#define YT921X_SMI_DATA YT921X_SMI_AD -#define YT921X_SMI_RW BIT(0) -#define YT921X_SMI_WRITE 0 -#define YT921X_SMI_READ YT921X_SMI_RW - -#define YT921X_SWITCHID_NUM 4 - -#define YT921X_RST 0x80000 -#define YT921X_RST_HW BIT(31) -#define YT921X_RST_SW BIT(1) -#define YT921X_FUNC 0x80004 -#define YT921X_FUNC_METER BIT(4) -#define YT921X_FUNC_ACL BIT(2) -#define YT921X_FUNC_MIB BIT(1) -#define YT921X_CHIP_ID 0x80008 -#define YT921X_CHIP_ID_MAJOR GENMASK(31, 16) -#define YT921X_EXT_CPU_PORT 0x8000c -#define YT921X_EXT_CPU_PORT_TAG_EN BIT(15) -#define YT921X_EXT_CPU_PORT_PORT_EN BIT(14) -#define YT921X_EXT_CPU_PORT_PORT_M GENMASK(3, 0) -#define YT921X_EXT_CPU_PORT_PORT(x) FIELD_PREP(YT921X_EXT_CPU_PORT_PORT_M, (x)) -#define YT921X_CPU_TAG_TPID 0x80010 -#define YT921X_CPU_TAG_TPID_TPID_M GENMASK(15, 0) -/* Same as ETH_P_YT921X, but this represents the true HW default, while the - * former is a local convention chosen by us. - */ -#define YT921X_CPU_TAG_TPID_TPID_DEFAULT 0x9988 -#define YT921X_PVID_SEL 0x80014 -#define YT921X_PVID_SEL_SVID_PORTn(port) BIT(port) -#define YT921X_SERDES_CTRL 0x80028 -#define YT921X_SERDES_CTRL_PORTn_TEST(port) BIT((port) - 3) -#define YT921X_SERDES_CTRL_PORTn(port) BIT((port) - 8) -#define YT921X_IO_LEVEL 0x80030 -#define YT9215_IO_LEVEL_NORMAL_M GENMASK(5, 4) -#define YT9215_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9215_IO_LEVEL_NORMAL_M, (x)) -#define YT9215_IO_LEVEL_NORMAL_3V3 YT9215_IO_LEVEL_NORMAL(0) -#define YT9215_IO_LEVEL_NORMAL_1V8 YT9215_IO_LEVEL_NORMAL(3) -#define YT9215_IO_LEVEL_RGMII1_M GENMASK(3, 2) -#define YT9215_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII1_M, (x)) -#define YT9215_IO_LEVEL_RGMII1_3V3 YT9215_IO_LEVEL_RGMII1(0) -#define YT9215_IO_LEVEL_RGMII1_2V5 YT9215_IO_LEVEL_RGMII1(1) -#define YT9215_IO_LEVEL_RGMII1_1V8 YT9215_IO_LEVEL_RGMII1(2) -#define YT9215_IO_LEVEL_RGMII0_M GENMASK(1, 0) -#define YT9215_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9215_IO_LEVEL_RGMII0_M, (x)) -#define YT9215_IO_LEVEL_RGMII0_3V3 YT9215_IO_LEVEL_RGMII0(0) -#define YT9215_IO_LEVEL_RGMII0_2V5 YT9215_IO_LEVEL_RGMII0(1) -#define YT9215_IO_LEVEL_RGMII0_1V8 YT9215_IO_LEVEL_RGMII0(2) -#define YT9218_IO_LEVEL_RGMII1_M GENMASK(5, 4) -#define YT9218_IO_LEVEL_RGMII1(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII1_M, (x)) -#define YT9218_IO_LEVEL_RGMII1_3V3 YT9218_IO_LEVEL_RGMII1(0) -#define YT9218_IO_LEVEL_RGMII1_2V5 YT9218_IO_LEVEL_RGMII1(1) -#define YT9218_IO_LEVEL_RGMII1_1V8 YT9218_IO_LEVEL_RGMII1(2) -#define YT9218_IO_LEVEL_RGMII0_M GENMASK(3, 2) -#define YT9218_IO_LEVEL_RGMII0(x) FIELD_PREP(YT9218_IO_LEVEL_RGMII0_M, (x)) -#define YT9218_IO_LEVEL_RGMII0_3V3 YT9218_IO_LEVEL_RGMII0(0) -#define YT9218_IO_LEVEL_RGMII0_2V5 YT9218_IO_LEVEL_RGMII0(1) -#define YT9218_IO_LEVEL_RGMII0_1V8 YT9218_IO_LEVEL_RGMII0(2) -#define YT9218_IO_LEVEL_NORMAL_M GENMASK(1, 0) -#define YT9218_IO_LEVEL_NORMAL(x) FIELD_PREP(YT9218_IO_LEVEL_NORMAL_M, (x)) -#define YT9218_IO_LEVEL_NORMAL_3V3 YT9218_IO_LEVEL_NORMAL(0) -#define YT9218_IO_LEVEL_NORMAL_1V8 YT9218_IO_LEVEL_NORMAL(3) -#define YT921X_MAC_ADDR_HI2 0x80080 -#define YT921X_MAC_ADDR_LO4 0x80084 -#define YT921X_SERDESn(port) (0x8008c + 4 * ((port) - 8)) -#define YT921X_SERDES_MODE_M GENMASK(9, 7) -#define YT921X_SERDES_MODE(x) FIELD_PREP(YT921X_SERDES_MODE_M, (x)) -#define YT921X_SERDES_MODE_SGMII YT921X_SERDES_MODE(0) -#define YT921X_SERDES_MODE_REVSGMII YT921X_SERDES_MODE(1) -#define YT921X_SERDES_MODE_1000BASEX YT921X_SERDES_MODE(2) -#define YT921X_SERDES_MODE_100BASEX YT921X_SERDES_MODE(3) -#define YT921X_SERDES_MODE_2500BASEX YT921X_SERDES_MODE(4) -#define YT921X_SERDES_RX_PAUSE BIT(6) -#define YT921X_SERDES_TX_PAUSE BIT(5) -#define YT921X_SERDES_LINK BIT(4) /* force link */ -#define YT921X_SERDES_DUPLEX_FULL BIT(3) -#define YT921X_SERDES_SPEED_M GENMASK(2, 0) -#define YT921X_SERDES_SPEED(x) FIELD_PREP(YT921X_SERDES_SPEED_M, (x)) -#define YT921X_SERDES_SPEED_10 YT921X_SERDES_SPEED(0) -#define YT921X_SERDES_SPEED_100 YT921X_SERDES_SPEED(1) -#define YT921X_SERDES_SPEED_1000 YT921X_SERDES_SPEED(2) -#define YT921X_SERDES_SPEED_10000 YT921X_SERDES_SPEED(3) -#define YT921X_SERDES_SPEED_2500 YT921X_SERDES_SPEED(4) -#define YT921X_PORTn_CTRL(port) (0x80100 + 4 * (port)) -#define YT921X_PORT_CTRL_PAUSE_AN BIT(10) -#define YT921X_PORTn_STATUS(port) (0x80200 + 4 * (port)) -#define YT921X_PORT_LINK BIT(9) /* CTRL: auto negotiation */ -#define YT921X_PORT_HALF_PAUSE BIT(8) /* Half-duplex back pressure mode */ -#define YT921X_PORT_DUPLEX_FULL BIT(7) -#define YT921X_PORT_RX_PAUSE BIT(6) -#define YT921X_PORT_TX_PAUSE BIT(5) -#define YT921X_PORT_RX_MAC_EN BIT(4) -#define YT921X_PORT_TX_MAC_EN BIT(3) -#define YT921X_PORT_SPEED_M GENMASK(2, 0) -#define YT921X_PORT_SPEED(x) FIELD_PREP(YT921X_PORT_SPEED_M, (x)) -#define YT921X_PORT_SPEED_10 YT921X_PORT_SPEED(0) -#define YT921X_PORT_SPEED_100 YT921X_PORT_SPEED(1) -#define YT921X_PORT_SPEED_1000 YT921X_PORT_SPEED(2) -#define YT921X_PORT_SPEED_10000 YT921X_PORT_SPEED(3) -#define YT921X_PORT_SPEED_2500 YT921X_PORT_SPEED(4) -#define YT921X_PON_STRAP_FUNC 0x80320 -#define YT921X_PON_STRAP_VAL 0x80324 -#define YT921X_PON_STRAP_CAP 0x80328 -#define YT921X_PON_STRAP_EEE BIT(16) -#define YT921X_PON_STRAP_LOOP_DETECT BIT(7) -#define YT921X_MDIO_POLLINGn(port) (0x80364 + 4 * ((port) - 8)) -#define YT921X_MDIO_POLLING_DUPLEX_FULL BIT(4) -#define YT921X_MDIO_POLLING_LINK BIT(3) -#define YT921X_MDIO_POLLING_SPEED_M GENMASK(2, 0) -#define YT921X_MDIO_POLLING_SPEED(x) FIELD_PREP(YT921X_MDIO_POLLING_SPEED_M, (x)) -#define YT921X_MDIO_POLLING_SPEED_10 YT921X_MDIO_POLLING_SPEED(0) -#define YT921X_MDIO_POLLING_SPEED_100 YT921X_MDIO_POLLING_SPEED(1) -#define YT921X_MDIO_POLLING_SPEED_1000 YT921X_MDIO_POLLING_SPEED(2) -#define YT921X_MDIO_POLLING_SPEED_10000 YT921X_MDIO_POLLING_SPEED(3) -#define YT921X_MDIO_POLLING_SPEED_2500 YT921X_MDIO_POLLING_SPEED(4) -#define YT921X_SENSOR 0x8036c -#define YT921X_SENSOR_TEMP BIT(18) -#define YT921X_TEMP 0x80374 -#define YT921X_CHIP_MODE 0x80388 -#define YT921X_CHIP_MODE_MODE GENMASK(1, 0) -#define YT921X_XMII_CTRL 0x80394 -#define YT921X_XMII_CTRL_PORTn(port) BIT(9 - (port)) /* Yes, it's reversed */ -#define YT921X_XMIIn(port) (0x80400 + 8 * ((port) - 8)) -#define YT921X_XMII_MODE_M GENMASK(31, 29) -#define YT921X_XMII_MODE(x) FIELD_PREP(YT921X_XMII_MODE_M, (x)) -#define YT921X_XMII_MODE_MII YT921X_XMII_MODE(0) -#define YT921X_XMII_MODE_REVMII YT921X_XMII_MODE(1) -#define YT921X_XMII_MODE_RMII YT921X_XMII_MODE(2) -#define YT921X_XMII_MODE_REVRMII YT921X_XMII_MODE(3) -#define YT921X_XMII_MODE_RGMII YT921X_XMII_MODE(4) -#define YT921X_XMII_MODE_DISABLE YT921X_XMII_MODE(5) -#define YT921X_XMII_LINK BIT(19) /* force link */ -#define YT921X_XMII_EN BIT(18) -#define YT921X_XMII_SOFT_RST BIT(17) -#define YT921X_XMII_RGMII_TX_DELAY_150PS_M GENMASK(16, 13) -#define YT921X_XMII_RGMII_TX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_TX_DELAY_150PS_M, (x)) -#define YT921X_XMII_TX_CLK_IN BIT(11) -#define YT921X_XMII_RX_CLK_IN BIT(10) -#define YT921X_XMII_RGMII_TX_DELAY_2NS BIT(8) -#define YT921X_XMII_RGMII_TX_CLK_OUT BIT(7) -#define YT921X_XMII_RGMII_RX_DELAY_150PS_M GENMASK(6, 3) -#define YT921X_XMII_RGMII_RX_DELAY_150PS(x) FIELD_PREP(YT921X_XMII_RGMII_RX_DELAY_150PS_M, (x)) -#define YT921X_XMII_RMII_PHY_TX_CLK_OUT BIT(2) -#define YT921X_XMII_REVMII_TX_CLK_OUT BIT(1) -#define YT921X_XMII_REVMII_RX_CLK_OUT BIT(0) - -#define YT921X_MACn_FRAME(port) (0x81008 + 0x1000 * (port)) -#define YT921X_MAC_FRAME_SIZE_M GENMASK(21, 8) -#define YT921X_MAC_FRAME_SIZE(x) FIELD_PREP(YT921X_MAC_FRAME_SIZE_M, (x)) - -#define YT921X_EEEn_VAL(port) (0xa0000 + 0x40 * (port)) -#define YT921X_EEE_VAL_DATA BIT(1) - -#define YT921X_EEE_CTRL 0xb0000 -#define YT921X_EEE_CTRL_ENn(port) BIT(port) - -#define YT921X_MIB_CTRL 0xc0004 -#define YT921X_MIB_CTRL_CLEAN BIT(30) -#define YT921X_MIB_CTRL_PORT_M GENMASK(6, 3) -#define YT921X_MIB_CTRL_PORT(x) FIELD_PREP(YT921X_MIB_CTRL_PORT_M, (x)) -#define YT921X_MIB_CTRL_ONE_PORT BIT(1) -#define YT921X_MIB_CTRL_ALL_PORT BIT(0) -#define YT921X_MIBn_DATA0(port) (0xc0100 + 0x100 * (port)) -#define YT921X_MIBn_DATAm(port, x) (YT921X_MIBn_DATA0(port) + 4 * (x)) -#define YT921X_MIB_DATA_RX_BROADCAST 0x00 -#define YT921X_MIB_DATA_RX_PAUSE 0x04 -#define YT921X_MIB_DATA_RX_MULTICAST 0x08 -#define YT921X_MIB_DATA_RX_CRC_ERR 0x0c - -#define YT921X_MIB_DATA_RX_ALIGN_ERR 0x10 -#define YT921X_MIB_DATA_RX_UNDERSIZE_ERR 0x14 -#define YT921X_MIB_DATA_RX_FRAG_ERR 0x18 -#define YT921X_MIB_DATA_RX_PKT_SZ_64 0x1c - -#define YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127 0x20 -#define YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255 0x24 -#define YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511 0x28 -#define YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023 0x2c - -#define YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518 0x30 -#define YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX 0x34 -/* 0x38: unused */ -#define YT921X_MIB_DATA_RX_GOOD_BYTES 0x3c - -/* 0x40: 64 bytes */ -#define YT921X_MIB_DATA_RX_BAD_BYTES 0x44 -/* 0x48: 64 bytes */ -#define YT921X_MIB_DATA_RX_OVERSIZE_ERR 0x4c - -#define YT921X_MIB_DATA_RX_DROPPED 0x50 -#define YT921X_MIB_DATA_TX_BROADCAST 0x54 -#define YT921X_MIB_DATA_TX_PAUSE 0x58 -#define YT921X_MIB_DATA_TX_MULTICAST 0x5c - -#define YT921X_MIB_DATA_TX_UNDERSIZE_ERR 0x60 -#define YT921X_MIB_DATA_TX_PKT_SZ_64 0x64 -#define YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127 0x68 -#define YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255 0x6c - -#define YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511 0x70 -#define YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023 0x74 -#define YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518 0x78 -#define YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX 0x7c - -/* 0x80: unused */ -#define YT921X_MIB_DATA_TX_GOOD_BYTES 0x84 -/* 0x88: 64 bytes */ -#define YT921X_MIB_DATA_TX_COLLISION 0x8c - -#define YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION 0x90 -#define YT921X_MIB_DATA_TX_MULTIPLE_COLLISION 0x94 -#define YT921X_MIB_DATA_TX_SINGLE_COLLISION 0x98 -#define YT921X_MIB_DATA_TX_PKT 0x9c - -#define YT921X_MIB_DATA_TX_DEFERRED 0xa0 -#define YT921X_MIB_DATA_TX_LATE_COLLISION 0xa4 -#define YT921X_MIB_DATA_RX_OAM 0xa8 -#define YT921X_MIB_DATA_TX_OAM 0xac - -#define YT921X_EDATA_CTRL 0xe0000 -#define YT921X_EDATA_CTRL_ADDR_M GENMASK(15, 8) -#define YT921X_EDATA_CTRL_ADDR(x) FIELD_PREP(YT921X_EDATA_CTRL_ADDR_M, (x)) -#define YT921X_EDATA_CTRL_OP_M GENMASK(3, 0) -#define YT921X_EDATA_CTRL_OP(x) FIELD_PREP(YT921X_EDATA_CTRL_OP_M, (x)) -#define YT921X_EDATA_CTRL_READ YT921X_EDATA_CTRL_OP(5) -#define YT921X_EDATA_DATA 0xe0004 -#define YT921X_EDATA_DATA_DATA_M GENMASK(31, 24) -#define YT921X_EDATA_DATA_STATUS_M GENMASK(3, 0) -#define YT921X_EDATA_DATA_STATUS(x) FIELD_PREP(YT921X_EDATA_DATA_STATUS_M, (x)) -#define YT921X_EDATA_DATA_IDLE YT921X_EDATA_DATA_STATUS(3) -#define YT921X_SYS_CLK 0xe0040 -#define YT921X_SYS_CLK_SEL_M GENMASK(1, 0) /* unknown: 167M */ -#define YT9215_SYS_CLK_125M 0 -#define YT9218_SYS_CLK_167M 0 -#define YT921X_SYS_CLK_143M 1 - -#define YT921X_EXT_MBUS_OP 0x6a000 -#define YT921X_INT_MBUS_OP 0xf0000 -#define YT921X_MBUS_OP_START BIT(0) -#define YT921X_EXT_MBUS_CTRL 0x6a004 -#define YT921X_INT_MBUS_CTRL 0xf0004 -#define YT921X_MBUS_CTRL_PORT_M GENMASK(25, 21) -#define YT921X_MBUS_CTRL_PORT(x) FIELD_PREP(YT921X_MBUS_CTRL_PORT_M, (x)) -#define YT921X_MBUS_CTRL_REG_M GENMASK(20, 16) -#define YT921X_MBUS_CTRL_REG(x) FIELD_PREP(YT921X_MBUS_CTRL_REG_M, (x)) -#define YT921X_MBUS_CTRL_TYPE_M GENMASK(11, 8) /* wild guess */ -#define YT921X_MBUS_CTRL_TYPE(x) FIELD_PREP(YT921X_MBUS_CTRL_TYPE_M, (x)) -#define YT921X_MBUS_CTRL_TYPE_C22 YT921X_MBUS_CTRL_TYPE(4) -#define YT921X_MBUS_CTRL_OP_M GENMASK(3, 2) /* wild guess */ -#define YT921X_MBUS_CTRL_OP(x) FIELD_PREP(YT921X_MBUS_CTRL_OP_M, (x)) -#define YT921X_MBUS_CTRL_WRITE YT921X_MBUS_CTRL_OP(1) -#define YT921X_MBUS_CTRL_READ YT921X_MBUS_CTRL_OP(2) -#define YT921X_EXT_MBUS_DOUT 0x6a008 -#define YT921X_INT_MBUS_DOUT 0xf0008 -#define YT921X_EXT_MBUS_DIN 0x6a00c -#define YT921X_INT_MBUS_DIN 0xf000c - -#define YT921X_PORTn_EGR(port) (0x100000 + 4 * (port)) -#define YT921X_PORT_EGR_TPID_CTAG_M GENMASK(5, 4) -#define YT921X_PORT_EGR_TPID_CTAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_CTAG_M, (x)) -#define YT921X_PORT_EGR_TPID_STAG_M GENMASK(3, 2) -#define YT921X_PORT_EGR_TPID_STAG(x) FIELD_PREP(YT921X_PORT_EGR_TPID_STAG_M, (x)) -#define YT921X_TPID_EGRn(x) (0x100300 + 4 * (x)) /* [0, 3] */ -#define YT921X_TPID_EGR_TPID_M GENMASK(15, 0) - -#define YT921X_IPM_DSCPn(n) (0x180000 + 4 * (n)) /* Internal Priority Map */ -#define YT921X_IPM_PCPn(map, dei, pcp) (0x180100 + 4 * (16 * (map) + 8 * (dei) + (pcp))) -#define YT921X_IPM_PRIO_M GENMASK(4, 2) -#define YT921X_IPM_PRIO(x) FIELD_PREP(YT921X_IPM_PRIO_M, (x)) -#define YT921X_IPM_COLOR_M GENMASK(1, 0) -#define YT921X_IPM_COLOR(x) FIELD_PREP(YT921X_IPM_COLOR_M, (x)) -#define YT921X_IPM_COLOR_GREEN YT921X_IPM_COLOR(0) -#define YT921X_IPM_COLOR_YELLOW YT921X_IPM_COLOR(1) -#define YT921X_IPM_COLOR_RED YT921X_IPM_COLOR(2) -#define YT921X_PORTn_QOS(port) (0x180180 + 4 * (port)) -#define YT921X_PORT_QOS_CVLAN_PRIO_MAP_ID BIT(5) -#define YT921X_PORT_QOS_SVLAN_PRIO_MAP_ID BIT(4) -#define YT921X_PORT_QOS_PRIO_M GENMASK(3, 1) -#define YT921X_PORT_QOS_PRIO(x) FIELD_PREP(YT921X_PORT_QOS_PRIO_M, (x)) -#define YT921X_PORT_QOS_PRIO_EN BIT(0) -#define YT921X_PORTn_PRIO_ORD(port) (0x180200 + 4 * (port)) -#define YT921X_PORT_PRIO_ORD_APPm_M(m) GENMASK(3 * (m) + 2, 3 * (m)) -#define YT921X_PORT_PRIO_ORD_APPm(m, x) ((x) << (3 * (m))) /* 0: disabled, except PORT_QOS_PRIO */ - -enum yt921x_app_selector { - YT921X_APP_SEL_MAC_SA, - YT921X_APP_SEL_MAC_DA, - YT921X_APP_SEL_VID, - YT921X_APP_SEL_ACL, - YT921X_APP_SEL_DSCP, - YT921X_APP_SEL_CVLAN_PCP, - YT921X_APP_SEL_SVLAN_PCP, - /* The physical port, i.e. YT921X_PORT_QOS_PRIO */ - YT921X_APP_SEL_PORT, - YT921X_APP_SEL_NUM -}; - -#define YT921X_VLAN_IGR_FILTER 0x180280 -#define YT921X_VLAN_IGR_FILTER_PORTn_BYPASS_IGMP(port) BIT((port) + 11) -#define YT921X_VLAN_IGR_FILTER_PORTn(port) BIT(port) -#define YT921X_PORTn_ISOLATION(port) (0x180294 + 4 * (port)) -#define YT921X_PORT_ISOLATION_BLOCKn(port) BIT(port) -#define YT921X_STPn(n) (0x18038c + 4 * (n)) -#define YT921X_STP_PORTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) -#define YT921X_STP_PORTn(port, x) ((x) << (2 * (port))) -#define YT921X_STP_PORTn_DISABLED(port) YT921X_STP_PORTn(port, 0) -#define YT921X_STP_PORTn_LEARNING(port) YT921X_STP_PORTn(port, 1) -#define YT921X_STP_PORTn_BLOCKING(port) YT921X_STP_PORTn(port, 2) -#define YT921X_STP_PORTn_FORWARD(port) YT921X_STP_PORTn(port, 3) -#define YT921X_PORTn_LEARN(port) (0x1803d0 + 4 * (port)) -#define YT921X_PORT_LEARN_VID_LEARN_MULTI_EN BIT(22) -#define YT921X_PORT_LEARN_VID_LEARN_MODE BIT(21) -#define YT921X_PORT_LEARN_VID_LEARN_EN BIT(20) -#define YT921X_PORT_LEARN_SUSPEND_COPY_EN BIT(19) -#define YT921X_PORT_LEARN_SUSPEND_DROP_EN BIT(18) -#define YT921X_PORT_LEARN_DIS BIT(17) -#define YT921X_PORT_LEARN_LIMIT_EN BIT(16) -#define YT921X_PORT_LEARN_LIMIT_M GENMASK(15, 8) -#define YT921X_PORT_LEARN_LIMIT(x) FIELD_PREP(YT921X_PORT_LEARN_LIMIT_M, (x)) -#define YT921X_PORT_LEARN_DROP_ON_EXCEEDED BIT(2) -#define YT921X_PORT_LEARN_MODE_M GENMASK(1, 0) -#define YT921X_PORT_LEARN_MODE(x) FIELD_PREP(YT921X_PORT_LEARN_MODE_M, (x)) -#define YT921X_PORT_LEARN_MODE_AUTO YT921X_PORT_LEARN_MODE(0) -#define YT921X_PORT_LEARN_MODE_AUTO_AND_COPY YT921X_PORT_LEARN_MODE(1) -#define YT921X_PORT_LEARN_MODE_CPU_CONTROL YT921X_PORT_LEARN_MODE(2) -#define YT921X_AGEING 0x180440 -#define YT921X_AGEING_INTERVAL_M GENMASK(15, 0) -#define YT921X_FDB_IN0 0x180454 -#define YT921X_FDB_IN1 0x180458 -#define YT921X_FDB_IN2 0x18045c -#define YT921X_FDB_OP 0x180460 -#define YT921X_FDB_OP_INDEX_M GENMASK(22, 11) -#define YT921X_FDB_OP_INDEX(x) FIELD_PREP(YT921X_FDB_OP_INDEX_M, (x)) -#define YT921X_FDB_OP_MODE_INDEX BIT(10) /* mac+fid / index */ -#define YT921X_FDB_OP_FLUSH_MCAST BIT(9) /* ucast / mcast */ -#define YT921X_FDB_OP_FLUSH_M GENMASK(8, 7) -#define YT921X_FDB_OP_FLUSH(x) FIELD_PREP(YT921X_FDB_OP_FLUSH_M, (x)) -#define YT921X_FDB_OP_FLUSH_ALL YT921X_FDB_OP_FLUSH(0) -#define YT921X_FDB_OP_FLUSH_PORT YT921X_FDB_OP_FLUSH(1) -#define YT921X_FDB_OP_FLUSH_PORT_VID YT921X_FDB_OP_FLUSH(2) -#define YT921X_FDB_OP_FLUSH_VID YT921X_FDB_OP_FLUSH(3) -#define YT921X_FDB_OP_FLUSH_STATIC BIT(6) -#define YT921X_FDB_OP_NEXT_TYPE_M GENMASK(5, 4) -#define YT921X_FDB_OP_NEXT_TYPE(x) FIELD_PREP(YT921X_FDB_OP_NEXT_TYPE_M, (x)) -#define YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT YT921X_FDB_OP_NEXT_TYPE(0) -#define YT921X_FDB_OP_NEXT_TYPE_UCAST_VID YT921X_FDB_OP_NEXT_TYPE(1) -#define YT921X_FDB_OP_NEXT_TYPE_UCAST YT921X_FDB_OP_NEXT_TYPE(2) -#define YT921X_FDB_OP_NEXT_TYPE_MCAST YT921X_FDB_OP_NEXT_TYPE(3) -#define YT921X_FDB_OP_OP_M GENMASK(3, 1) -#define YT921X_FDB_OP_OP(x) FIELD_PREP(YT921X_FDB_OP_OP_M, (x)) -#define YT921X_FDB_OP_OP_ADD YT921X_FDB_OP_OP(0) -#define YT921X_FDB_OP_OP_DEL YT921X_FDB_OP_OP(1) -#define YT921X_FDB_OP_OP_GET_ONE YT921X_FDB_OP_OP(2) -#define YT921X_FDB_OP_OP_GET_NEXT YT921X_FDB_OP_OP(3) -#define YT921X_FDB_OP_OP_FLUSH YT921X_FDB_OP_OP(4) -#define YT921X_FDB_OP_START BIT(0) -#define YT921X_FDB_RESULT 0x180464 -#define YT921X_FDB_RESULT_DONE BIT(15) -#define YT921X_FDB_RESULT_NOTFOUND BIT(14) -#define YT921X_FDB_RESULT_OVERWRITED BIT(13) -#define YT921X_FDB_RESULT_INDEX_M GENMASK(11, 0) -#define YT921X_FDB_RESULT_INDEX(x) FIELD_PREP(YT921X_FDB_RESULT_INDEX_M, (x)) -#define YT921X_FDB_OUT0 0x1804b0 -#define YT921X_FDB_IO0_ADDR_HI4_M GENMASK(31, 0) -#define YT921X_FDB_OUT1 0x1804b4 -#define YT921X_FDB_IO1_EGR_PRIO_EN BIT(31) -#define YT921X_FDB_IO1_STATUS_M GENMASK(30, 28) -#define YT921X_FDB_IO1_STATUS(x) FIELD_PREP(YT921X_FDB_IO1_STATUS_M, (x)) -#define YT921X_FDB_IO1_STATUS_INVALID YT921X_FDB_IO1_STATUS(0) -#define YT921X_FDB_IO1_STATUS_MIN_TIME YT921X_FDB_IO1_STATUS(1) -#define YT921X_FDB_IO1_STATUS_MOVE_AGING_MAX_TIME YT921X_FDB_IO1_STATUS(3) -#define YT921X_FDB_IO1_STATUS_MAX_TIME YT921X_FDB_IO1_STATUS(5) -#define YT921X_FDB_IO1_STATUS_PENDING YT921X_FDB_IO1_STATUS(6) -#define YT921X_FDB_IO1_STATUS_STATIC YT921X_FDB_IO1_STATUS(7) -#define YT921X_FDB_IO1_FID_M GENMASK(27, 16) /* filtering ID (VID) */ -#define YT921X_FDB_IO1_FID(x) FIELD_PREP(YT921X_FDB_IO1_FID_M, (x)) -#define YT921X_FDB_IO1_ADDR_LO2_M GENMASK(15, 0) -#define YT921X_FDB_OUT2 0x1804b8 -#define YT921X_FDB_IO2_MOVE_AGING_STATUS_M GENMASK(31, 30) -#define YT921X_FDB_IO2_IGR_DROP BIT(29) -#define YT921X_FDB_IO2_EGR_PORTS_M GENMASK(28, 18) -#define YT921X_FDB_IO2_EGR_PORTS(x) FIELD_PREP(YT921X_FDB_IO2_EGR_PORTS_M, (x)) -#define YT921X_FDB_IO2_EGR_DROP BIT(17) -#define YT921X_FDB_IO2_COPY_TO_CPU BIT(16) -#define YT921X_FDB_IO2_IGR_PRIO_EN BIT(15) -#define YT921X_FDB_IO2_PRIO_M GENMASK(14, 12) -#define YT921X_FDB_IO2_PRIO(x) FIELD_PREP(YT921X_FDB_IO2_PRIO_M, (x)) -#define YT921X_FDB_IO2_NEW_VID_M GENMASK(11, 0) -#define YT921X_FDB_IO2_NEW_VID(x) FIELD_PREP(YT921X_FDB_IO2_NEW_VID_M, (x)) -#define YT921X_FILTER_UNK_UCAST 0x180508 -#define YT921X_FILTER_UNK_MCAST 0x18050c -#define YT921X_FILTER_MCAST 0x180510 -#define YT921X_FILTER_BCAST 0x180514 -#define YT921X_FILTER_PORTS_M GENMASK(10, 0) -#define YT921X_FILTER_PORTS(x) FIELD_PREP(YT921X_FILTER_PORTS_M, (x)) -#define YT921X_FILTER_PORTn(port) BIT(port) -#define YT921X_VLAN_EGR_FILTER 0x180598 -#define YT921X_VLAN_EGR_FILTER_PORTn(port) BIT(port) -#define YT921X_LAG_GROUPn(n) (0x1805a8 + 4 * (n)) -#define YT921X_LAG_GROUP_PORTS_M GENMASK(13, 3) -#define YT921X_LAG_GROUP_PORTS(x) FIELD_PREP(YT921X_LAG_GROUP_PORTS_M, (x)) -#define YT921X_LAG_GROUP_MEMBER_NUM_M GENMASK(2, 0) -#define YT921X_LAG_GROUP_MEMBER_NUM(x) FIELD_PREP(YT921X_LAG_GROUP_MEMBER_NUM_M, (x)) -#define YT921X_LAG_MEMBERnm(n, m) (0x1805b0 + 4 * (4 * (n) + (m))) -#define YT921X_LAG_MEMBER_PORT_M GENMASK(3, 0) -#define YT921X_LAG_MEMBER_PORT(x) FIELD_PREP(YT921X_LAG_MEMBER_PORT_M, (x)) -#define YT921X_CPU_COPY 0x180690 -#define YT921X_CPU_COPY_FORCE_INT_PORT BIT(2) -#define YT921X_CPU_COPY_TO_INT_CPU BIT(1) -#define YT921X_CPU_COPY_TO_EXT_CPU BIT(0) -#define YT921X_ACL_PERMIT_UNMATCH 0x1806a0 -#define YT921X_ACL_PERMIT_UNMATCH_PORTS_M GENMASK(10, 0) -#define YT921X_ACL_PERMIT_UNMATCH_PORTS(x) FIELD_PREP(YT921X_ACL_PERMIT_UNMATCH_PORTS_M, (x)) -#define YT921X_ACL_PERMIT_UNMATCH_PORTn(port) BIT(port) -#define YT921X_ACT_UNK_UCAST 0x180734 -#define YT921X_ACT_UNK_MCAST 0x180738 -#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_RMA BIT(23) -#define YT921X_ACT_UNK_MCAST_BYPASS_DROP_IGMP BIT(22) -#define YT921X_ACT_UNK_ACTn_M(port) GENMASK(2 * (port) + 1, 2 * (port)) -#define YT921X_ACT_UNK_ACTn(port, x) ((x) << (2 * (port))) -#define YT921X_ACT_UNK_ACTn_FORWARD(port) YT921X_ACT_UNK_ACTn(port, 0) /* flood */ -#define YT921X_ACT_UNK_ACTn_TRAP(port) YT921X_ACT_UNK_ACTn(port, 1) /* steer to CPU */ -#define YT921X_ACT_UNK_ACTn_DROP(port) YT921X_ACT_UNK_ACTn(port, 2) /* discard */ -/* NEVER use this action; see comments in the tag driver */ -#define YT921X_ACT_UNK_ACTn_COPY(port) YT921X_ACT_UNK_ACTn(port, 3) /* flood and copy */ -#define YT921X_FDB_HW_FLUSH 0x180958 -#define YT921X_FDB_HW_FLUSH_ON_LINKDOWN BIT(0) - -#define YT921X_VLANn_CTRL(vlan) (0x188000 + 8 * (vlan)) -#define YT921X_VLAN_CTRLb_UNTAG_PORTS_M GENMASK(18, 8) -#define YT921X_VLAN_CTRLb_UNTAG_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLb_UNTAG_PORTS_M, (x)) -#define YT921X_VLAN_CTRLb_UNTAG_PORTn(port) BIT((port) + 8) -#define YT921X_VLAN_CTRLb_STP_ID_M GENMASK(7, 4) -#define YT921X_VLAN_CTRLb_STP_ID(x) FIELD_PREP(YT921X_VLAN_CTRLb_STP_ID_M, (x)) -#define YT921X_VLAN_CTRLb_SVLAN_EN BIT(3) -#define YT921X_VLAN_CTRLab_FID_M GENMASK_ULL(34, 23) -#define YT921X_VLAN_CTRLab_FID(x) FIELD_PREP(YT921X_VLAN_CTRLab_FID_M, (x)) -#define YT921X_VLAN_CTRLa_LEARN_DIS BIT(22) -#define YT921X_VLAN_CTRLa_PRIO_EN BIT(21) -#define YT921X_VLAN_CTRLa_PRIO_M GENMASK(20, 18) -#define YT921X_VLAN_CTRLa_PRIO(x) FIELD_PREP(YT921X_VLAN_CTRLa_PRIO_M, (x)) -#define YT921X_VLAN_CTRLa_PORTS_M GENMASK(17, 7) -#define YT921X_VLAN_CTRLa_PORTS(x) FIELD_PREP(YT921X_VLAN_CTRLa_PORTS_M, (x)) -#define YT921X_VLAN_CTRLa_PORTn(port) BIT((port) + 7) -#define YT921X_VLAN_CTRLa_BYPASS_1X_AC BIT(6) -#define YT921X_VLAN_CTRLa_METER_EN BIT(5) -#define YT921X_VLAN_CTRLa_METER_ID_M GENMASK(4, 0) - -#define YT921X_ACLn_ACT(n) (0x1c0000 + 0x10 * (n)) -#define YT921X_ACL_ACTc_STAG_M GENMASK(26, 25) -#define YT921X_ACL_ACTc_STAG(x) FIELD_PREP(YT921X_ACL_ACTc_STAG_M, (x)) -#define YT921X_ACL_ACTc_STAG_DONTCARE YT921X_ACL_ACTc_STAG(0) -#define YT921X_ACL_ACTc_STAG_UNTAG YT921X_ACL_ACTc_STAG(1) -#define YT921X_ACL_ACTc_STAG_TAG YT921X_ACL_ACTc_STAG(2) -#define YT921X_ACL_ACTc_STAG_KEEP YT921X_ACL_ACTc_STAG(3) -#define YT921X_ACL_ACTc_CTAG_M GENMASK(24, 23) -#define YT921X_ACL_ACTc_CTAG(x) FIELD_PREP(YT921X_ACL_ACTc_CTAG_M, (x)) -#define YT921X_ACL_ACTc_CTAG_DONTCARE YT921X_ACL_ACTc_CTAG(0) -#define YT921X_ACL_ACTc_CTAG_UNTAG YT921X_ACL_ACTc_CTAG(1) -#define YT921X_ACL_ACTc_CTAG_TAG YT921X_ACL_ACTc_CTAG(2) -#define YT921X_ACL_ACTc_CTAG_KEEP YT921X_ACL_ACTc_CTAG(3) -#define YT921X_ACL_ACTc_FWD_M GENMASK(22, 21) -#define YT921X_ACL_ACTc_FWD(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_M, (x)) -#define YT921X_ACL_ACTc_FWD_FWD YT921X_ACL_ACTc_FWD(0) -#define YT921X_ACL_ACTc_FWD_COPY YT921X_ACL_ACTc_FWD(1) -#define YT921X_ACL_ACTc_FWD_REDIR YT921X_ACL_ACTc_FWD(2) -#define YT921X_ACL_ACTc_FWD_TRAP YT921X_ACL_ACTc_FWD(3) -#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M GENMASK(20, 10) -#define YT921X_ACL_ACTc_FWD_REDIR_DPORTS(x) FIELD_PREP(YT921X_ACL_ACTc_FWD_REDIR_DPORTS_M, (x)) -#define YT921X_ACL_ACTc_FWD_REDIR_DPORTn(port) BIT((port) + 10) -#define YT921X_ACL_ACTc_FWD_EN BIT(9) -#define YT921X_ACL_ACTc_SDEI BIT(8) -#define YT921X_ACL_ACTc_SDEI_REPLACE BIT(7) -#define YT921X_ACL_ACTc_SPRI_M GENMASK(6, 4) -#define YT921X_ACL_ACTc_SPRI(x) FIELD_PREP(YT921X_ACL_ACTc_SPRI_M, (x)) -#define YT921X_ACL_ACTc_SPRI_REPLACE BIT(3) -#define YT921X_ACL_ACTbc_SVID_M GENMASK_ULL(34, 23) -#define YT921X_ACL_ACTbc_SVID(x) FIELD_PREP(YT921X_ACL_ACTbc_SVID_M, (x)) -#define YT921X_ACL_ACTb_SVID_REPLACE BIT(22) -#define YT921X_ACL_ACTb_CDEI BIT(21) -#define YT921X_ACL_ACTb_CDEI_REPLACE BIT(20) -#define YT921X_ACL_ACTb_CPRI_M GENMASK(19, 17) -#define YT921X_ACL_ACTb_CPRI(x) FIELD_PREP(YT921X_ACL_ACTb_CPRI_M, (x)) -#define YT921X_ACL_ACTb_CPRI_REPLACE BIT(16) -#define YT921X_ACL_ACTb_CVID_M GENMASK(15, 4) -#define YT921X_ACL_ACTb_CVID(x) FIELD_PREP(YT921X_ACL_ACTb_CVID_M, (x)) -#define YT921X_ACL_ACTb_CVID_REPLACE BIT(3) -#define YT921X_ACL_ACTb_PRIO_M GENMASK(2, 0) -#define YT921X_ACL_ACTb_PRIO(x) FIELD_PREP(YT921X_ACL_ACTb_PRIO_M, (x)) -#define YT921X_ACL_ACTa_PRIO_EN BIT(31) -#define YT921X_ACL_ACTa_COLOR_M GENMASK(30, 29) -#define YT921X_ACL_ACTa_COLOR(x) FIELD_PREP(YT921X_ACL_ACTa_COLOR_M, (x)) -#define YT921X_ACL_ACTa_COLOR_GREEN YT921X_ACL_ACTa_COLOR(0) -#define YT921X_ACL_ACTa_COLOR_YELLOW YT921X_ACL_ACTa_COLOR(1) -#define YT921X_ACL_ACTa_COLOR_RED YT921X_ACL_ACTa_COLOR(2) -#define YT921X_ACL_ACTa_COLOR_EN BIT(28) -#define YT921X_ACL_ACTa_DSCP_M GENMASK(27, 22) -#define YT921X_ACL_ACTa_DSCP(x) FIELD_PREP(YT921X_ACL_ACTa_DSCP_M, (x)) -#define YT921X_ACL_ACTa_DSCP_REPLACE BIT(21) -#define YT921X_ACL_ACTa_METER_ID_M GENMASK(20, 15) -#define YT921X_ACL_ACTa_METER_ID(x) FIELD_PREP(YT921X_ACL_ACTa_METER_ID_M, (x)) -#define YT921X_ACL_ACTa_METER_EN BIT(14) -#define YT921X_ACL_ACTa_MIRROR_EN BIT(13) -#define YT921X_ACL_ACTa_FLOWSTAT_EN BIT(12) -#define YT921X_ACL_ACTa_FLOWSTAT_ID_M GENMASK(11, 6) -#define YT921X_ACL_ACTa_FLOWSTAT_ID(x) FIELD_PREP(YT921X_ACL_ACTa_FLOWSTAT_ID_M, (x)) -#define YT921X_ACL_ACTa_GPIO_EN BIT(5) -#define YT921X_ACL_ACTa_GPIO_PIN_M GENMASK(4, 1) -#define YT921X_ACL_ACTa_GPIO_PIN(x) FIELD_PREP(YT921X_ACL_ACTa_GPIO_PIN_M, (x)) -#define YT921X_ACL_ACTa_INTR_EN BIT(0) -#define YT921X_ACL_BLK_KEEP 0x201000 -#define YT921X_ACL_BLK_KEEP_GRPIDn_M(bin) (7 << (4 * (bin) + 1)) -#define YT921X_ACL_BLK_KEEP_GRPIDn(bin, x) ((x) << (4 * (bin) + 1)) -#define YT921X_ACL_BLK_KEEP_KEEPn(bin) BIT(4 * (bin)) -#define YT921X_ACL_PORT 0x202000 -#define YT921X_ACL_PORT_PORTS_M GENMASK(10, 0) -#define YT921X_ACL_PORT_PORTS(x) FIELD_PREP(YT921X_ACL_PORT_PORTS_M, (x)) -#define YT921X_ACL_PORT_PORTn(port) BIT(port) -#define YT921X_ACL_BLK_CMD 0x202004 -#define YT921X_ACL_BLK_CMD_BLKID_M GENMASK(6, 1) -#define YT921X_ACL_BLK_CMD_BLKID(x) FIELD_PREP(YT921X_ACL_BLK_CMD_BLKID_M, (x)) -#define YT921X_ACL_BLK_CMD_MODIFY BIT(0) -#define YT921X_ACLn_ENTRY(blk) (0x203000 + 4 * (blk)) -#define YT921X_ACL_ENTRY_GRPIDm_M(bin) (7 << (4 * (bin) + 1)) -#define YT921X_ACL_ENTRY_GRPIDm(bin, x) ((x) << (4 * (bin) + 1)) -#define YT921X_ACL_ENTRY_ENm(bin) BIT(4 * (bin)) -#define YT921X_ACLn_KEYm(blk, bin) (0x204000 + 0x200 * (bin) + 8 * (blk)) -#define YT921X_ACL_KEYb_ORD_M GENMASK(29, 21) -#define YT921X_ACL_KEYb_ORD(x) FIELD_PREP(YT921X_ACL_KEYb_ORD_M, (x)) -#define YT921X_ACL_KEYb_SPORTS_M GENMASK(20, 10) -#define YT921X_ACL_KEYb_SPORTS(x) FIELD_PREP(YT921X_ACL_KEYb_SPORTS_M, (x)) -#define YT921X_ACL_KEYb_SPORTn(port) BIT((port) + 10) -#define YT921X_ACL_KEYb_REVERSE BIT(9) /* reverse match */ -#define YT921X_ACL_KEYb_TYPE_M GENMASK(8, 4) -#define YT921X_ACL_KEYb_TYPE(x) FIELD_PREP(YT921X_ACL_KEYb_TYPE_M, (x)) -/* KEY_* fields need no masks */ -#define YT921X_ACLn_MASKm(blk, bin) (0x205000 + 0x200 * (bin) + 8 * (blk)) - -enum yt921x_acl_type { - YT921X_ACL_TYPE_NA, - YT921X_ACL_TYPE_MAC_DA0, - YT921X_ACL_TYPE_MAC_SA0, - YT921X_ACL_TYPE_MAC_DA1_SA1, - YT921X_ACL_TYPE_VLAN, - YT921X_ACL_TYPE_VTAG, - YT921X_ACL_TYPE_IPV4_DA, - YT921X_ACL_TYPE_IPV4_SA, - YT921X_ACL_TYPE_IPV6_DA0, - YT921X_ACL_TYPE_IPV6_DA1, - YT921X_ACL_TYPE_IPV6_DA2, - YT921X_ACL_TYPE_IPV6_DA3, - YT921X_ACL_TYPE_IPV6_SA0, - YT921X_ACL_TYPE_IPV6_SA1, - YT921X_ACL_TYPE_IPV6_SA2, - YT921X_ACL_TYPE_IPV6_SA3, - YT921X_ACL_TYPE_MISC, - YT921X_ACL_TYPE_L4, - YT921X_ACL_TYPE_UDF0, - YT921X_ACL_TYPE_UDF1, - YT921X_ACL_TYPE_UDF2, - YT921X_ACL_TYPE_UDF3, - YT921X_ACL_TYPE_UDF4, - YT921X_ACL_TYPE_UDF5, - YT921X_ACL_TYPE_UDF6, - YT921X_ACL_TYPE_UDF7, - YT921X_ACL_TYPE_ETHERTYPE, - YT921X_ACL_TYPE_NUM -}; - -/* Range: turn KEY:MASK into MIN:MAX */ - -#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M GENMASK(3, 0) -#define YT921X_ACL_BINb_MAC_xA0_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_xA0_L3_TYPE_M, (x)) -#define YT921X_ACL_BINa_MAC_xA0_MAC_xA0_M GENMASK(31, 0) - -#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MAC_DA1_SA1_L2_TYPE_M, (x)) -#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_DA1_M GENMASK(31, 16) -#define YT921X_ACL_BINa_MAC_DA1_SA1_MAC_SA1_M GENMASK(15, 0) - -#define YT921X_ACL_KEYb_VLAN_SVID_RANGE_EN BIT(31) -#define YT921X_ACL_KEYb_VLAN_CVID_RANGE_EN BIT(30) -#define YT921X_ACL_BINb_VLAN_CDEI BIT(3) -#define YT921X_ACL_BINb_VLAN_CPRI_M GENMASK(2, 0) -#define YT921X_ACL_BINb_VLAN_CPRI(x) FIELD_PREP(YT921X_ACL_BINb_VLAN_CPRI_M, (x)) -#define YT921X_ACL_BINa_VLAN_CTAG_FMT_M GENMASK(31, 30) -#define YT921X_ACL_BINa_VLAN_CTAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CTAG_FMT_M, (x)) -#define YT921X_ACL_BINa_VLAN_SDEI BIT(29) -#define YT921X_ACL_BINa_VLAN_SPRI_M GENMASK(28, 26) -#define YT921X_ACL_BINa_VLAN_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SPRI_M, (x)) -#define YT921X_ACL_BINa_VLAN_STAG_FMT_M GENMASK(25, 24) -#define YT921X_ACL_BINa_VLAN_STAG_FMT(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_STAG_FMT_M, (x)) -#define YT921X_ACL_BINa_VLAN_SVID_M GENMASK(23, 12) -#define YT921X_ACL_BINa_VLAN_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_SVID_M, (x)) -#define YT921X_ACL_BINa_VLAN_CVID_M GENMASK(11, 0) -#define YT921X_ACL_BINa_VLAN_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VLAN_CVID_M, (x)) - -#define YT921X_ACL_KEYb_VTAG_SVID_RANGE_EN BIT(31) -#define YT921X_ACL_KEYb_VTAG_CVID_RANGE_EN BIT(30) -#define YT921X_ACL_BINa_VTAG_CDEI BIT(31) -#define YT921X_ACL_BINa_VTAG_CPRI_M GENMASK(30, 28) -#define YT921X_ACL_BINa_VTAG_CPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CPRI_M, (x)) -#define YT921X_ACL_BINa_VTAG_SDEI BIT(27) -#define YT921X_ACL_BINa_VTAG_SPRI_M GENMASK(26, 24) -#define YT921X_ACL_BINa_VTAG_SPRI(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SPRI_M, (x)) -#define YT921X_ACL_BINa_VTAG_SVID_M GENMASK(23, 12) -#define YT921X_ACL_BINa_VTAG_SVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_SVID_M, (x)) -#define YT921X_ACL_BINa_VTAG_CVID_M GENMASK(11, 0) -#define YT921X_ACL_BINa_VTAG_CVID(x) FIELD_PREP(YT921X_ACL_BINa_VTAG_CVID_M, (x)) - -#define YT921X_ACL_KEYb_IPV4_ADDR_RANGE_EN BIT(30) -#define YT921X_ACL_BINb_IPV4_FRAG BIT(3) -#define YT921X_ACL_BINb_IPV4_L4_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_IPV4_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV4_L4_TYPE_M, (x)) -#define YT921X_ACL_BINa_IPV4_ADDR_M GENMASK(31, 0) - -#define YT921X_ACL_BINb_IPV6_L4_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_IPV6_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_IPV6_L4_TYPE_M, (x)) -#define YT921X_ACL_BINa_IPV6_ADDRx_M GENMASK(31, 0) - -#define YT921X_ACL_BINb_IPV6_xA1_IP_OPTION BIT(3) - -#define YT921X_ACL_BINb_IPV6_xA2_FIRST_FRAG BIT(3) - -#define YT921X_ACL_KEYb_IPV6_xA3_ADDR_RANGE_EN BIT(30) -#define YT921X_ACL_BINb_IPV6_xA3_FRAG BIT(3) - -#define YT921X_ACL_BINb_MISC_FRAG BIT(3) -#define YT921X_ACL_BINb_MISC_L4_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_MISC_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_MISC_L4_TYPE_M, (x)) -#define YT921X_ACL_BINa_MISC_PPPOE_FLAG BIT(30) -#define YT921X_ACL_BINa_MISC_FIRST_FRAG BIT(29) -#define YT921X_ACL_BINa_MISC_IP_OPTION BIT(28) -#define YT921X_ACL_BINa_MISC_TCP_FLAGS_M GENMASK(27, 20) -#define YT921X_ACL_BINa_MISC_TCP_FLAGS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TCP_FLAGS_M, (x)) -#define YT921X_ACL_BINa_MISC_IP_PROTO_M GENMASK(19, 12) -#define YT921X_ACL_BINa_MISC_IP_PROTO(x) FIELD_PREP(YT921X_ACL_BINa_MISC_IP_PROTO_M, (x)) -#define YT921X_ACL_BINa_MISC_TOS_M GENMASK(11, 4) -#define YT921X_ACL_BINa_MISC_TOS(x) FIELD_PREP(YT921X_ACL_BINa_MISC_TOS_M, (x)) -#define YT921X_ACL_BINa_MISC_L3_TYPE_M GENMASK(3, 0) -#define YT921X_ACL_BINa_MISC_L3_TYPE(x) FIELD_PREP(YT921X_ACL_BINa_MISC_L3_TYPE_M, (x)) - -#define YT921X_ACL_KEYb_L4_DPORT_RANGE_EN BIT(31) -#define YT921X_ACL_KEYb_L4_SPORT_RANGE_EN BIT(30) -#define YT921X_ACL_BINb_L4_FRAG BIT(3) -#define YT921X_ACL_BINb_L4_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_L4_TYPE_M, (x)) -#define YT921X_ACL_BINa_L4_DPORT_M GENMASK(31, 16) -#define YT921X_ACL_BINa_L4_SPORT_M GENMASK(15, 0) - -#define YT921X_ACL_BINb_UDF_IS_IGMP BIT(0) -#define YT921X_ACL_BINa_UDF_UDF0_M GENMASK(31, 16) -#define YT921X_ACL_BINa_UDF_UDF0(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF0_M, (x)) -#define YT921X_ACL_BINa_UDF_UDF1_M GENMASK(15, 0) -#define YT921X_ACL_BINa_UDF_UDF1(x) FIELD_PREP(YT921X_ACL_BINa_UDF_UDF1_M, (x)) - -#define YT921X_ACL_KEYb_ETHERTYPE_ETHERTYPE_RANGE_EN BIT(30) -#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M GENMASK(2, 0) -#define YT921X_ACL_BINb_ETHERTYPE_L4_TYPE(x) FIELD_PREP(YT921X_ACL_BINb_ETHERTYPE_L4_TYPE_M, (x)) -#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M GENMASK(15, 0) -#define YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE(x) FIELD_PREP(YT921X_ACL_BINa_ETHERTYPE_ETHERTYPE_M, (x)) - -enum yt921x_l2_type { - YT921X_L2_TYPE_ETH, - YT921X_L2_TYPE_ETHV2, - YT921X_L2_TYPE_ETHSAP, - YT921X_L2_TYPE_ETHSNAP, -}; - -enum yt921x_l3_type { - YT921X_L3_TYPE_OTHER, - YT921X_L3_TYPE_IPV4, - YT921X_L3_TYPE_IPV6, - YT921X_L3_TYPE_ARP, - YT921X_L3_TYPE_LLDP, - YT921X_L3_TYPE_PAE, - YT921X_L3_TYPE_ERP, - YT921X_L3_TYPE_SLOW_PROTOCOL, -}; - -enum yt921x_l4_type { - YT921X_L4_TYPE_OTHER, - YT921X_L4_TYPE_TCP, - YT921X_L4_TYPE_UDP, - YT921X_L4_TYPE_UDPLITE, - YT921X_L4_TYPE_ICMP, - YT921X_L4_TYPE_IGMP, - YT921X_L4_TYPE_MLD, - YT921X_L4_TYPE_ND, -}; - -#define YT921X_TPID_IGRn(x) (0x210000 + 4 * (x)) /* [0, 3] */ -#define YT921X_TPID_IGR_TPID_M GENMASK(15, 0) -#define YT921X_PORTn_IGR_TPID(port) (0x210010 + 4 * (port)) -#define YT921X_PORT_IGR_TPIDn_STAG_M GENMASK(7, 4) -#define YT921X_PORT_IGR_TPIDn_STAG(x) BIT((x) + 4) -#define YT921X_PORT_IGR_TPIDn_CTAG_M GENMASK(3, 0) -#define YT921X_PORT_IGR_TPIDn_CTAG(x) BIT(x) -#define YT921X_LAG_HASH 0x210090 -#define YT921X_LAG_HASH_L4_SPORT BIT(7) -#define YT921X_LAG_HASH_L4_DPORT BIT(6) -#define YT921X_LAG_HASH_IP_PROTO BIT(5) -#define YT921X_LAG_HASH_IP_SRC BIT(4) -#define YT921X_LAG_HASH_IP_DST BIT(3) -#define YT921X_LAG_HASH_MAC_SA BIT(2) -#define YT921X_LAG_HASH_MAC_DA BIT(1) -#define YT921X_LAG_HASH_SRC_PORT BIT(0) -#define YT921X_UDFn_CTRL(x) (0x210094 + 4 * (x)) -#define YT921X_UDF_CTRL_UDF_TYPE_M GENMASK(8, 7) -#define YT921X_UDF_CTRL_UDF_TYPE(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_TYPE_M, (x)) -#define YT921X_UDF_CTRL_UDF_TYPE_ETH YT921X_UDF_CTRL_UDF_TYPE(0) -#define YT921X_UDF_CTRL_UDF_TYPE_L3 YT921X_UDF_CTRL_UDF_TYPE(1) -#define YT921X_UDF_CTRL_UDF_TYPE_L4 YT921X_UDF_CTRL_UDF_TYPE(2) -#define YT921X_UDF_CTRL_UDF_OFFSET_M GENMASK(6, 0) -#define YT921X_UDF_CTRL_UDF_OFFSET(x) FIELD_PREP(YT921X_UDF_CTRL_UDF_OFFSET_M, (x)) - -#define YT921X_PORTn_RATE(port) (0x220000 + 4 * (port)) -#define YT921X_PORT_RATE_GAP_VALUE GENMASK(4, 0) /* default 20 */ -#define YT921X_METER_SLOT 0x220104 -#define YT921X_METER_SLOT_SLOT_M GENMASK(11, 0) -#define YT921X_PORTn_METER(port) (0x220108 + 4 * (port)) -#define YT921X_PORT_METER_EN BIT(4) -#define YT921X_PORT_METER_ID_M GENMASK(3, 0) -#define YT921X_PORT_METER_ID(x) FIELD_PREP(YT921X_PORT_METER_ID_M, (x)) -#define YT921X_METERn_CTRL(x) (0x220800 + 0x10 * (x)) -#define YT921X_METER_CTRLc_METER_EN BIT(14) -#define YT921X_METER_CTRLc_TOKEN_OVERFLOW_EN BIT(13) /* RFC4115: yellow use unused green bw */ -#define YT921X_METER_CTRLc_DROP_M GENMASK(12, 11) -#define YT921X_METER_CTRLc_DROP(x) FIELD_PREP(YT921X_METER_CTRLc_DROP_M, (x)) -#define YT921X_METER_CTRLc_DROP_GYR YT921X_METER_CTRLc_DROP(0) -#define YT921X_METER_CTRLc_DROP_YR YT921X_METER_CTRLc_DROP(1) -#define YT921X_METER_CTRLc_DROP_R YT921X_METER_CTRLc_DROP(2) -#define YT921X_METER_CTRLc_DROP_NONE YT921X_METER_CTRLc_DROP(3) -#define YT921X_METER_CTRLc_COLOR_BLIND BIT(10) -#define YT921X_METER_CTRLc_UNIT_M GENMASK(9, 7) -#define YT921X_METER_CTRLc_UNIT(x) FIELD_PREP(YT921X_METER_CTRLc_UNIT_M, (x)) -#define YT921X_METER_CTRLc_BYTE_MODE_INCLUDE_GAP BIT(6) /* +GAP_VALUE bytes each packet */ -#define YT921X_METER_CTRLc_PKT_MODE BIT(5) /* 0: byte rate mode */ -#define YT921X_METER_CTRLc_RFC2698 BIT(4) /* 0: RFC4115 */ -#define YT921X_METER_CTRLbc_CBS_M GENMASK_ULL(35, 20) -#define YT921X_METER_CTRLbc_CBS(x) FIELD_PREP(YT921X_METER_CTRLbc_CBS_M, (x)) -#define YT921X_METER_CTRLb_CIR_M GENMASK(19, 2) -#define YT921X_METER_CTRLb_CIR(x) FIELD_PREP(YT921X_METER_CTRLb_CIR_M, (x)) -#define YT921X_METER_CTRLab_EBS_M GENMASK_ULL(33, 18) -#define YT921X_METER_CTRLab_EBS(x) FIELD_PREP(YT921X_METER_CTRLab_EBS_M, (x)) -#define YT921X_METER_CTRLa_EIR_M GENMASK(17, 0) -#define YT921X_METER_CTRLa_EIR(x) FIELD_PREP(YT921X_METER_CTRLa_EIR_M, (x)) -#define YT921X_METERn_STAT(x) (0x221000 + 8 * (x)) - -#define YT921X_PORTn_VLAN_CTRL(port) (0x230010 + 4 * (port)) -#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_EN BIT(31) -#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_EN BIT(30) -#define YT921X_PORT_VLAN_CTRL_SVID_M GENMASK(29, 18) -#define YT921X_PORT_VLAN_CTRL_SVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_SVID_M, (x)) -#define YT921X_PORT_VLAN_CTRL_CVID_M GENMASK(17, 6) -#define YT921X_PORT_VLAN_CTRL_CVID(x) FIELD_PREP(YT921X_PORT_VLAN_CTRL_CVID_M, (x)) -#define YT921X_PORT_VLAN_CTRL_SVLAN_PRIO_M GENMASK(5, 3) -#define YT921X_PORT_VLAN_CTRL_CVLAN_PRIO_M GENMASK(2, 0) -#define YT921X_PORTn_VLAN_CTRL1(port) (0x230080 + 4 * (port)) -#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_EN BIT(8) -#define YT921X_PORT_VLAN_CTRL1_VLAN_RANGE_PROFILE_ID_M GENMASK(7, 4) -#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_TAGGED BIT(3) -#define YT921X_PORT_VLAN_CTRL1_SVLAN_DROP_UNTAGGED BIT(2) -#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED BIT(1) -#define YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED BIT(0) - -#define YT921X_MIRROR 0x300300 -#define YT921X_MIRROR_IGR_PORTS_M GENMASK(26, 16) -#define YT921X_MIRROR_IGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_IGR_PORTS_M, (x)) -#define YT921X_MIRROR_IGR_PORTn(port) BIT((port) + 16) -#define YT921X_MIRROR_EGR_PORTS_M GENMASK(14, 4) -#define YT921X_MIRROR_EGR_PORTS(x) FIELD_PREP(YT921X_MIRROR_EGR_PORTS_M, (x)) -#define YT921X_MIRROR_EGR_PORTn(port) BIT((port) + 4) -#define YT921X_MIRROR_PORT_M GENMASK(3, 0) -#define YT921X_MIRROR_PORT(x) FIELD_PREP(YT921X_MIRROR_PORT_M, (x)) - -#define YT921X_PORT_SHAPE_SLOT 0x34000c -#define YT921X_PORT_SHAPE_SLOT_SLOT_M GENMASK(11, 0) -#define YT921X_PORTn_SHAPE_CTRL(port) (0x354000 + 8 * (port)) -#define YT921X_PORT_SHAPE_CTRLb_EN BIT(4) -#define YT921X_PORT_SHAPE_CTRLb_PKT_MODE BIT(3) /* 0: byte rate mode */ -#define YT921X_PORT_SHAPE_CTRLb_UNIT_M GENMASK(2, 0) -#define YT921X_PORT_SHAPE_CTRLb_UNIT(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLb_UNIT_M, (x)) -#define YT921X_PORT_SHAPE_CTRLa_CBS_M GENMASK(31, 18) -#define YT921X_PORT_SHAPE_CTRLa_CBS(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CBS_M, (x)) -#define YT921X_PORT_SHAPE_CTRLa_CIR_M GENMASK(17, 0) -#define YT921X_PORT_SHAPE_CTRLa_CIR(x) FIELD_PREP(YT921X_PORT_SHAPE_CTRLa_CIR_M, (x)) -#define YT921X_PORTn_SHAPE_STAT(port) (0x356000 + 4 * (port)) - -#define YT921X_EDATA_EXTMODE 0xfb -#define YT921X_EDATA_LEN 0x100 - -#define YT921X_FDB_NUM 4096 - -enum yt921x_fdb_entry_status { - YT921X_FDB_ENTRY_STATUS_INVALID = 0, - YT921X_FDB_ENTRY_STATUS_MIN_TIME = 1, - YT921X_FDB_ENTRY_STATUS_MOVE_AGING_MAX_TIME = 3, - YT921X_FDB_ENTRY_STATUS_MAX_TIME = 5, - YT921X_FDB_ENTRY_STATUS_PENDING = 6, - YT921X_FDB_ENTRY_STATUS_STATIC = 7, -}; - -#define YT921X_MSTI_NUM 16 - -#define YT921X_TOKEN_BYTE_C 1 /* 1 token = 2^1 byte */ -#define YT921X_TOKEN_PKT_C -6 /* 1 token = 2^-6 packets */ -#define YT921X_TOKEN_RATE_C -15 -/* Custom meters only, not including dedicated port meters (11) */ -#define YT921X_METER_NUM 64 -#define YT921X_METER_SLOT_MIN 80 -#define YT921X_METER_UNIT_MAX ((1 << 3) - 1) -#define YT921X_METER_CIR_MAX ((1 << 18) - 1) -#define YT921X_METER_CBS_MAX ((1 << 16) - 1) -#define YT921X_PORT_SHAPE_SLOT_MIN 80 -#define YT921X_SHAPE_UNIT_MAX ((1 << 3) - 1) -#define YT921X_SHAPE_CIR_MAX ((1 << 18) - 1) -#define YT921X_SHAPE_CBS_MAX ((1 << 14) - 1) - -#define YT921X_LAG_NUM 2 -#define YT921X_LAG_PORT_NUM 4 - -#define YT921X_PRIO_NUM 8 - -#define YT9215_MAJOR 0x9002 -#define YT9218_MAJOR 0x9001 - -/* required for a hard reset */ -#define YT921X_RST_DELAY_US 10000 - -#define YT921X_FRAME_SIZE_MAX 0x2400 /* 9216 */ - -#define YT921X_TAG_LEN 8 - -#define YT921X_ACL_BLK_NUM 48 -#define YT921X_ACL_ENT_PER_BLK 8 -#define YT921X_ACL_NUM (YT921X_ACL_BLK_NUM * YT921X_ACL_ENT_PER_BLK) -#define YT921X_UDF_NUM 8 - -/* 8 internal + 2 external + 1 mcu */ -#define YT921X_PORT_NUM 11 - -#define yt921x_port_is_internal(port) ((port) < 8) -#define yt921x_port_is_external(port) ((port) == 8 || (port) == 9) - -struct yt921x_mib { - u64 rx_broadcast; - u64 rx_pause; - u64 rx_multicast; - u64 rx_crc_errors; - - u64 rx_alignment_errors; - u64 rx_undersize_errors; - u64 rx_fragment_errors; - u64 rx_64byte; - - u64 rx_65_127byte; - u64 rx_128_255byte; - u64 rx_256_511byte; - u64 rx_512_1023byte; - - u64 rx_1024_1518byte; - u64 rx_jumbo; - u64 rx_good_bytes; - - u64 rx_bad_bytes; - u64 rx_oversize_errors; - - u64 rx_dropped; - u64 tx_broadcast; - u64 tx_pause; - u64 tx_multicast; - - u64 tx_undersize_errors; - u64 tx_64byte; - u64 tx_65_127byte; - u64 tx_128_255byte; - - u64 tx_256_511byte; - u64 tx_512_1023byte; - u64 tx_1024_1518byte; - u64 tx_jumbo; - - u64 tx_good_bytes; - u64 tx_collisions; - - u64 tx_aborted_errors; - u64 tx_multiple_collisions; - u64 tx_single_collisions; - u64 tx_good; - - u64 tx_deferred; - u64 tx_late_collisions; - u64 rx_oam; - u64 tx_oam; -}; - -struct yt921x_acl_entry { - u32 key[2]; - u32 mask[2]; -}; - -struct yt921x_acl_rule { - unsigned long tag; - enum tc_setup_type type; - - u32 action[3]; - bool sw_assisted; - - u8 mask; - struct yt921x_acl_entry entries[YT921X_ACL_ENT_PER_BLK]; -}; - -struct yt921x_acl_blk { - struct yt921x_acl_rule *rules[YT921X_ACL_ENT_PER_BLK]; -}; - -struct yt921x_port { - unsigned char index; - - bool hairpin; - bool isolated; - - struct delayed_work mib_read; - struct yt921x_mib mib; - u64 rx_frames; - u64 tx_frames; -}; - -struct yt921x_reg_ops { - int (*read)(void *context, u32 reg, u32 *valp); - int (*write)(void *context, u32 reg, u32 val); -}; - -struct yt921x_priv { - struct dsa_switch ds; - - const struct yt921x_info *info; - unsigned int meter_slot_ns; - unsigned int port_shape_slot_ns; - /* cache of dsa_cpu_ports(ds) */ - u16 cpu_ports_mask; - unsigned char cycle_ns; - - /* protect the access to the switch registers */ - struct mutex reg_lock; - const struct yt921x_reg_ops *reg_ops; - void *reg_ctx; - - /* mdio master bus */ - struct mii_bus *mbus_int; - struct mii_bus *mbus_ext; - - struct yt921x_port ports[YT921X_PORT_NUM]; - - u16 eee_ports_mask; - - DECLARE_BITMAP(meters_map, YT921X_METER_NUM); - - u8 acl_masks[YT921X_ACL_BLK_NUM]; - struct yt921x_acl_blk *acl_blks[YT921X_ACL_BLK_NUM]; -}; - -#endif |
