diff options
Diffstat (limited to 'drivers/net/dsa/microchip')
-rw-r--r-- | drivers/net/dsa/microchip/Kconfig | 42 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/Makefile | 11 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8.h | 104 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8795.c | 1053 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8795_reg.h | 42 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8795_spi.c | 142 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz8863_smi.c | 31 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477.c | 872 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477.h | 61 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477_i2c.c | 45 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477_reg.h | 55 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz9477_spi.c | 122 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.c | 2840 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_common.h | 454 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/ksz_spi.c | 246 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x.h | 23 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x_main.c | 391 | ||||
-rw-r--r-- | drivers/net/dsa/microchip/lan937x_reg.h | 202 |
18 files changed, 4707 insertions, 2029 deletions
diff --git a/drivers/net/dsa/microchip/Kconfig b/drivers/net/dsa/microchip/Kconfig index c9e2a8989556..06b1efdb5e7d 100644 --- a/drivers/net/dsa/microchip/Kconfig +++ b/drivers/net/dsa/microchip/Kconfig @@ -1,49 +1,29 @@ # SPDX-License-Identifier: GPL-2.0-only -config NET_DSA_MICROCHIP_KSZ_COMMON - select NET_DSA_TAG_KSZ - tristate - -menuconfig NET_DSA_MICROCHIP_KSZ9477 - tristate "Microchip KSZ9477 series switch support" +menuconfig NET_DSA_MICROCHIP_KSZ_COMMON + tristate "Microchip KSZ8795/KSZ9477/LAN937x series switch support" depends on NET_DSA - select NET_DSA_MICROCHIP_KSZ_COMMON + select NET_DSA_TAG_KSZ help - This driver adds support for Microchip KSZ9477 switch chips. + This driver adds support for Microchip KSZ9477 series switch and + KSZ8795/KSZ88x3 switch chips. config NET_DSA_MICROCHIP_KSZ9477_I2C - tristate "KSZ9477 series I2C connected switch driver" - depends on NET_DSA_MICROCHIP_KSZ9477 && I2C + tristate "KSZ series I2C connected switch driver" + depends on NET_DSA_MICROCHIP_KSZ_COMMON && I2C select REGMAP_I2C help Select to enable support for registering switches configured through I2C. -config NET_DSA_MICROCHIP_KSZ9477_SPI - tristate "KSZ9477 series SPI connected switch driver" - depends on NET_DSA_MICROCHIP_KSZ9477 && SPI +config NET_DSA_MICROCHIP_KSZ_SPI + tristate "KSZ series SPI connected switch driver" + depends on NET_DSA_MICROCHIP_KSZ_COMMON && SPI select REGMAP_SPI help Select to enable support for registering switches configured through SPI. -menuconfig NET_DSA_MICROCHIP_KSZ8795 - tristate "Microchip KSZ8795 series switch support" - depends on NET_DSA - select NET_DSA_MICROCHIP_KSZ_COMMON - help - This driver adds support for Microchip KSZ8795/KSZ88X3 switch chips. - -config NET_DSA_MICROCHIP_KSZ8795_SPI - tristate "KSZ8795 series SPI connected switch driver" - depends on NET_DSA_MICROCHIP_KSZ8795 && SPI - select REGMAP_SPI - help - This driver accesses KSZ8795 chip through SPI. - - It is required to use the KSZ8795 switch driver as the only access - is through SPI. - config NET_DSA_MICROCHIP_KSZ8863_SMI tristate "KSZ series SMI connected switch driver" - depends on NET_DSA_MICROCHIP_KSZ8795 + depends on NET_DSA_MICROCHIP_KSZ_COMMON select MDIO_BITBANG help Select to enable support for registering switches configured through diff --git a/drivers/net/dsa/microchip/Makefile b/drivers/net/dsa/microchip/Makefile index 2a03b21a3386..28873559efc2 100644 --- a/drivers/net/dsa/microchip/Makefile +++ b/drivers/net/dsa/microchip/Makefile @@ -1,8 +1,9 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o -obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o +obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_switch.o +ksz_switch-objs := ksz_common.o +ksz_switch-objs += ksz9477.o +ksz_switch-objs += ksz8795.o +ksz_switch-objs += lan937x_main.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o -obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o -obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795) += ksz8795.o -obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8795_SPI) += ksz8795_spi.o +obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_SPI) += ksz_spi.o obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ8863_SMI) += ksz8863_smi.o diff --git a/drivers/net/dsa/microchip/ksz8.h b/drivers/net/dsa/microchip/ksz8.h index 9d611895d3cf..8582b4b67d98 100644 --- a/drivers/net/dsa/microchip/ksz8.h +++ b/drivers/net/dsa/microchip/ksz8.h @@ -7,63 +7,55 @@ #ifndef __KSZ8XXX_H #define __KSZ8XXX_H -#include <linux/kernel.h> -enum ksz_regs { - REG_IND_CTRL_0, - REG_IND_DATA_8, - REG_IND_DATA_CHECK, - REG_IND_DATA_HI, - REG_IND_DATA_LO, - REG_IND_MIB_CHECK, - P_FORCE_CTRL, - P_LINK_STATUS, - P_LOCAL_CTRL, - P_NEG_RESTART_CTRL, - P_REMOTE_STATUS, - P_SPEED_STATUS, - S_TAIL_TAG_CTRL, -}; +#include <linux/types.h> +#include <net/dsa.h> +#include "ksz_common.h" -enum ksz_masks { - PORT_802_1P_REMAPPING, - SW_TAIL_TAG_ENABLE, - MIB_COUNTER_OVERFLOW, - MIB_COUNTER_VALID, - VLAN_TABLE_FID, - VLAN_TABLE_MEMBERSHIP, - VLAN_TABLE_VALID, - STATIC_MAC_TABLE_VALID, - STATIC_MAC_TABLE_USE_FID, - STATIC_MAC_TABLE_FID, - STATIC_MAC_TABLE_OVERRIDE, - STATIC_MAC_TABLE_FWD_PORTS, - DYNAMIC_MAC_TABLE_ENTRIES_H, - DYNAMIC_MAC_TABLE_MAC_EMPTY, - DYNAMIC_MAC_TABLE_NOT_READY, - DYNAMIC_MAC_TABLE_ENTRIES, - DYNAMIC_MAC_TABLE_FID, - DYNAMIC_MAC_TABLE_SRC_PORT, - DYNAMIC_MAC_TABLE_TIMESTAMP, -}; - -enum ksz_shifts { - VLAN_TABLE_MEMBERSHIP_S, - VLAN_TABLE, - STATIC_MAC_FWD_PORTS, - STATIC_MAC_FID, - DYNAMIC_MAC_ENTRIES_H, - DYNAMIC_MAC_ENTRIES, - DYNAMIC_MAC_FID, - DYNAMIC_MAC_TIMESTAMP, - DYNAMIC_MAC_SRC_PORT, -}; - -struct ksz8 { - const u8 *regs; - const u32 *masks; - const u8 *shifts; - void *priv; -}; +int ksz8_setup(struct dsa_switch *ds); +u32 ksz8_get_port_addr(int port, int offset); +void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member); +void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port); +void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port); +int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); +int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val); +int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, + u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries); +int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu); +void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu); +void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt); +void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt); +void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze); +void ksz8_port_init_cnt(struct ksz_device *dev, int port); +int ksz8_fdb_dump(struct ksz_device *dev, int port, + dsa_fdb_dump_cb_t *cb, void *data); +int ksz8_mdb_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); +int ksz8_mdb_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); +int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, + struct netlink_ext_ack *extack); +int ksz8_port_vlan_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int ksz8_port_vlan_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan); +int ksz8_port_mirror_add(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack); +void ksz8_port_mirror_del(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror); +int ksz8_get_stp_reg(void); +void ksz8_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config); +void ksz8_config_cpu_port(struct dsa_switch *ds); +int ksz8_enable_stp_addr(struct ksz_device *dev); +int ksz8_reset_switch(struct ksz_device *dev); +int ksz8_switch_detect(struct ksz_device *dev); +int ksz8_switch_init(struct ksz_device *dev); +void ksz8_switch_exit(struct ksz_device *dev); #endif diff --git a/drivers/net/dsa/microchip/ksz8795.c b/drivers/net/dsa/microchip/ksz8795.c index 991b9c6b6ce7..bd3b133e7085 100644 --- a/drivers/net/dsa/microchip/ksz8795.c +++ b/drivers/net/dsa/microchip/ksz8795.c @@ -26,190 +26,6 @@ #include "ksz8795_reg.h" #include "ksz8.h" -static const u8 ksz8795_regs[] = { - [REG_IND_CTRL_0] = 0x6E, - [REG_IND_DATA_8] = 0x70, - [REG_IND_DATA_CHECK] = 0x72, - [REG_IND_DATA_HI] = 0x71, - [REG_IND_DATA_LO] = 0x75, - [REG_IND_MIB_CHECK] = 0x74, - [P_FORCE_CTRL] = 0x0C, - [P_LINK_STATUS] = 0x0E, - [P_LOCAL_CTRL] = 0x07, - [P_NEG_RESTART_CTRL] = 0x0D, - [P_REMOTE_STATUS] = 0x08, - [P_SPEED_STATUS] = 0x09, - [S_TAIL_TAG_CTRL] = 0x0C, -}; - -static const u32 ksz8795_masks[] = { - [PORT_802_1P_REMAPPING] = BIT(7), - [SW_TAIL_TAG_ENABLE] = BIT(1), - [MIB_COUNTER_OVERFLOW] = BIT(6), - [MIB_COUNTER_VALID] = BIT(5), - [VLAN_TABLE_FID] = GENMASK(6, 0), - [VLAN_TABLE_MEMBERSHIP] = GENMASK(11, 7), - [VLAN_TABLE_VALID] = BIT(12), - [STATIC_MAC_TABLE_VALID] = BIT(21), - [STATIC_MAC_TABLE_USE_FID] = BIT(23), - [STATIC_MAC_TABLE_FID] = GENMASK(30, 24), - [STATIC_MAC_TABLE_OVERRIDE] = BIT(26), - [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(24, 20), - [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(6, 0), - [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(8), - [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), - [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 29), - [DYNAMIC_MAC_TABLE_FID] = GENMASK(26, 20), - [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(26, 24), - [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(28, 27), -}; - -static const u8 ksz8795_shifts[] = { - [VLAN_TABLE_MEMBERSHIP_S] = 7, - [VLAN_TABLE] = 16, - [STATIC_MAC_FWD_PORTS] = 16, - [STATIC_MAC_FID] = 24, - [DYNAMIC_MAC_ENTRIES_H] = 3, - [DYNAMIC_MAC_ENTRIES] = 29, - [DYNAMIC_MAC_FID] = 16, - [DYNAMIC_MAC_TIMESTAMP] = 27, - [DYNAMIC_MAC_SRC_PORT] = 24, -}; - -static const u8 ksz8863_regs[] = { - [REG_IND_CTRL_0] = 0x79, - [REG_IND_DATA_8] = 0x7B, - [REG_IND_DATA_CHECK] = 0x7B, - [REG_IND_DATA_HI] = 0x7C, - [REG_IND_DATA_LO] = 0x80, - [REG_IND_MIB_CHECK] = 0x80, - [P_FORCE_CTRL] = 0x0C, - [P_LINK_STATUS] = 0x0E, - [P_LOCAL_CTRL] = 0x0C, - [P_NEG_RESTART_CTRL] = 0x0D, - [P_REMOTE_STATUS] = 0x0E, - [P_SPEED_STATUS] = 0x0F, - [S_TAIL_TAG_CTRL] = 0x03, -}; - -static const u32 ksz8863_masks[] = { - [PORT_802_1P_REMAPPING] = BIT(3), - [SW_TAIL_TAG_ENABLE] = BIT(6), - [MIB_COUNTER_OVERFLOW] = BIT(7), - [MIB_COUNTER_VALID] = BIT(6), - [VLAN_TABLE_FID] = GENMASK(15, 12), - [VLAN_TABLE_MEMBERSHIP] = GENMASK(18, 16), - [VLAN_TABLE_VALID] = BIT(19), - [STATIC_MAC_TABLE_VALID] = BIT(19), - [STATIC_MAC_TABLE_USE_FID] = BIT(21), - [STATIC_MAC_TABLE_FID] = GENMASK(29, 26), - [STATIC_MAC_TABLE_OVERRIDE] = BIT(20), - [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16), - [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(5, 0), - [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(7), - [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), - [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 28), - [DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16), - [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20), - [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22), -}; - -static u8 ksz8863_shifts[] = { - [VLAN_TABLE_MEMBERSHIP_S] = 16, - [STATIC_MAC_FWD_PORTS] = 16, - [STATIC_MAC_FID] = 22, - [DYNAMIC_MAC_ENTRIES_H] = 3, - [DYNAMIC_MAC_ENTRIES] = 24, - [DYNAMIC_MAC_FID] = 16, - [DYNAMIC_MAC_TIMESTAMP] = 24, - [DYNAMIC_MAC_SRC_PORT] = 20, -}; - -struct mib_names { - char string[ETH_GSTRING_LEN]; -}; - -static const struct mib_names ksz87xx_mib_names[] = { - { "rx_hi" }, - { "rx_undersize" }, - { "rx_fragments" }, - { "rx_oversize" }, - { "rx_jabbers" }, - { "rx_symbol_err" }, - { "rx_crc_err" }, - { "rx_align_err" }, - { "rx_mac_ctrl" }, - { "rx_pause" }, - { "rx_bcast" }, - { "rx_mcast" }, - { "rx_ucast" }, - { "rx_64_or_less" }, - { "rx_65_127" }, - { "rx_128_255" }, - { "rx_256_511" }, - { "rx_512_1023" }, - { "rx_1024_1522" }, - { "rx_1523_2000" }, - { "rx_2001" }, - { "tx_hi" }, - { "tx_late_col" }, - { "tx_pause" }, - { "tx_bcast" }, - { "tx_mcast" }, - { "tx_ucast" }, - { "tx_deferred" }, - { "tx_total_col" }, - { "tx_exc_col" }, - { "tx_single_col" }, - { "tx_mult_col" }, - { "rx_total" }, - { "tx_total" }, - { "rx_discards" }, - { "tx_discards" }, -}; - -static const struct mib_names ksz88xx_mib_names[] = { - { "rx" }, - { "rx_hi" }, - { "rx_undersize" }, - { "rx_fragments" }, - { "rx_oversize" }, - { "rx_jabbers" }, - { "rx_symbol_err" }, - { "rx_crc_err" }, - { "rx_align_err" }, - { "rx_mac_ctrl" }, - { "rx_pause" }, - { "rx_bcast" }, - { "rx_mcast" }, - { "rx_ucast" }, - { "rx_64_or_less" }, - { "rx_65_127" }, - { "rx_128_255" }, - { "rx_256_511" }, - { "rx_512_1023" }, - { "rx_1024_1522" }, - { "tx" }, - { "tx_hi" }, - { "tx_late_col" }, - { "tx_pause" }, - { "tx_bcast" }, - { "tx_mcast" }, - { "tx_ucast" }, - { "tx_deferred" }, - { "tx_total_col" }, - { "tx_exc_col" }, - { "tx_single_col" }, - { "tx_mult_col" }, - { "rx_discards" }, - { "tx_discards" }, -}; - -static bool ksz_is_ksz88x3(struct ksz_device *dev) -{ - return dev->chip_id == 0x8830; -} - static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); @@ -222,7 +38,27 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits, bits, set ? bits : 0); } -static int ksz8_reset_switch(struct ksz_device *dev) +static int ksz8_ind_write8(struct ksz_device *dev, u8 table, u16 addr, u8 data) +{ + const u16 *regs; + u16 ctrl_addr; + int ret = 0; + + regs = dev->info->regs; + + mutex_lock(&dev->alu_mutex); + + ctrl_addr = IND_ACC_TABLE(table) | addr; + ret = ksz_write8(dev, regs[REG_IND_BYTE], data); + if (!ret) + ret = ksz_write16(dev, regs[REG_IND_CTRL_0], ctrl_addr); + + mutex_unlock(&dev->alu_mutex); + + return ret; +} + +int ksz8_reset_switch(struct ksz_device *dev) { if (ksz_is_ksz88x3(dev)) { /* reset switch */ @@ -273,20 +109,19 @@ static void ksz8795_set_prio_queue(struct ksz_device *dev, int port, int queue) true); } -static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) +void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) { - struct ksz8 *ksz8 = dev->priv; const u32 *masks; - const u8 *regs; + const u16 *regs; u16 ctrl_addr; u32 data; u8 check; int loop; - masks = ksz8->masks; - regs = ksz8->regs; + masks = dev->info->masks; + regs = dev->info->regs; - ctrl_addr = addr + dev->reg_mib_cnt * port; + ctrl_addr = addr + dev->info->reg_mib_cnt * port; ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); mutex_lock(&dev->alu_mutex); @@ -312,18 +147,17 @@ static void ksz8_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { - struct ksz8 *ksz8 = dev->priv; const u32 *masks; - const u8 *regs; + const u16 *regs; u16 ctrl_addr; u32 data; u8 check; int loop; - masks = ksz8->masks; - regs = ksz8->regs; + masks = dev->info->masks; + regs = dev->info->regs; - addr -= dev->reg_mib_cnt; + addr -= dev->info->reg_mib_cnt; ctrl_addr = (KSZ8795_MIB_TOTAL_RX_1 - KSZ8795_MIB_TOTAL_RX_0) * port; ctrl_addr += addr + KSZ8795_MIB_TOTAL_RX_0; ctrl_addr |= IND_ACC_TABLE(TABLE_MIB | TABLE_READ); @@ -365,14 +199,15 @@ static void ksz8795_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt) { - struct ksz8 *ksz8 = dev->priv; - const u8 *regs = ksz8->regs; u32 *last = (u32 *)dropped; + const u16 *regs; u16 ctrl_addr; u32 data; u32 cur; - addr -= dev->reg_mib_cnt; + regs = dev->info->regs; + + addr -= dev->info->reg_mib_cnt; ctrl_addr = addr ? KSZ8863_MIB_PACKET_DROPPED_TX_0 : KSZ8863_MIB_PACKET_DROPPED_RX_0; ctrl_addr += port; @@ -394,8 +229,8 @@ static void ksz8863_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, } } -static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, - u64 *dropped, u64 *cnt) +void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) { if (ksz_is_ksz88x3(dev)) ksz8863_r_mib_pkt(dev, port, addr, dropped, cnt); @@ -403,7 +238,7 @@ static void ksz8_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, ksz8795_r_mib_pkt(dev, port, addr, dropped, cnt); } -static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) +void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) { if (ksz_is_ksz88x3(dev)) return; @@ -418,7 +253,7 @@ static void ksz8_freeze_mib(struct ksz_device *dev, int port, bool freeze) ksz_cfg(dev, REG_SW_CTRL_6, BIT(port), false); } -static void ksz8_port_init_cnt(struct ksz_device *dev, int port) +void ksz8_port_init_cnt(struct ksz_device *dev, int port) { struct ksz_port_mib *mib = &dev->ports[port].mib; u64 *dropped; @@ -433,31 +268,30 @@ static void ksz8_port_init_cnt(struct ksz_device *dev, int port) mib->cnt_ptr = 0; /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->reg_mib_cnt) { + while (mib->cnt_ptr < dev->info->reg_mib_cnt) { dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } /* last one in storage */ - dropped = &mib->counters[dev->mib_cnt]; + dropped = &mib->counters[dev->info->mib_cnt]; /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->mib_cnt) { + while (mib->cnt_ptr < dev->info->mib_cnt) { dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, dropped, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } - mib->cnt_ptr = 0; - memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); } static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) { - struct ksz8 *ksz8 = dev->priv; - const u8 *regs = ksz8->regs; + const u16 *regs; u16 ctrl_addr; + regs = dev->info->regs; + ctrl_addr = IND_ACC_TABLE(table | TABLE_READ) | addr; mutex_lock(&dev->alu_mutex); @@ -468,10 +302,11 @@ static void ksz8_r_table(struct ksz_device *dev, int table, u16 addr, u64 *data) static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) { - struct ksz8 *ksz8 = dev->priv; - const u8 *regs = ksz8->regs; + const u16 *regs; u16 ctrl_addr; + regs = dev->info->regs; + ctrl_addr = IND_ACC_TABLE(table) | addr; mutex_lock(&dev->alu_mutex); @@ -482,13 +317,12 @@ static void ksz8_w_table(struct ksz_device *dev, int table, u16 addr, u64 data) static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) { - struct ksz8 *ksz8 = dev->priv; int timeout = 100; const u32 *masks; - const u8 *regs; + const u16 *regs; - masks = ksz8->masks; - regs = ksz8->regs; + masks = dev->info->masks; + regs = dev->info->regs; do { ksz_read8(dev, regs[REG_IND_DATA_CHECK], data); @@ -509,22 +343,20 @@ static int ksz8_valid_dyn_entry(struct ksz_device *dev, u8 *data) return 0; } -static int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, - u8 *mac_addr, u8 *fid, u8 *src_port, - u8 *timestamp, u16 *entries) +int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, u8 *mac_addr, + u8 *fid, u8 *src_port, u8 *timestamp, u16 *entries) { - struct ksz8 *ksz8 = dev->priv; u32 data_hi, data_lo; const u8 *shifts; const u32 *masks; - const u8 *regs; + const u16 *regs; u16 ctrl_addr; u8 data; int rc; - shifts = ksz8->shifts; - masks = ksz8->masks; - regs = ksz8->regs; + shifts = dev->info->shifts; + masks = dev->info->masks; + regs = dev->info->regs; ctrl_addr = IND_ACC_TABLE(TABLE_DYNAMIC_MAC | TABLE_READ) | addr; @@ -574,17 +406,16 @@ static int ksz8_r_dyn_mac_table(struct ksz_device *dev, u16 addr, return rc; } -static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu) +int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu) { - struct ksz8 *ksz8 = dev->priv; u32 data_hi, data_lo; const u8 *shifts; const u32 *masks; u64 data; - shifts = ksz8->shifts; - masks = ksz8->masks; + shifts = dev->info->shifts; + masks = dev->info->masks; ksz8_r_table(dev, TABLE_STATIC_MAC, addr, &data); data_hi = data >> 32; @@ -613,17 +444,16 @@ static int ksz8_r_sta_mac_table(struct ksz_device *dev, u16 addr, return -ENXIO; } -static void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, - struct alu_struct *alu) +void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, + struct alu_struct *alu) { - struct ksz8 *ksz8 = dev->priv; u32 data_hi, data_lo; const u8 *shifts; const u32 *masks; u64 data; - shifts = ksz8->shifts; - masks = ksz8->masks; + shifts = dev->info->shifts; + masks = dev->info->masks; data_lo = ((u32)alu->mac[2] << 24) | ((u32)alu->mac[3] << 16) | @@ -649,12 +479,11 @@ static void ksz8_w_sta_mac_table(struct ksz_device *dev, u16 addr, static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, u8 *member, u8 *valid) { - struct ksz8 *ksz8 = dev->priv; const u8 *shifts; const u32 *masks; - shifts = ksz8->shifts; - masks = ksz8->masks; + shifts = dev->info->shifts; + masks = dev->info->masks; *fid = vlan & masks[VLAN_TABLE_FID]; *member = (vlan & masks[VLAN_TABLE_MEMBERSHIP]) >> @@ -665,12 +494,11 @@ static void ksz8_from_vlan(struct ksz_device *dev, u32 vlan, u8 *fid, static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid, u16 *vlan) { - struct ksz8 *ksz8 = dev->priv; const u8 *shifts; const u32 *masks; - shifts = ksz8->shifts; - masks = ksz8->masks; + shifts = dev->info->shifts; + masks = dev->info->masks; *vlan = fid; *vlan |= (u16)member << shifts[VLAN_TABLE_MEMBERSHIP_S]; @@ -680,12 +508,11 @@ static void ksz8_to_vlan(struct ksz_device *dev, u8 fid, u8 member, u8 valid, static void ksz8_r_vlan_entries(struct ksz_device *dev, u16 addr) { - struct ksz8 *ksz8 = dev->priv; const u8 *shifts; u64 data; int i; - shifts = ksz8->shifts; + shifts = dev->info->shifts; ksz8_r_table(dev, TABLE_VLAN, addr, &data); addr *= 4; @@ -725,21 +552,32 @@ static void ksz8_w_vlan_table(struct ksz_device *dev, u16 vid, u16 vlan) ksz8_w_table(dev, TABLE_VLAN, addr, buf); } -static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) +int ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) { - struct ksz8 *ksz8 = dev->priv; u8 restart, speed, ctrl, link; - const u8 *regs = ksz8->regs; int processed = true; + const u16 *regs; u8 val1, val2; u16 data = 0; u8 p = phy; + int ret; + + regs = dev->info->regs; switch (reg) { case MII_BMCR: - ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); - ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); - ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); + ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); + if (ret) + return ret; + + ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); + if (ret) + return ret; + + ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); + if (ret) + return ret; + if (restart & PORT_PHY_LOOPBACK) data |= BMCR_LOOPBACK; if (ctrl & PORT_FORCE_100_MBIT) @@ -769,7 +607,10 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data |= KSZ886X_BMCR_DISABLE_LED; break; case MII_BMSR: - ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); + ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); + if (ret) + return ret; + data = BMSR_100FULL | BMSR_100HALF | BMSR_10FULL | @@ -790,7 +631,10 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data = KSZ8795_ID_LO; break; case MII_ADVERTISE: - ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); + ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); + if (ret) + return ret; + data = ADVERTISE_CSMA; if (ctrl & PORT_AUTO_NEG_SYM_PAUSE) data |= ADVERTISE_PAUSE_CAP; @@ -804,7 +648,10 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data |= ADVERTISE_10HALF; break; case MII_LPA: - ksz_pread8(dev, p, regs[P_REMOTE_STATUS], &link); + ret = ksz_pread8(dev, p, regs[P_REMOTE_STATUS], &link); + if (ret) + return ret; + data = LPA_SLCT; if (link & PORT_REMOTE_SYM_PAUSE) data |= LPA_PAUSE_CAP; @@ -820,8 +667,14 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) data |= LPA_LPACK; break; case PHY_REG_LINK_MD: - ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1); - ksz_pread8(dev, p, REG_PORT_LINK_MD_RESULT, &val2); + ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_CTRL, &val1); + if (ret) + return ret; + + ret = ksz_pread8(dev, p, REG_PORT_LINK_MD_RESULT, &val2); + if (ret) + return ret; + if (val1 & PORT_START_CABLE_DIAG) data |= PHY_START_CABLE_DIAG; @@ -836,7 +689,10 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) FIELD_GET(PORT_CABLE_FAULT_COUNTER_L, val2)); break; case PHY_REG_PHY_CTRL: - ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); + ret = ksz_pread8(dev, p, regs[P_LINK_STATUS], &link); + if (ret) + return ret; + if (link & PORT_MDIX_STATUS) data |= KSZ886X_CTRL_MDIX_STAT; break; @@ -846,14 +702,18 @@ static void ksz8_r_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 *val) } if (processed) *val = data; + + return 0; } -static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) +int ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) { - struct ksz8 *ksz8 = dev->priv; u8 restart, speed, ctrl, data; - const u8 *regs = ksz8->regs; + const u16 *regs; u8 p = phy; + int ret; + + regs = dev->info->regs; switch (reg) { case MII_BMCR: @@ -861,15 +721,26 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) /* Do not support PHY reset function. */ if (val & BMCR_RESET) break; - ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); + ret = ksz_pread8(dev, p, regs[P_SPEED_STATUS], &speed); + if (ret) + return ret; + data = speed; if (val & KSZ886X_BMCR_HP_MDIX) data |= PORT_HP_MDIX; else data &= ~PORT_HP_MDIX; - if (data != speed) - ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data); - ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); + + if (data != speed) { + ret = ksz_pwrite8(dev, p, regs[P_SPEED_STATUS], data); + if (ret) + return ret; + } + + ret = ksz_pread8(dev, p, regs[P_FORCE_CTRL], &ctrl); + if (ret) + return ret; + data = ctrl; if (ksz_is_ksz88x3(dev)) { if ((val & BMCR_ANENABLE)) @@ -895,9 +766,17 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) data |= PORT_FORCE_FULL_DUPLEX; else data &= ~PORT_FORCE_FULL_DUPLEX; - if (data != ctrl) - ksz_pwrite8(dev, p, regs[P_FORCE_CTRL], data); - ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); + + if (data != ctrl) { + ret = ksz_pwrite8(dev, p, regs[P_FORCE_CTRL], data); + if (ret) + return ret; + } + + ret = ksz_pread8(dev, p, regs[P_NEG_RESTART_CTRL], &restart); + if (ret) + return ret; + data = restart; if (val & KSZ886X_BMCR_DISABLE_LED) data |= PORT_LED_OFF; @@ -927,11 +806,19 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) data |= PORT_PHY_LOOPBACK; else data &= ~PORT_PHY_LOOPBACK; - if (data != restart) - ksz_pwrite8(dev, p, regs[P_NEG_RESTART_CTRL], data); + + if (data != restart) { + ret = ksz_pwrite8(dev, p, regs[P_NEG_RESTART_CTRL], + data); + if (ret) + return ret; + } break; case MII_ADVERTISE: - ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); + ret = ksz_pread8(dev, p, regs[P_LOCAL_CTRL], &ctrl); + if (ret) + return ret; + data = ctrl; data &= ~(PORT_AUTO_NEG_SYM_PAUSE | PORT_AUTO_NEG_100BTX_FD | @@ -948,8 +835,12 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) data |= PORT_AUTO_NEG_10BT_FD; if (val & ADVERTISE_10HALF) data |= PORT_AUTO_NEG_10BT; - if (data != ctrl) - ksz_pwrite8(dev, p, regs[P_LOCAL_CTRL], data); + + if (data != ctrl) { + ret = ksz_pwrite8(dev, p, regs[P_LOCAL_CTRL], data); + if (ret) + return ret; + } break; case PHY_REG_LINK_MD: if (val & PHY_START_CABLE_DIAG) @@ -958,44 +849,11 @@ static void ksz8_w_phy(struct ksz_device *dev, u16 phy, u16 reg, u16 val) default: break; } -} - -static enum dsa_tag_protocol ksz8_get_tag_protocol(struct dsa_switch *ds, - int port, - enum dsa_tag_protocol mp) -{ - struct ksz_device *dev = ds->priv; - - /* ksz88x3 uses the same tag schema as KSZ9893 */ - return ksz_is_ksz88x3(dev) ? - DSA_TAG_PROTO_KSZ9893 : DSA_TAG_PROTO_KSZ8795; -} - -static u32 ksz8_sw_get_phy_flags(struct dsa_switch *ds, int port) -{ - /* Silicon Errata Sheet (DS80000830A): - * Port 1 does not work with LinkMD Cable-Testing. - * Port 1 does not respond to received PAUSE control frames. - */ - if (!port) - return MICREL_KSZ8_P1_ERRATA; return 0; } -static void ksz8_get_strings(struct dsa_switch *ds, int port, - u32 stringset, uint8_t *buf) -{ - struct ksz_device *dev = ds->priv; - int i; - - for (i = 0; i < dev->mib_cnt; i++) { - memcpy(buf + i * ETH_GSTRING_LEN, - dev->mib_names[i].string, ETH_GSTRING_LEN); - } -} - -static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) +void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) { u8 data; @@ -1005,65 +863,30 @@ static void ksz8_cfg_port_member(struct ksz_device *dev, int port, u8 member) ksz_pwrite8(dev, port, P_MIRROR_CTRL, data); } -static void ksz8_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) -{ - struct ksz_device *dev = ds->priv; - struct ksz_port *p; - u8 data; - - ksz_pread8(dev, port, P_STP_CTRL, &data); - data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); - - switch (state) { - case BR_STATE_DISABLED: - data |= PORT_LEARN_DISABLE; - break; - case BR_STATE_LISTENING: - data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); - break; - case BR_STATE_LEARNING: - data |= PORT_RX_ENABLE; - break; - case BR_STATE_FORWARDING: - data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); - break; - case BR_STATE_BLOCKING: - data |= PORT_LEARN_DISABLE; - break; - default: - dev_err(ds->dev, "invalid STP state: %d\n", state); - return; - } - - ksz_pwrite8(dev, port, P_STP_CTRL, data); - - p = &dev->ports[port]; - p->stp_state = state; - - ksz_update_port_member(dev, port); -} - -static void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) +void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) { u8 learn[DSA_MAX_PORTS]; int first, index, cnt; struct ksz_port *p; + const u16 *regs; + + regs = dev->info->regs; - if ((uint)port < dev->port_cnt) { + if ((uint)port < dev->info->port_cnt) { first = port; cnt = port + 1; } else { /* Flush all ports. */ first = 0; - cnt = dev->port_cnt; + cnt = dev->info->port_cnt; } for (index = first; index < cnt; index++) { p = &dev->ports[index]; if (!p->on) continue; - ksz_pread8(dev, index, P_STP_CTRL, &learn[index]); + ksz_pread8(dev, index, regs[P_STP_CTRL], &learn[index]); if (!(learn[index] & PORT_LEARN_DISABLE)) - ksz_pwrite8(dev, index, P_STP_CTRL, + ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index] | PORT_LEARN_DISABLE); } ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true); @@ -1072,15 +895,113 @@ static void ksz8_flush_dyn_mac_table(struct ksz_device *dev, int port) if (!p->on) continue; if (!(learn[index] & PORT_LEARN_DISABLE)) - ksz_pwrite8(dev, index, P_STP_CTRL, learn[index]); + ksz_pwrite8(dev, index, regs[P_STP_CTRL], learn[index]); } } -static int ksz8_port_vlan_filtering(struct dsa_switch *ds, int port, bool flag, - struct netlink_ext_ack *extack) +int ksz8_fdb_dump(struct ksz_device *dev, int port, + dsa_fdb_dump_cb_t *cb, void *data) { - struct ksz_device *dev = ds->priv; + int ret = 0; + u16 i = 0; + u16 entries = 0; + u8 timestamp = 0; + u8 fid; + u8 member; + struct alu_struct alu; + + do { + alu.is_static = false; + ret = ksz8_r_dyn_mac_table(dev, i, alu.mac, &fid, &member, + ×tamp, &entries); + if (!ret && (member & BIT(port))) { + ret = cb(alu.mac, alu.fid, alu.is_static, data); + if (ret) + break; + } + i++; + } while (i < entries); + if (i >= entries) + ret = 0; + + return ret; +} + +int ksz8_mdb_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ + struct alu_struct alu; + int index; + int empty = 0; + + alu.port_forward = 0; + for (index = 0; index < dev->info->num_statics; index++) { + if (!ksz8_r_sta_mac_table(dev, index, &alu)) { + /* Found one already in static MAC table. */ + if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && + alu.fid == mdb->vid) + break; + /* Remember the first empty entry. */ + } else if (!empty) { + empty = index + 1; + } + } + + /* no available entry */ + if (index == dev->info->num_statics && !empty) + return -ENOSPC; + + /* add entry */ + if (index == dev->info->num_statics) { + index = empty - 1; + memset(&alu, 0, sizeof(alu)); + memcpy(alu.mac, mdb->addr, ETH_ALEN); + alu.is_static = true; + } + alu.port_forward |= BIT(port); + if (mdb->vid) { + alu.is_use_fid = true; + + /* Need a way to map VID to FID. */ + alu.fid = mdb->vid; + } + ksz8_w_sta_mac_table(dev, index, &alu); + return 0; +} + +int ksz8_mdb_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) +{ + struct alu_struct alu; + int index; + + for (index = 0; index < dev->info->num_statics; index++) { + if (!ksz8_r_sta_mac_table(dev, index, &alu)) { + /* Found one already in static MAC table. */ + if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && + alu.fid == mdb->vid) + break; + } + } + + /* no available entry */ + if (index == dev->info->num_statics) + goto exit; + + /* clear port */ + alu.port_forward &= ~BIT(port); + if (!alu.port_forward) + alu.is_static = false; + ksz8_w_sta_mac_table(dev, index, &alu); + +exit: + return 0; +} + +int ksz8_port_vlan_filtering(struct ksz_device *dev, int port, bool flag, + struct netlink_ext_ack *extack) +{ if (ksz_is_ksz88x3(dev)) return -ENOTSUPP; @@ -1105,12 +1026,11 @@ static void ksz8_port_enable_pvid(struct ksz_device *dev, int port, bool state) } } -static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) +int ksz8_port_vlan_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; - struct ksz_device *dev = ds->priv; struct ksz_port *p = &dev->ports[port]; u16 data, new_pvid = 0; u8 fid, member, valid; @@ -1131,7 +1051,7 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, * Remove Tag flag to be changed, unless there are no * other VLANs currently configured. */ - for (vid = 1; vid < dev->num_vlans; ++vid) { + for (vid = 1; vid < dev->info->num_vlans; ++vid) { /* Skip the VID we are going to add or reconfigure */ if (vid == vlan->vid) continue; @@ -1178,10 +1098,9 @@ static int ksz8_port_vlan_add(struct dsa_switch *ds, int port, return 0; } -static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +int ksz8_port_vlan_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan) { - struct ksz_device *dev = ds->priv; u16 data, pvid; u8 fid, member, valid; @@ -1211,12 +1130,10 @@ static int ksz8_port_vlan_del(struct dsa_switch *ds, int port, return 0; } -static int ksz8_port_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) +int ksz8_port_mirror_add(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) { - struct ksz_device *dev = ds->priv; - if (ingress) { ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true); dev->mirror_rx |= BIT(port); @@ -1235,10 +1152,9 @@ static int ksz8_port_mirror_add(struct dsa_switch *ds, int port, return 0; } -static void ksz8_port_mirror_del(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror) +void ksz8_port_mirror_del(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror) { - struct ksz_device *dev = ds->priv; u8 data; if (mirror->ingress) { @@ -1259,7 +1175,6 @@ static void ksz8_port_mirror_del(struct dsa_switch *ds, int port, static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; - u8 data8; if (!p->interface && dev->compat_interface) { dev_warn(dev->dev, @@ -1268,50 +1183,15 @@ static void ksz8795_cpu_interface_select(struct ksz_device *dev, int port) port); p->interface = dev->compat_interface; } - - /* Configure MII interface for proper network communication. */ - ksz_read8(dev, REG_PORT_5_CTRL_6, &data8); - data8 &= ~PORT_INTERFACE_TYPE; - data8 &= ~PORT_GMII_1GPS_MODE; - switch (p->interface) { - case PHY_INTERFACE_MODE_MII: - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_RMII: - data8 |= PORT_INTERFACE_RMII; - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_GMII: - data8 |= PORT_GMII_1GPS_MODE; - data8 |= PORT_INTERFACE_GMII; - p->phydev.speed = SPEED_1000; - break; - default: - data8 &= ~PORT_RGMII_ID_IN_ENABLE; - data8 &= ~PORT_RGMII_ID_OUT_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_RXID) - data8 |= PORT_RGMII_ID_IN_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_TXID) - data8 |= PORT_RGMII_ID_OUT_ENABLE; - data8 |= PORT_GMII_1GPS_MODE; - data8 |= PORT_INTERFACE_RGMII; - p->phydev.speed = SPEED_1000; - break; - } - ksz_write8(dev, REG_PORT_5_CTRL_6, data8); - p->phydev.duplex = 1; } -static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) +void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) { struct dsa_switch *ds = dev->ds; - struct ksz8 *ksz8 = dev->priv; const u32 *masks; u8 member; - masks = ksz8->masks; + masks = dev->info->masks; /* enable broadcast storm limit */ ksz_port_cfg(dev, port, P_BCAST_STORM_CTRL, PORT_BROADCAST_STORM, true); @@ -1341,17 +1221,17 @@ static void ksz8_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz8_cfg_port_member(dev, port, member); } -static void ksz8_config_cpu_port(struct dsa_switch *ds) +void ksz8_config_cpu_port(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; - struct ksz8 *ksz8 = dev->priv; - const u8 *regs = ksz8->regs; struct ksz_port *p; const u32 *masks; + const u16 *regs; u8 remote; int i; - masks = ksz8->masks; + masks = dev->info->masks; + regs = dev->info->regs; /* Switch marks the maximum frame with extra byte as oversize. */ ksz_cfg(dev, REG_SW_CTRL_2, SW_LEGAL_PACKET_DISABLE, true); @@ -1365,13 +1245,12 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds) for (i = 0; i < dev->phy_port_cnt; i++) { p = &dev->ports[i]; - ksz8_port_stp_state_set(ds, i, BR_STATE_DISABLED); + ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED); /* Last port may be disabled. */ if (i == dev->phy_port_cnt) break; p->on = 1; - p->phy = 1; } for (i = 0; i < dev->phy_port_cnt; i++) { p = &dev->ports[i]; @@ -1379,34 +1258,55 @@ static void ksz8_config_cpu_port(struct dsa_switch *ds) continue; if (!ksz_is_ksz88x3(dev)) { ksz_pread8(dev, i, regs[P_REMOTE_STATUS], &remote); - if (remote & PORT_FIBER_MODE) + if (remote & KSZ8_PORT_FIBER_MODE) p->fiber = 1; } if (p->fiber) - ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL, - true); + ksz_port_cfg(dev, i, regs[P_STP_CTRL], + PORT_FORCE_FLOW_CTRL, true); else - ksz_port_cfg(dev, i, P_STP_CTRL, PORT_FORCE_FLOW_CTRL, - false); + ksz_port_cfg(dev, i, regs[P_STP_CTRL], + PORT_FORCE_FLOW_CTRL, false); } } -static int ksz8_setup(struct dsa_switch *ds) +static int ksz8_handle_global_errata(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; + int ret = 0; + + /* KSZ87xx Errata DS80000687C. + * Module 2: Link drops with some EEE link partners. + * An issue with the EEE next page exchange between the + * KSZ879x/KSZ877x/KSZ876x and some EEE link partners may result in + * the link dropping. + */ + if (dev->info->ksz87xx_eee_link_erratum) + ret = ksz8_ind_write8(dev, TABLE_EEE, REG_IND_EEE_GLOB2_HI, 0); + + return ret; +} + +int ksz8_enable_stp_addr(struct ksz_device *dev) +{ struct alu_struct alu; - int i, ret = 0; - dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->num_vlans, GFP_KERNEL); - if (!dev->vlan_cache) - return -ENOMEM; + /* Setup STP address for STP operation. */ + memset(&alu, 0, sizeof(alu)); + ether_addr_copy(alu.mac, eth_stp_addr); + alu.is_static = true; + alu.is_override = true; + alu.port_forward = dev->info->cpu_ports; + + ksz8_w_sta_mac_table(dev, 0, &alu); - ret = ksz8_reset_switch(dev); - if (ret) { - dev_err(ds->dev, "failed to reset switch\n"); - return ret; - } + return 0; +} + +int ksz8_setup(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + int i; ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_FLOW_CTRL, true); @@ -1425,10 +1325,6 @@ static int ksz8_setup(struct dsa_switch *ds) UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP, UNICAST_VLAN_BOUNDARY | NO_EXC_COLLISION_DROP); - ksz8_config_cpu_port(ds); - - ksz_cfg(dev, REG_SW_CTRL_2, MULTICAST_STORM_DISABLE, true); - ksz_cfg(dev, S_REPLACE_VID_CTRL, SW_REPLACE_VID, false); ksz_cfg(dev, S_MIRROR_CTRL, SW_MIRROR_RX_TX, false); @@ -1436,52 +1332,16 @@ static int ksz8_setup(struct dsa_switch *ds) if (!ksz_is_ksz88x3(dev)) ksz_cfg(dev, REG_SW_CTRL_19, SW_INS_TAG_ENABLE, true); - /* set broadcast storm protection 10% rate */ - regmap_update_bits(dev->regmap[1], S_REPLACE_VID_CTRL, - BROADCAST_STORM_RATE, - (BROADCAST_STORM_VALUE * - BROADCAST_STORM_PROT_RATE) / 100); - - for (i = 0; i < (dev->num_vlans / 4); i++) + for (i = 0; i < (dev->info->num_vlans / 4); i++) ksz8_r_vlan_entries(dev, i); - /* Setup STP address for STP operation. */ - memset(&alu, 0, sizeof(alu)); - ether_addr_copy(alu.mac, eth_stp_addr); - alu.is_static = true; - alu.is_override = true; - alu.port_forward = dev->host_mask; - - ksz8_w_sta_mac_table(dev, 0, &alu); - - ksz_init_mib_timer(dev); - - ds->configure_vlan_while_not_filtering = false; - - return 0; + return ksz8_handle_global_errata(ds); } -static void ksz8_validate(struct dsa_switch *ds, int port, - unsigned long *supported, - struct phylink_link_state *state) +void ksz8_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config) { - __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; - struct ksz_device *dev = ds->priv; - - if (port == dev->cpu_port) { - if (state->interface != PHY_INTERFACE_MODE_RMII && - state->interface != PHY_INTERFACE_MODE_MII && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; - } else { - if (state->interface != PHY_INTERFACE_MODE_INTERNAL && - state->interface != PHY_INTERFACE_MODE_NA) - goto unsupported; - } - - /* Allow all the expected bits */ - phylink_set_port_modes(mask); - phylink_set(mask, Autoneg); + config->mac_capabilities = MAC_10 | MAC_100; /* Silicon Errata Sheet (DS80000830A): * "Port 1 does not respond to received flow control PAUSE frames" @@ -1489,232 +1349,23 @@ static void ksz8_validate(struct dsa_switch *ds, int port, * switches. */ if (!ksz_is_ksz88x3(dev) || port) - phylink_set(mask, Pause); + config->mac_capabilities |= MAC_SYM_PAUSE; /* Asym pause is not supported on KSZ8863 and KSZ8873 */ if (!ksz_is_ksz88x3(dev)) - phylink_set(mask, Asym_Pause); - - /* 10M and 100M are only supported */ - phylink_set(mask, 10baseT_Half); - phylink_set(mask, 10baseT_Full); - phylink_set(mask, 100baseT_Half); - phylink_set(mask, 100baseT_Full); - - linkmode_and(supported, supported, mask); - linkmode_and(state->advertising, state->advertising, mask); - - return; - -unsupported: - linkmode_zero(supported); - dev_err(ds->dev, "Unsupported interface: %s, port: %d\n", - phy_modes(state->interface), port); + config->mac_capabilities |= MAC_ASYM_PAUSE; } -static const struct dsa_switch_ops ksz8_switch_ops = { - .get_tag_protocol = ksz8_get_tag_protocol, - .get_phy_flags = ksz8_sw_get_phy_flags, - .setup = ksz8_setup, - .phy_read = ksz_phy_read16, - .phy_write = ksz_phy_write16, - .phylink_validate = ksz8_validate, - .phylink_mac_link_down = ksz_mac_link_down, - .port_enable = ksz_enable_port, - .get_strings = ksz8_get_strings, - .get_ethtool_stats = ksz_get_ethtool_stats, - .get_sset_count = ksz_sset_count, - .port_bridge_join = ksz_port_bridge_join, - .port_bridge_leave = ksz_port_bridge_leave, - .port_stp_state_set = ksz8_port_stp_state_set, - .port_fast_age = ksz_port_fast_age, - .port_vlan_filtering = ksz8_port_vlan_filtering, - .port_vlan_add = ksz8_port_vlan_add, - .port_vlan_del = ksz8_port_vlan_del, - .port_fdb_dump = ksz_port_fdb_dump, - .port_mdb_add = ksz_port_mdb_add, - .port_mdb_del = ksz_port_mdb_del, - .port_mirror_add = ksz8_port_mirror_add, - .port_mirror_del = ksz8_port_mirror_del, -}; - -static u32 ksz8_get_port_addr(int port, int offset) +u32 ksz8_get_port_addr(int port, int offset) { return PORT_CTRL_ADDR(port, offset); } -static int ksz8_switch_detect(struct ksz_device *dev) -{ - u8 id1, id2; - u16 id16; - int ret; - - /* read chip id */ - ret = ksz_read16(dev, REG_CHIP_ID0, &id16); - if (ret) - return ret; - - id1 = id16 >> 8; - id2 = id16 & SW_CHIP_ID_M; - - switch (id1) { - case KSZ87_FAMILY_ID: - if ((id2 != CHIP_ID_94 && id2 != CHIP_ID_95)) - return -ENODEV; - - if (id2 == CHIP_ID_95) { - u8 val; - - id2 = 0x95; - ksz_read8(dev, REG_PORT_STATUS_0, &val); - if (val & PORT_FIBER_MODE) - id2 = 0x65; - } else if (id2 == CHIP_ID_94) { - id2 = 0x94; - } - break; - case KSZ88_FAMILY_ID: - if (id2 != CHIP_ID_63) - return -ENODEV; - break; - default: - dev_err(dev->dev, "invalid family id: %d\n", id1); - return -ENODEV; - } - id16 &= ~0xff; - id16 |= id2; - dev->chip_id = id16; - - return 0; -} - -struct ksz_chip_data { - u16 chip_id; - const char *dev_name; - int num_vlans; - int num_alus; - int num_statics; - int cpu_ports; - int port_cnt; -}; - -static const struct ksz_chip_data ksz8_switch_chips[] = { - { - .chip_id = 0x8795, - .dev_name = "KSZ8795", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 5, /* total cpu and user ports */ - }, - { - /* - * WARNING - * ======= - * KSZ8794 is similar to KSZ8795, except the port map - * contains a gap between external and CPU ports, the - * port map is NOT continuous. The per-port register - * map is shifted accordingly too, i.e. registers at - * offset 0x40 are NOT used on KSZ8794 and they ARE - * used on KSZ8795 for external port 3. - * external cpu - * KSZ8794 0,1,2 4 - * KSZ8795 0,1,2,3 4 - * KSZ8765 0,1,2,3 4 - */ - .chip_id = 0x8794, - .dev_name = "KSZ8794", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 4, /* total cpu and user ports */ - }, - { - .chip_id = 0x8765, - .dev_name = "KSZ8765", - .num_vlans = 4096, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x10, /* can be configured as cpu port */ - .port_cnt = 5, /* total cpu and user ports */ - }, - { - .chip_id = 0x8830, - .dev_name = "KSZ8863/KSZ8873", - .num_vlans = 16, - .num_alus = 0, - .num_statics = 8, - .cpu_ports = 0x4, /* can be configured as cpu port */ - .port_cnt = 3, - }, -}; - -static int ksz8_switch_init(struct ksz_device *dev) +int ksz8_switch_init(struct ksz_device *dev) { - struct ksz8 *ksz8 = dev->priv; - int i; - - dev->ds->ops = &ksz8_switch_ops; - - for (i = 0; i < ARRAY_SIZE(ksz8_switch_chips); i++) { - const struct ksz_chip_data *chip = &ksz8_switch_chips[i]; - - if (dev->chip_id == chip->chip_id) { - dev->name = chip->dev_name; - dev->num_vlans = chip->num_vlans; - dev->num_alus = chip->num_alus; - dev->num_statics = chip->num_statics; - dev->port_cnt = fls(chip->cpu_ports); - dev->cpu_port = fls(chip->cpu_ports) - 1; - dev->phy_port_cnt = dev->port_cnt - 1; - dev->cpu_ports = chip->cpu_ports; - dev->host_mask = chip->cpu_ports; - dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | - chip->cpu_ports; - break; - } - } - - /* no switch found */ - if (!dev->cpu_ports) - return -ENODEV; - - if (ksz_is_ksz88x3(dev)) { - ksz8->regs = ksz8863_regs; - ksz8->masks = ksz8863_masks; - ksz8->shifts = ksz8863_shifts; - dev->mib_cnt = ARRAY_SIZE(ksz88xx_mib_names); - dev->mib_names = ksz88xx_mib_names; - } else { - ksz8->regs = ksz8795_regs; - ksz8->masks = ksz8795_masks; - ksz8->shifts = ksz8795_shifts; - dev->mib_cnt = ARRAY_SIZE(ksz87xx_mib_names); - dev->mib_names = ksz87xx_mib_names; - } - - dev->reg_mib_cnt = MIB_COUNTER_NUM; - - dev->ports = devm_kzalloc(dev->dev, - dev->port_cnt * sizeof(struct ksz_port), - GFP_KERNEL); - if (!dev->ports) - return -ENOMEM; - for (i = 0; i < dev->port_cnt; i++) { - mutex_init(&dev->ports[i].mib.cnt_mutex); - dev->ports[i].mib.counters = - devm_kzalloc(dev->dev, - sizeof(u64) * - (dev->mib_cnt + 1), - GFP_KERNEL); - if (!dev->ports[i].mib.counters) - return -ENOMEM; - } - - /* set the real number of ports */ - dev->ds->num_ports = dev->port_cnt; + dev->cpu_port = fls(dev->info->cpu_ports) - 1; + dev->phy_port_cnt = dev->info->port_cnt - 1; + dev->port_mask = (BIT(dev->phy_port_cnt) - 1) | dev->info->cpu_ports; /* We rely on software untagging on the CPU port, so that we * can support both tagged and untagged VLANs @@ -1729,37 +1380,11 @@ static int ksz8_switch_init(struct ksz_device *dev) return 0; } -static void ksz8_switch_exit(struct ksz_device *dev) +void ksz8_switch_exit(struct ksz_device *dev) { ksz8_reset_switch(dev); } -static const struct ksz_dev_ops ksz8_dev_ops = { - .get_port_addr = ksz8_get_port_addr, - .cfg_port_member = ksz8_cfg_port_member, - .flush_dyn_mac_table = ksz8_flush_dyn_mac_table, - .port_setup = ksz8_port_setup, - .r_phy = ksz8_r_phy, - .w_phy = ksz8_w_phy, - .r_dyn_mac_table = ksz8_r_dyn_mac_table, - .r_sta_mac_table = ksz8_r_sta_mac_table, - .w_sta_mac_table = ksz8_w_sta_mac_table, - .r_mib_cnt = ksz8_r_mib_cnt, - .r_mib_pkt = ksz8_r_mib_pkt, - .freeze_mib = ksz8_freeze_mib, - .port_init_cnt = ksz8_port_init_cnt, - .shutdown = ksz8_reset_switch, - .detect = ksz8_switch_detect, - .init = ksz8_switch_init, - .exit = ksz8_switch_exit, -}; - -int ksz8_switch_register(struct ksz_device *dev) -{ - return ksz_switch_register(dev, &ksz8_dev_ops); -} -EXPORT_SYMBOL(ksz8_switch_register); - MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz8795_reg.h b/drivers/net/dsa/microchip/ksz8795_reg.h index 6b40bc25f7ff..77487d611824 100644 --- a/drivers/net/dsa/microchip/ksz8795_reg.h +++ b/drivers/net/dsa/microchip/ksz8795_reg.h @@ -14,22 +14,8 @@ #define KS_PRIO_M 0x3 #define KS_PRIO_S 2 -#define REG_CHIP_ID0 0x00 - -#define KSZ87_FAMILY_ID 0x87 -#define KSZ88_FAMILY_ID 0x88 - -#define REG_CHIP_ID1 0x01 - -#define SW_CHIP_ID_M 0xF0 -#define SW_CHIP_ID_S 4 #define SW_REVISION_M 0x0E #define SW_REVISION_S 1 -#define SW_START 0x01 - -#define CHIP_ID_94 0x60 -#define CHIP_ID_95 0x90 -#define CHIP_ID_63 0x30 #define KSZ8863_REG_SW_RESET 0x43 @@ -57,7 +43,6 @@ #define REG_SW_CTRL_2 0x04 #define UNICAST_VLAN_BOUNDARY BIT(7) -#define MULTICAST_STORM_DISABLE BIT(6) #define SW_BACK_PRESSURE BIT(5) #define FAIR_FLOW_CTRL BIT(4) #define NO_EXC_COLLISION_DROP BIT(3) @@ -77,13 +62,9 @@ #define SW_FLOW_CTRL BIT(5) #define SW_10_MBIT BIT(4) #define SW_REPLACE_VID BIT(3) -#define BROADCAST_STORM_RATE_HI 0x07 #define REG_SW_CTRL_5 0x07 -#define BROADCAST_STORM_RATE_LO 0xFF -#define BROADCAST_STORM_RATE 0x07FF - #define REG_SW_CTRL_6 0x08 #define SW_MIB_COUNTER_FLUSH BIT(7) @@ -160,9 +141,6 @@ #define PORT_DISCARD_NON_VID BIT(5) #define PORT_FORCE_FLOW_CTRL BIT(4) #define PORT_BACK_PRESSURE BIT(3) -#define PORT_TX_ENABLE BIT(2) -#define PORT_RX_ENABLE BIT(1) -#define PORT_LEARN_DISABLE BIT(0) #define REG_PORT_1_CTRL_3 0x13 #define REG_PORT_2_CTRL_3 0x23 @@ -192,15 +170,7 @@ #define REG_PORT_5_CTRL_6 0x56 #define PORT_MII_INTERNAL_CLOCK BIT(7) -#define PORT_GMII_1GPS_MODE BIT(6) -#define PORT_RGMII_ID_IN_ENABLE BIT(4) -#define PORT_RGMII_ID_OUT_ENABLE BIT(3) #define PORT_GMII_MAC_MODE BIT(2) -#define PORT_INTERFACE_TYPE 0x3 -#define PORT_INTERFACE_MII 0 -#define PORT_INTERFACE_RMII 1 -#define PORT_INTERFACE_GMII 2 -#define PORT_INTERFACE_RGMII 3 #define REG_PORT_1_CTRL_7 0x17 #define REG_PORT_2_CTRL_7 0x27 @@ -220,8 +190,6 @@ #define REG_PORT_4_STATUS_0 0x48 /* For KSZ8765. */ -#define PORT_FIBER_MODE BIT(7) - #define PORT_REMOTE_ASYM_PAUSE BIT(5) #define PORT_REMOTE_SYM_PAUSE BIT(4) #define PORT_REMOTE_100BTX_FD BIT(3) @@ -325,7 +293,6 @@ #define REG_PORT_CTRL_5 0x05 -#define REG_PORT_STATUS_0 0x08 #define REG_PORT_STATUS_1 0x09 #define REG_PORT_LINK_MD_CTRL 0x0A #define REG_PORT_LINK_MD_RESULT 0x0B @@ -791,7 +758,6 @@ #define P_TAG_CTRL REG_PORT_CTRL_0 #define P_MIRROR_CTRL REG_PORT_CTRL_1 #define P_802_1P_CTRL REG_PORT_CTRL_2 -#define P_STP_CTRL REG_PORT_CTRL_2 #define P_PASS_ALL_CTRL REG_PORT_CTRL_12 #define P_INS_SRC_PVID_CTRL REG_PORT_CTRL_12 #define P_DROP_TAG_CTRL REG_PORT_CTRL_13 @@ -812,11 +778,9 @@ #define IND_ACC_TABLE(table) ((table) << 8) -/* Driver set switch broadcast storm protection at 10% rate. */ -#define BROADCAST_STORM_PROT_RATE 10 - -/* 148,800 frames * 67 ms / 100 */ -#define BROADCAST_STORM_VALUE 9969 +/* */ +#define REG_IND_EEE_GLOB2_LO 0x34 +#define REG_IND_EEE_GLOB2_HI 0x35 /** * MIB_COUNTER_VALUE 00-00000000-3FFFFFFF diff --git a/drivers/net/dsa/microchip/ksz8795_spi.c b/drivers/net/dsa/microchip/ksz8795_spi.c deleted file mode 100644 index 866767b70d65..000000000000 --- a/drivers/net/dsa/microchip/ksz8795_spi.c +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Microchip KSZ8795 series register access through SPI - * - * Copyright (C) 2017 Microchip Technology Inc. - * Tristram Ha <Tristram.Ha@microchip.com> - */ - -#include <asm/unaligned.h> - -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> - -#include "ksz8.h" -#include "ksz_common.h" - -#define KSZ8795_SPI_ADDR_SHIFT 12 -#define KSZ8795_SPI_ADDR_ALIGN 3 -#define KSZ8795_SPI_TURNAROUND_SHIFT 1 - -#define KSZ8863_SPI_ADDR_SHIFT 8 -#define KSZ8863_SPI_ADDR_ALIGN 8 -#define KSZ8863_SPI_TURNAROUND_SHIFT 0 - -KSZ_REGMAP_TABLE(ksz8795, 16, KSZ8795_SPI_ADDR_SHIFT, - KSZ8795_SPI_TURNAROUND_SHIFT, KSZ8795_SPI_ADDR_ALIGN); - -KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT, - KSZ8863_SPI_TURNAROUND_SHIFT, KSZ8863_SPI_ADDR_ALIGN); - -static int ksz8795_spi_probe(struct spi_device *spi) -{ - const struct regmap_config *regmap_config; - struct device *ddev = &spi->dev; - struct regmap_config rc; - struct ksz_device *dev; - struct ksz8 *ksz8; - int i, ret = 0; - - ksz8 = devm_kzalloc(&spi->dev, sizeof(struct ksz8), GFP_KERNEL); - if (!ksz8) - return -ENOMEM; - - ksz8->priv = spi; - - dev = ksz_switch_alloc(&spi->dev, ksz8); - if (!dev) - return -ENOMEM; - - regmap_config = device_get_match_data(ddev); - if (!regmap_config) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ksz8795_regmap_config); i++) { - rc = regmap_config[i]; - rc.lock_arg = &dev->regmap_mutex; - dev->regmap[i] = devm_regmap_init_spi(spi, &rc); - if (IS_ERR(dev->regmap[i])) { - ret = PTR_ERR(dev->regmap[i]); - dev_err(&spi->dev, - "Failed to initialize regmap%i: %d\n", - regmap_config[i].val_bits, ret); - return ret; - } - } - - if (spi->dev.platform_data) - dev->pdata = spi->dev.platform_data; - - /* setup spi */ - spi->mode = SPI_MODE_3; - ret = spi_setup(spi); - if (ret) - return ret; - - ret = ksz8_switch_register(dev); - - /* Main DSA driver may not be started yet. */ - if (ret) - return ret; - - spi_set_drvdata(spi, dev); - - return 0; -} - -static int ksz8795_spi_remove(struct spi_device *spi) -{ - struct ksz_device *dev = spi_get_drvdata(spi); - - if (dev) - ksz_switch_remove(dev); - - spi_set_drvdata(spi, NULL); - - return 0; -} - -static void ksz8795_spi_shutdown(struct spi_device *spi) -{ - struct ksz_device *dev = spi_get_drvdata(spi); - - if (!dev) - return; - - if (dev->dev_ops->shutdown) - dev->dev_ops->shutdown(dev); - - dsa_switch_shutdown(dev->ds); - - spi_set_drvdata(spi, NULL); -} - -static const struct of_device_id ksz8795_dt_ids[] = { - { .compatible = "microchip,ksz8765", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8794", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8795", .data = &ksz8795_regmap_config }, - { .compatible = "microchip,ksz8863", .data = &ksz8863_regmap_config }, - { .compatible = "microchip,ksz8873", .data = &ksz8863_regmap_config }, - {}, -}; -MODULE_DEVICE_TABLE(of, ksz8795_dt_ids); - -static struct spi_driver ksz8795_spi_driver = { - .driver = { - .name = "ksz8795-switch", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(ksz8795_dt_ids), - }, - .probe = ksz8795_spi_probe, - .remove = ksz8795_spi_remove, - .shutdown = ksz8795_spi_shutdown, -}; - -module_spi_driver(ksz8795_spi_driver); - -MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); -MODULE_DESCRIPTION("Microchip KSZ8795 Series Switch SPI Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz8863_smi.c b/drivers/net/dsa/microchip/ksz8863_smi.c index 5883fa7edda2..ddb40838181e 100644 --- a/drivers/net/dsa/microchip/ksz8863_smi.c +++ b/drivers/net/dsa/microchip/ksz8863_smi.c @@ -26,11 +26,9 @@ static int ksz8863_mdio_read(void *ctx, const void *reg_buf, size_t reg_len, struct mdio_device *mdev; u8 reg = *(u8 *)reg_buf; u8 *val = val_buf; - struct ksz8 *ksz8; int i, ret = 0; - ksz8 = dev->priv; - mdev = ksz8->priv; + mdev = dev->priv; mutex_lock_nested(&mdev->bus->mdio_lock, MDIO_MUTEX_NESTED); for (i = 0; i < val_len; i++) { @@ -55,13 +53,11 @@ static int ksz8863_mdio_write(void *ctx, const void *data, size_t count) { struct ksz_device *dev = ctx; struct mdio_device *mdev; - struct ksz8 *ksz8; int i, ret = 0; u32 reg; u8 *val; - ksz8 = dev->priv; - mdev = ksz8->priv; + mdev = dev->priv; val = (u8 *)(data + 4); reg = *(u32 *)data; @@ -142,17 +138,10 @@ static int ksz8863_smi_probe(struct mdio_device *mdiodev) { struct regmap_config rc; struct ksz_device *dev; - struct ksz8 *ksz8; int ret; int i; - ksz8 = devm_kzalloc(&mdiodev->dev, sizeof(struct ksz8), GFP_KERNEL); - if (!ksz8) - return -ENOMEM; - - ksz8->priv = mdiodev; - - dev = ksz_switch_alloc(&mdiodev->dev, ksz8); + dev = ksz_switch_alloc(&mdiodev->dev, mdiodev); if (!dev) return -ENOMEM; @@ -174,7 +163,7 @@ static int ksz8863_smi_probe(struct mdio_device *mdiodev) if (mdiodev->dev.platform_data) dev->pdata = mdiodev->dev.platform_data; - ret = ksz8_switch_register(dev); + ret = ksz_switch_register(dev); /* Main DSA driver may not be started yet. */ if (ret) @@ -191,8 +180,6 @@ static void ksz8863_smi_remove(struct mdio_device *mdiodev) if (dev) ksz_switch_remove(dev); - - dev_set_drvdata(&mdiodev->dev, NULL); } static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) @@ -206,8 +193,14 @@ static void ksz8863_smi_shutdown(struct mdio_device *mdiodev) } static const struct of_device_id ksz8863_dt_ids[] = { - { .compatible = "microchip,ksz8863" }, - { .compatible = "microchip,ksz8873" }, + { + .compatible = "microchip,ksz8863", + .data = &ksz_switch_chips[KSZ8830] + }, + { + .compatible = "microchip,ksz8873", + .data = &ksz_switch_chips[KSZ8830] + }, { }, }; MODULE_DEVICE_TABLE(of, ksz8863_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz9477.c b/drivers/net/dsa/microchip/ksz9477.c index 353b5f981740..a6a0321a8931 100644 --- a/drivers/net/dsa/microchip/ksz9477.c +++ b/drivers/net/dsa/microchip/ksz9477.c @@ -11,58 +11,13 @@ #include <linux/platform_data/microchip-ksz.h> #include <linux/phy.h> #include <linux/if_bridge.h> +#include <linux/if_vlan.h> #include <net/dsa.h> #include <net/switchdev.h> #include "ksz9477_reg.h" #include "ksz_common.h" - -/* Used with variable features to indicate capabilities. */ -#define GBIT_SUPPORT BIT(0) -#define NEW_XMII BIT(1) -#define IS_9893 BIT(2) - -static const struct { - int index; - char string[ETH_GSTRING_LEN]; -} ksz9477_mib_names[TOTAL_SWITCH_COUNTER_NUM] = { - { 0x00, "rx_hi" }, - { 0x01, "rx_undersize" }, - { 0x02, "rx_fragments" }, - { 0x03, "rx_oversize" }, - { 0x04, "rx_jabbers" }, - { 0x05, "rx_symbol_err" }, - { 0x06, "rx_crc_err" }, - { 0x07, "rx_align_err" }, - { 0x08, "rx_mac_ctrl" }, - { 0x09, "rx_pause" }, - { 0x0A, "rx_bcast" }, - { 0x0B, "rx_mcast" }, - { 0x0C, "rx_ucast" }, - { 0x0D, "rx_64_or_less" }, - { 0x0E, "rx_65_127" }, - { 0x0F, "rx_128_255" }, - { 0x10, "rx_256_511" }, - { 0x11, "rx_512_1023" }, - { 0x12, "rx_1024_1522" }, - { 0x13, "rx_1523_2000" }, - { 0x14, "rx_2001" }, - { 0x15, "tx_hi" }, - { 0x16, "tx_late_col" }, - { 0x17, "tx_pause" }, - { 0x18, "tx_bcast" }, - { 0x19, "tx_mcast" }, - { 0x1A, "tx_ucast" }, - { 0x1B, "tx_deferred" }, - { 0x1C, "tx_total_col" }, - { 0x1D, "tx_exc_col" }, - { 0x1E, "tx_single_col" }, - { 0x1F, "tx_mult_col" }, - { 0x80, "rx_total" }, - { 0x81, "tx_total" }, - { 0x82, "rx_discards" }, - { 0x83, "tx_discards" }, -}; +#include "ksz9477.h" static void ksz_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) { @@ -88,6 +43,28 @@ static void ksz9477_port_cfg32(struct ksz_device *dev, int port, int offset, bits, set ? bits : 0); } +int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu) +{ + u16 frame_size, max_frame = 0; + int i; + + frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; + + /* Cache the per-port MTU setting */ + dev->ports[port].max_frame = frame_size; + + for (i = 0; i < dev->info->port_cnt; i++) + max_frame = max(max_frame, dev->ports[i].max_frame); + + return regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, + REG_SW_MTU_MASK, max_frame); +} + +int ksz9477_max_mtu(struct ksz_device *dev, int port) +{ + return KSZ9477_MAX_FRAME_SIZE - VLAN_ETH_HLEN - ETH_FCS_LEN; +} + static int ksz9477_wait_vlan_ctrl_ready(struct ksz_device *dev) { unsigned int val; @@ -193,7 +170,7 @@ static int ksz9477_wait_alu_sta_ready(struct ksz_device *dev) 10, 1000); } -static int ksz9477_reset_switch(struct ksz_device *dev) +int ksz9477_reset_switch(struct ksz_device *dev) { u8 data8; u32 data32; @@ -216,21 +193,22 @@ static int ksz9477_reset_switch(struct ksz_device *dev) ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0x7F); ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32); - /* set broadcast storm protection 10% rate */ - regmap_update_bits(dev->regmap[1], REG_SW_MAC_CTRL_2, - BROADCAST_STORM_RATE, - (BROADCAST_STORM_VALUE * - BROADCAST_STORM_PROT_RATE) / 100); + /* KSZ9893 compatible chips do not support refclk configuration */ + if (dev->chip_id == KSZ9893_CHIP_ID || + dev->chip_id == KSZ8563_CHIP_ID) + return 0; - if (dev->synclko_125) - ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, - SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ); + data8 = SW_ENABLE_REFCLKO; + if (dev->synclko_disable) + data8 = 0; + else if (dev->synclko_125) + data8 = SW_ENABLE_REFCLKO | SW_REFCLKO_IS_125MHZ; + ksz_write8(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, data8); return 0; } -static void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, - u64 *cnt) +void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt) { struct ksz_port *p = &dev->ports[port]; unsigned int val; @@ -257,14 +235,14 @@ static void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, *cnt += data; } -static void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, - u64 *dropped, u64 *cnt) +void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt) { - addr = ksz9477_mib_names[addr].index; + addr = dev->info->mib_names[addr].index; ksz9477_r_mib_cnt(dev, port, addr, cnt); } -static void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze) +void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze) { u32 val = freeze ? MIB_COUNTER_FLUSH_FREEZE : 0; struct ksz_port *p = &dev->ports[port]; @@ -278,7 +256,7 @@ static void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze) mutex_unlock(&p->mib.cnt_mutex); } -static void ksz9477_port_init_cnt(struct ksz_device *dev, int port) +void ksz9477_port_init_cnt(struct ksz_device *dev, int port) { struct ksz_port_mib *mib = &dev->ports[port].mib; @@ -289,27 +267,22 @@ static void ksz9477_port_init_cnt(struct ksz_device *dev, int port) ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH); ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0); mutex_unlock(&mib->cnt_mutex); - - mib->cnt_ptr = 0; - memset(mib->counters, 0, dev->mib_cnt * sizeof(u64)); } -static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds, - int port, - enum dsa_tag_protocol mp) +static void ksz9477_r_phy_quirks(struct ksz_device *dev, u16 addr, u16 reg, + u16 *data) { - enum dsa_tag_protocol proto = DSA_TAG_PROTO_KSZ9477; - struct ksz_device *dev = ds->priv; - - if (dev->features & IS_9893) - proto = DSA_TAG_PROTO_KSZ9893; - return proto; + /* KSZ8563R do not have extended registers but BMSR_ESTATEN and + * BMSR_ERCAP bits are set. + */ + if (dev->chip_id == KSZ8563_CHIP_ID && reg == MII_BMSR) + *data &= ~(BMSR_ESTATEN | BMSR_ERCAP); } -static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) +int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) { - struct ksz_device *dev = ds->priv; u16 val = 0xffff; + int ret; /* No real PHY after this. Simulate the PHY. * A fixed PHY can be setup in the device tree, but this function is @@ -317,7 +290,7 @@ static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) * For RGMII PHY there is no way to access it so the fixed PHY should * be used. For SGMII PHY the supporting code will be added later. */ - if (addr >= dev->phy_port_cnt) { + if (!dev->info->internal_phy[addr]) { struct ksz_port *p = &dev->ports[addr]; switch (reg) { @@ -350,114 +323,58 @@ static int ksz9477_phy_read16(struct dsa_switch *ds, int addr, int reg) break; } } else { - ksz_pread16(dev, addr, 0x100 + (reg << 1), &val); - } - - return val; -} - -static int ksz9477_phy_write16(struct dsa_switch *ds, int addr, int reg, - u16 val) -{ - struct ksz_device *dev = ds->priv; + ret = ksz_pread16(dev, addr, 0x100 + (reg << 1), &val); + if (ret) + return ret; - /* No real PHY after this. */ - if (addr >= dev->phy_port_cnt) - return 0; + ksz9477_r_phy_quirks(dev, addr, reg, &val); + } - /* No gigabit support. Do not write to this register. */ - if (!(dev->features & GBIT_SUPPORT) && reg == MII_CTRL1000) - return 0; - ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val); + *data = val; return 0; } -static void ksz9477_get_strings(struct dsa_switch *ds, int port, - u32 stringset, uint8_t *buf) +int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) { - int i; - - if (stringset != ETH_SS_STATS) - return; + /* No real PHY after this. */ + if (!dev->info->internal_phy[addr]) + return 0; - for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) { - memcpy(buf + i * ETH_GSTRING_LEN, ksz9477_mib_names[i].string, - ETH_GSTRING_LEN); - } + return ksz_pwrite16(dev, addr, 0x100 + (reg << 1), val); } -static void ksz9477_cfg_port_member(struct ksz_device *dev, int port, - u8 member) +void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member) { ksz_pwrite32(dev, port, REG_PORT_VLAN_MEMBERSHIP__4, member); } -static void ksz9477_port_stp_state_set(struct dsa_switch *ds, int port, - u8 state) -{ - struct ksz_device *dev = ds->priv; - struct ksz_port *p = &dev->ports[port]; - u8 data; - - ksz_pread8(dev, port, P_STP_CTRL, &data); - data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); - - switch (state) { - case BR_STATE_DISABLED: - data |= PORT_LEARN_DISABLE; - break; - case BR_STATE_LISTENING: - data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); - break; - case BR_STATE_LEARNING: - data |= PORT_RX_ENABLE; - break; - case BR_STATE_FORWARDING: - data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); - break; - case BR_STATE_BLOCKING: - data |= PORT_LEARN_DISABLE; - break; - default: - dev_err(ds->dev, "invalid STP state: %d\n", state); - return; - } - - ksz_pwrite8(dev, port, P_STP_CTRL, data); - p->stp_state = state; - - ksz_update_port_member(dev, port); -} - -static void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) +void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port) { + const u16 *regs = dev->info->regs; u8 data; regmap_update_bits(dev->regmap[0], REG_SW_LUE_CTRL_2, SW_FLUSH_OPTION_M << SW_FLUSH_OPTION_S, SW_FLUSH_OPTION_DYN_MAC << SW_FLUSH_OPTION_S); - if (port < dev->port_cnt) { + if (port < dev->info->port_cnt) { /* flush individual port */ - ksz_pread8(dev, port, P_STP_CTRL, &data); + ksz_pread8(dev, port, regs[P_STP_CTRL], &data); if (!(data & PORT_LEARN_DISABLE)) - ksz_pwrite8(dev, port, P_STP_CTRL, + ksz_pwrite8(dev, port, regs[P_STP_CTRL], data | PORT_LEARN_DISABLE); ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_DYN_MAC_TABLE, true); - ksz_pwrite8(dev, port, P_STP_CTRL, data); + ksz_pwrite8(dev, port, regs[P_STP_CTRL], data); } else { /* flush all */ ksz_cfg(dev, S_FLUSH_TABLE_CTRL, SW_FLUSH_STP_TABLE, true); } } -static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, - bool flag, - struct netlink_ext_ack *extack) +int ksz9477_port_vlan_filtering(struct ksz_device *dev, int port, + bool flag, struct netlink_ext_ack *extack) { - struct ksz_device *dev = ds->priv; - if (flag) { ksz_port_cfg(dev, port, REG_PORT_LUE_CTRL, PORT_VLAN_LOOKUP_VID_0, true); @@ -471,11 +388,10 @@ static int ksz9477_port_vlan_filtering(struct dsa_switch *ds, int port, return 0; } -static int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan, - struct netlink_ext_ack *extack) +int ksz9477_port_vlan_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) { - struct ksz_device *dev = ds->priv; u32 vlan_table[3]; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; int err; @@ -508,10 +424,9 @@ static int ksz9477_port_vlan_add(struct dsa_switch *ds, int port, return 0; } -static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_vlan *vlan) +int ksz9477_port_vlan_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan) { - struct ksz_device *dev = ds->priv; bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; u32 vlan_table[3]; u16 pvid; @@ -542,10 +457,9 @@ static int ksz9477_port_vlan_del(struct dsa_switch *ds, int port, return 0; } -static int ksz9477_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int ksz9477_fdb_add(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) { - struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; int ret = 0; @@ -599,10 +513,9 @@ exit: return ret; } -static int ksz9477_port_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int ksz9477_fdb_del(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db) { - struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; int ret = 0; @@ -689,10 +602,9 @@ static void ksz9477_convert_alu(struct alu_struct *alu, u32 *alu_table) alu->mac[5] = alu_table[3] & 0xFF; } -static int ksz9477_port_fdb_dump(struct dsa_switch *ds, int port, - dsa_fdb_dump_cb_t *cb, void *data) +int ksz9477_fdb_dump(struct ksz_device *dev, int port, + dsa_fdb_dump_cb_t *cb, void *data) { - struct ksz_device *dev = ds->priv; int ret = 0; u32 ksz_data; u32 alu_table[4]; @@ -719,6 +631,9 @@ static int ksz9477_port_fdb_dump(struct dsa_switch *ds, int port, goto exit; } + if (!(ksz_data & ALU_VALID)) + continue; + /* read ALU table */ ksz9477_read_table(dev, alu_table); @@ -741,26 +656,30 @@ exit: return ret; } -static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +int ksz9477_mdb_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { - struct ksz_device *dev = ds->priv; u32 static_table[4]; + const u8 *shifts; + const u32 *masks; u32 data; int index; u32 mac_hi, mac_lo; int err = 0; + shifts = dev->info->shifts; + masks = dev->info->masks; + mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]); mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16)); mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]); mutex_lock(&dev->alu_mutex); - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { /* find empty slot first */ - data = (index << ALU_STAT_INDEX_S) | - ALU_STAT_READ | ALU_STAT_START; + data = (index << shifts[ALU_STAT_INDEX]) | + masks[ALU_STAT_READ] | ALU_STAT_START; ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); /* wait to be finished */ @@ -788,7 +707,7 @@ static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) { + if (index == dev->info->num_statics) { err = -ENOSPC; goto exit; } @@ -804,7 +723,7 @@ static int ksz9477_port_mdb_add(struct dsa_switch *ds, int port, ksz9477_write_table(dev, static_table); - data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START; + data = (index << shifts[ALU_STAT_INDEX]) | ALU_STAT_START; ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); /* wait to be finished */ @@ -816,26 +735,30 @@ exit: return err; } -static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +int ksz9477_mdb_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db) { - struct ksz_device *dev = ds->priv; u32 static_table[4]; + const u8 *shifts; + const u32 *masks; u32 data; int index; int ret = 0; u32 mac_hi, mac_lo; + shifts = dev->info->shifts; + masks = dev->info->masks; + mac_hi = ((mdb->addr[0] << 8) | mdb->addr[1]); mac_lo = ((mdb->addr[2] << 24) | (mdb->addr[3] << 16)); mac_lo |= ((mdb->addr[4] << 8) | mdb->addr[5]); mutex_lock(&dev->alu_mutex); - for (index = 0; index < dev->num_statics; index++) { + for (index = 0; index < dev->info->num_statics; index++) { /* find empty slot first */ - data = (index << ALU_STAT_INDEX_S) | - ALU_STAT_READ | ALU_STAT_START; + data = (index << shifts[ALU_STAT_INDEX]) | + masks[ALU_STAT_READ] | ALU_STAT_START; ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); /* wait to be finished */ @@ -861,7 +784,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, } /* no available entry */ - if (index == dev->num_statics) + if (index == dev->info->num_statics) goto exit; /* clear port */ @@ -877,7 +800,7 @@ static int ksz9477_port_mdb_del(struct dsa_switch *ds, int port, ksz9477_write_table(dev, static_table); - data = (index << ALU_STAT_INDEX_S) | ALU_STAT_START; + data = (index << shifts[ALU_STAT_INDEX]) | ALU_STAT_START; ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); /* wait to be finished */ @@ -891,19 +814,36 @@ exit: return ret; } -static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror, - bool ingress) +int ksz9477_port_mirror_add(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) { - struct ksz_device *dev = ds->priv; + u8 data; + int p; + + /* Limit to one sniffer port + * Check if any of the port is already set for sniffing + * If yes, instruct the user to remove the previous entry & exit + */ + for (p = 0; p < dev->info->port_cnt; p++) { + /* Skip the current sniffing port */ + if (p == mirror->to_local_port) + continue; + + ksz_pread8(dev, p, P_MIRROR_CTRL, &data); + + if (data & PORT_MIRROR_SNIFFER) { + NL_SET_ERR_MSG_MOD(extack, + "Sniffer port is already configured, delete existing rules & retry"); + return -EBUSY; + } + } if (ingress) ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, true); else ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, true); - ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, false); - /* configure mirror port */ ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, PORT_MIRROR_SNIFFER, true); @@ -913,160 +853,47 @@ static int ksz9477_port_mirror_add(struct dsa_switch *ds, int port, return 0; } -static void ksz9477_port_mirror_del(struct dsa_switch *ds, int port, - struct dsa_mall_mirror_tc_entry *mirror) +void ksz9477_port_mirror_del(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror) { - struct ksz_device *dev = ds->priv; + bool in_use = false; u8 data; + int p; if (mirror->ingress) ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_RX, false); else ksz_port_cfg(dev, port, P_MIRROR_CTRL, PORT_MIRROR_TX, false); - ksz_pread8(dev, port, P_MIRROR_CTRL, &data); - - if (!(data & (PORT_MIRROR_RX | PORT_MIRROR_TX))) - ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, - PORT_MIRROR_SNIFFER, false); -} - -static bool ksz9477_get_gbit(struct ksz_device *dev, u8 data) -{ - bool gbit; - - if (dev->features & NEW_XMII) - gbit = !(data & PORT_MII_NOT_1GBIT); - else - gbit = !!(data & PORT_MII_1000MBIT_S1); - return gbit; -} - -static void ksz9477_set_gbit(struct ksz_device *dev, bool gbit, u8 *data) -{ - if (dev->features & NEW_XMII) { - if (gbit) - *data &= ~PORT_MII_NOT_1GBIT; - else - *data |= PORT_MII_NOT_1GBIT; - } else { - if (gbit) - *data |= PORT_MII_1000MBIT_S1; - else - *data &= ~PORT_MII_1000MBIT_S1; - } -} -static int ksz9477_get_xmii(struct ksz_device *dev, u8 data) -{ - int mode; + /* Check if any of the port is still referring to sniffer port */ + for (p = 0; p < dev->info->port_cnt; p++) { + ksz_pread8(dev, p, P_MIRROR_CTRL, &data); - if (dev->features & NEW_XMII) { - switch (data & PORT_MII_SEL_M) { - case PORT_MII_SEL: - mode = 0; - break; - case PORT_RMII_SEL: - mode = 1; - break; - case PORT_GMII_SEL: - mode = 2; - break; - default: - mode = 3; - } - } else { - switch (data & PORT_MII_SEL_M) { - case PORT_MII_SEL_S1: - mode = 0; - break; - case PORT_RMII_SEL_S1: - mode = 1; - break; - case PORT_GMII_SEL_S1: - mode = 2; + if ((data & (PORT_MIRROR_RX | PORT_MIRROR_TX))) { + in_use = true; break; - default: - mode = 3; } } - return mode; -} - -static void ksz9477_set_xmii(struct ksz_device *dev, int mode, u8 *data) -{ - u8 xmii; - if (dev->features & NEW_XMII) { - switch (mode) { - case 0: - xmii = PORT_MII_SEL; - break; - case 1: - xmii = PORT_RMII_SEL; - break; - case 2: - xmii = PORT_GMII_SEL; - break; - default: - xmii = PORT_RGMII_SEL; - break; - } - } else { - switch (mode) { - case 0: - xmii = PORT_MII_SEL_S1; - break; - case 1: - xmii = PORT_RMII_SEL_S1; - break; - case 2: - xmii = PORT_GMII_SEL_S1; - break; - default: - xmii = PORT_RGMII_SEL_S1; - break; - } - } - *data &= ~PORT_MII_SEL_M; - *data |= xmii; + /* delete sniffing if there are no other mirroring rules */ + if (!in_use) + ksz_port_cfg(dev, mirror->to_local_port, P_MIRROR_CTRL, + PORT_MIRROR_SNIFFER, false); } static phy_interface_t ksz9477_get_interface(struct ksz_device *dev, int port) { phy_interface_t interface; bool gbit; - int mode; - u8 data8; - if (port < dev->phy_port_cnt) + if (dev->info->internal_phy[port]) return PHY_INTERFACE_MODE_NA; - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - gbit = ksz9477_get_gbit(dev, data8); - mode = ksz9477_get_xmii(dev, data8); - switch (mode) { - case 2: - interface = PHY_INTERFACE_MODE_GMII; - if (gbit) - break; - fallthrough; - case 0: - interface = PHY_INTERFACE_MODE_MII; - break; - case 1: - interface = PHY_INTERFACE_MODE_RMII; - break; - default: - interface = PHY_INTERFACE_MODE_RGMII; - if (data8 & PORT_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_TXID; - if (data8 & PORT_RGMII_ID_IG_ENABLE) { - interface = PHY_INTERFACE_MODE_RGMII_RXID; - if (data8 & PORT_RGMII_ID_EG_ENABLE) - interface = PHY_INTERFACE_MODE_RGMII_ID; - } - break; - } + + gbit = ksz_get_gbit(dev, port); + + interface = ksz_get_xmii(dev, port, gbit); + return interface; } @@ -1105,7 +932,7 @@ static void ksz9477_phy_errata_setup(struct ksz_device *dev, int port) /* Energy Efficient Ethernet (EEE) feature select must * be manually disabled (except on KSZ8565 which is 100Mbit) */ - if (dev->features & GBIT_SUPPORT) + if (dev->info->gbit_capable[port]) ksz9477_port_mmd_write(dev, port, 0x07, 0x3c, 0x0000); /* Register settings are required to meet data sheet @@ -1126,12 +953,46 @@ static void ksz9477_phy_errata_setup(struct ksz_device *dev, int port) ksz9477_port_mmd_write(dev, port, 0x1c, 0x20, 0xeeee); } -static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) +void ksz9477_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config) +{ + config->mac_capabilities = MAC_10 | MAC_100 | MAC_ASYM_PAUSE | + MAC_SYM_PAUSE; + + if (dev->info->gbit_capable[port]) + config->mac_capabilities |= MAC_1000FD; +} + +int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs) +{ + u32 secs = msecs / 1000; + u8 value; + u8 data; + int ret; + + value = FIELD_GET(SW_AGE_PERIOD_7_0_M, secs); + + ret = ksz_write8(dev, REG_SW_LUE_CTRL_3, value); + if (ret < 0) + return ret; + + data = FIELD_GET(SW_AGE_PERIOD_10_8_M, secs); + + ret = ksz_read8(dev, REG_SW_LUE_CTRL_0, &value); + if (ret < 0) + return ret; + + value &= ~SW_AGE_CNT_M; + value |= FIELD_PREP(SW_AGE_CNT_M, data); + + return ksz_write8(dev, REG_SW_LUE_CTRL_0, value); +} + +void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) { - struct ksz_port *p = &dev->ports[port]; struct dsa_switch *ds = dev->ds; - u8 data8, member; u16 data16; + u8 member; /* enable tag tail for host port */ if (cpu_port) @@ -1158,57 +1019,19 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) /* enable 802.1p priority */ ksz_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true); - if (port < dev->phy_port_cnt) { + if (dev->info->internal_phy[port]) { /* do not force flow control */ ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, false); - if (dev->phy_errata_9477) + if (dev->info->phy_errata_9477) ksz9477_phy_errata_setup(dev, port); } else { /* force flow control */ ksz_port_cfg(dev, port, REG_PORT_CTRL_0, PORT_FORCE_TX_FLOW_CTRL | PORT_FORCE_RX_FLOW_CTRL, true); - - /* configure MAC to 1G & RGMII mode */ - ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); - switch (p->interface) { - case PHY_INTERFACE_MODE_MII: - ksz9477_set_xmii(dev, 0, &data8); - ksz9477_set_gbit(dev, false, &data8); - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_RMII: - ksz9477_set_xmii(dev, 1, &data8); - ksz9477_set_gbit(dev, false, &data8); - p->phydev.speed = SPEED_100; - break; - case PHY_INTERFACE_MODE_GMII: - ksz9477_set_xmii(dev, 2, &data8); - ksz9477_set_gbit(dev, true, &data8); - p->phydev.speed = SPEED_1000; - break; - default: - ksz9477_set_xmii(dev, 3, &data8); - ksz9477_set_gbit(dev, true, &data8); - data8 &= ~PORT_RGMII_ID_IG_ENABLE; - data8 &= ~PORT_RGMII_ID_EG_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_RXID) - data8 |= PORT_RGMII_ID_IG_ENABLE; - if (p->interface == PHY_INTERFACE_MODE_RGMII_ID || - p->interface == PHY_INTERFACE_MODE_RGMII_TXID) - data8 |= PORT_RGMII_ID_EG_ENABLE; - /* On KSZ9893, disable RGMII in-band status support */ - if (dev->features & IS_9893) - data8 &= ~PORT_MII_MAC_MODE; - p->phydev.speed = SPEED_1000; - break; - } - ksz_pwrite8(dev, port, REG_PORT_XMII_CTRL_1, data8); - p->phydev.duplex = 1; } if (cpu_port) @@ -1219,18 +1042,19 @@ static void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port) ksz9477_cfg_port_member(dev, port, member); /* clear pending interrupts */ - if (port < dev->phy_port_cnt) + if (dev->info->internal_phy[port]) ksz_pread16(dev, port, REG_PORT_PHY_INT_ENABLE, &data16); } -static void ksz9477_config_cpu_port(struct dsa_switch *ds) +void ksz9477_config_cpu_port(struct dsa_switch *ds) { struct ksz_device *dev = ds->priv; struct ksz_port *p; int i; - for (i = 0; i < dev->port_cnt; i++) { - if (dsa_is_cpu_port(ds, i) && (dev->cpu_ports & (1 << i))) { + for (i = 0; i < dev->info->port_cnt; i++) { + if (dsa_is_cpu_port(ds, i) && + (dev->info->cpu_ports & (1 << i))) { phy_interface_t interface; const char *prev_msg; const char *prev_mode; @@ -1270,44 +1094,54 @@ static void ksz9477_config_cpu_port(struct dsa_switch *ds) /* enable cpu port */ ksz9477_port_setup(dev, i, true); - p->on = 1; } } - for (i = 0; i < dev->port_cnt; i++) { + for (i = 0; i < dev->info->port_cnt; i++) { if (i == dev->cpu_port) continue; - p = &dev->ports[i]; - - ksz9477_port_stp_state_set(ds, i, BR_STATE_DISABLED); - p->on = 1; - if (i < dev->phy_port_cnt) - p->phy = 1; - if (dev->chip_id == 0x00947700 && i == 6) { - p->sgmii = 1; - - /* SGMII PHY detection code is not implemented yet. */ - p->phy = 0; - } + ksz_port_stp_state_set(ds, i, BR_STATE_DISABLED); } } -static int ksz9477_setup(struct dsa_switch *ds) +int ksz9477_enable_stp_addr(struct ksz_device *dev) { - struct ksz_device *dev = ds->priv; - int ret = 0; + const u32 *masks; + u32 data; + int ret; - dev->vlan_cache = devm_kcalloc(dev->dev, sizeof(struct vlan_table), - dev->num_vlans, GFP_KERNEL); - if (!dev->vlan_cache) - return -ENOMEM; + masks = dev->info->masks; - ret = ksz9477_reset_switch(dev); - if (ret) { - dev_err(ds->dev, "failed to reset switch\n"); + /* Enable Reserved multicast table */ + ksz_cfg(dev, REG_SW_LUE_CTRL_0, SW_RESV_MCAST_ENABLE, true); + + /* Set the Override bit for forwarding BPDU packet to CPU */ + ret = ksz_write32(dev, REG_SW_ALU_VAL_B, + ALU_V_OVERRIDE | BIT(dev->cpu_port)); + if (ret < 0) + return ret; + + data = ALU_STAT_START | ALU_RESV_MCAST_ADDR | masks[ALU_STAT_WRITE]; + + ret = ksz_write32(dev, REG_SW_ALU_STAT_CTRL__4, data); + if (ret < 0) + return ret; + + /* wait to be finished */ + ret = ksz9477_wait_alu_sta_ready(dev); + if (ret < 0) { + dev_err(dev->dev, "Failed to update Reserved Multicast table\n"); return ret; } + return 0; +} + +int ksz9477_setup(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + int ret = 0; + /* Required for port partitioning. */ ksz9477_cfg32(dev, REG_SW_QM_CTRL__4, UNICAST_VLAN_BOUNDARY, true); @@ -1315,12 +1149,14 @@ static int ksz9477_setup(struct dsa_switch *ds) /* Do not work correctly with tail tagging. */ ksz_cfg(dev, REG_SW_MAC_CTRL_0, SW_CHECK_LENGTH, false); - /* accept packet up to 2000bytes */ - ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_LEGAL_PACKET_DISABLE, true); + /* Enable REG_SW_MTU__2 reg by setting SW_JUMBO_PACKET */ + ksz_cfg(dev, REG_SW_MAC_CTRL_1, SW_JUMBO_PACKET, true); - ksz9477_config_cpu_port(ds); - - ksz_cfg(dev, REG_SW_MAC_CTRL_1, MULTICAST_STORM_DISABLE, true); + /* Now we can configure default MTU value */ + ret = regmap_update_bits(dev->regmap[1], REG_SW_MTU__2, REG_SW_MTU_MASK, + VLAN_ETH_FRAME_LEN + ETH_FCS_LEN); + if (ret) + return ret; /* queue based egress rate limit */ ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true); @@ -1328,55 +1164,21 @@ static int ksz9477_setup(struct dsa_switch *ds) /* enable global MIB counter freeze function */ ksz_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true); - /* start switch */ - ksz_cfg(dev, REG_SW_OPERATION, SW_START, true); - - ksz_init_mib_timer(dev); - - ds->configure_vlan_while_not_filtering = false; - return 0; } -static const struct dsa_switch_ops ksz9477_switch_ops = { - .get_tag_protocol = ksz9477_get_tag_protocol, - .setup = ksz9477_setup, - .phy_read = ksz9477_phy_read16, - .phy_write = ksz9477_phy_write16, - .phylink_mac_link_down = ksz_mac_link_down, - .port_enable = ksz_enable_port, - .get_strings = ksz9477_get_strings, - .get_ethtool_stats = ksz_get_ethtool_stats, - .get_sset_count = ksz_sset_count, - .port_bridge_join = ksz_port_bridge_join, - .port_bridge_leave = ksz_port_bridge_leave, - .port_stp_state_set = ksz9477_port_stp_state_set, - .port_fast_age = ksz_port_fast_age, - .port_vlan_filtering = ksz9477_port_vlan_filtering, - .port_vlan_add = ksz9477_port_vlan_add, - .port_vlan_del = ksz9477_port_vlan_del, - .port_fdb_dump = ksz9477_port_fdb_dump, - .port_fdb_add = ksz9477_port_fdb_add, - .port_fdb_del = ksz9477_port_fdb_del, - .port_mdb_add = ksz9477_port_mdb_add, - .port_mdb_del = ksz9477_port_mdb_del, - .port_mirror_add = ksz9477_port_mirror_add, - .port_mirror_del = ksz9477_port_mirror_del, -}; - -static u32 ksz9477_get_port_addr(int port, int offset) +u32 ksz9477_get_port_addr(int port, int offset) { return PORT_CTRL_ADDR(port, offset); } -static int ksz9477_switch_detect(struct ksz_device *dev) +int ksz9477_switch_init(struct ksz_device *dev) { u8 data8; - u8 id_hi; - u8 id_lo; - u32 id32; int ret; + dev->port_mask = (1 << dev->info->port_cnt) - 1; + /* turn off SPI DO Edge select */ ret = ksz_read8(dev, REG_SW_GLOBAL_SERIAL_CTRL_0, &data8); if (ret) @@ -1387,204 +1189,14 @@ static int ksz9477_switch_detect(struct ksz_device *dev) if (ret) return ret; - /* read chip id */ - ret = ksz_read32(dev, REG_CHIP_ID0__1, &id32); - if (ret) - return ret; - ret = ksz_read8(dev, REG_GLOBAL_OPTIONS, &data8); - if (ret) - return ret; - - /* Number of ports can be reduced depending on chip. */ - dev->phy_port_cnt = 5; - - /* Default capability is gigabit capable. */ - dev->features = GBIT_SUPPORT; - - dev_dbg(dev->dev, "Switch detect: ID=%08x%02x\n", id32, data8); - id_hi = (u8)(id32 >> 16); - id_lo = (u8)(id32 >> 8); - if ((id_lo & 0xf) == 3) { - /* Chip is from KSZ9893 design. */ - dev_info(dev->dev, "Found KSZ9893\n"); - dev->features |= IS_9893; - - /* Chip does not support gigabit. */ - if (data8 & SW_QW_ABLE) - dev->features &= ~GBIT_SUPPORT; - dev->phy_port_cnt = 2; - } else { - dev_info(dev->dev, "Found KSZ9477 or compatible\n"); - /* Chip uses new XMII register definitions. */ - dev->features |= NEW_XMII; - - /* Chip does not support gigabit. */ - if (!(data8 & SW_GIGABIT_ABLE)) - dev->features &= ~GBIT_SUPPORT; - } - - /* Change chip id to known ones so it can be matched against them. */ - id32 = (id_hi << 16) | (id_lo << 8); - - dev->chip_id = id32; - - return 0; -} - -struct ksz_chip_data { - u32 chip_id; - const char *dev_name; - int num_vlans; - int num_alus; - int num_statics; - int cpu_ports; - int port_cnt; - bool phy_errata_9477; -}; - -static const struct ksz_chip_data ksz9477_switch_chips[] = { - { - .chip_id = 0x00947700, - .dev_name = "KSZ9477", - .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 */ - .phy_errata_9477 = true, - }, - { - .chip_id = 0x00989700, - .dev_name = "KSZ9897", - .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 */ - .phy_errata_9477 = true, - }, - { - .chip_id = 0x00989300, - .dev_name = "KSZ9893", - .num_vlans = 4096, - .num_alus = 4096, - .num_statics = 16, - .cpu_ports = 0x07, /* can be configured as cpu port */ - .port_cnt = 3, /* total port count */ - }, - { - .chip_id = 0x00956700, - .dev_name = "KSZ9567", - .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 */ - .phy_errata_9477 = true, - }, -}; - -static int ksz9477_switch_init(struct ksz_device *dev) -{ - int i; - - dev->ds->ops = &ksz9477_switch_ops; - - for (i = 0; i < ARRAY_SIZE(ksz9477_switch_chips); i++) { - const struct ksz_chip_data *chip = &ksz9477_switch_chips[i]; - - if (dev->chip_id == chip->chip_id) { - dev->name = chip->dev_name; - dev->num_vlans = chip->num_vlans; - dev->num_alus = chip->num_alus; - dev->num_statics = chip->num_statics; - dev->port_cnt = chip->port_cnt; - dev->cpu_ports = chip->cpu_ports; - dev->phy_errata_9477 = chip->phy_errata_9477; - - break; - } - } - - /* no switch found */ - if (!dev->port_cnt) - return -ENODEV; - - dev->port_mask = (1 << dev->port_cnt) - 1; - - dev->reg_mib_cnt = SWITCH_COUNTER_NUM; - dev->mib_cnt = TOTAL_SWITCH_COUNTER_NUM; - - dev->ports = devm_kzalloc(dev->dev, - dev->port_cnt * sizeof(struct ksz_port), - GFP_KERNEL); - if (!dev->ports) - return -ENOMEM; - for (i = 0; i < dev->port_cnt; i++) { - mutex_init(&dev->ports[i].mib.cnt_mutex); - dev->ports[i].mib.counters = - devm_kzalloc(dev->dev, - sizeof(u64) * - (TOTAL_SWITCH_COUNTER_NUM + 1), - GFP_KERNEL); - if (!dev->ports[i].mib.counters) - return -ENOMEM; - } - - /* set the real number of ports */ - dev->ds->num_ports = dev->port_cnt; - return 0; } -static void ksz9477_switch_exit(struct ksz_device *dev) +void ksz9477_switch_exit(struct ksz_device *dev) { ksz9477_reset_switch(dev); } -static const struct ksz_dev_ops ksz9477_dev_ops = { - .get_port_addr = ksz9477_get_port_addr, - .cfg_port_member = ksz9477_cfg_port_member, - .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, - .port_setup = ksz9477_port_setup, - .r_mib_cnt = ksz9477_r_mib_cnt, - .r_mib_pkt = ksz9477_r_mib_pkt, - .freeze_mib = ksz9477_freeze_mib, - .port_init_cnt = ksz9477_port_init_cnt, - .shutdown = ksz9477_reset_switch, - .detect = ksz9477_switch_detect, - .init = ksz9477_switch_init, - .exit = ksz9477_switch_exit, -}; - -int ksz9477_switch_register(struct ksz_device *dev) -{ - int ret, i; - struct phy_device *phydev; - - ret = ksz_switch_register(dev, &ksz9477_dev_ops); - if (ret) - return ret; - - for (i = 0; i < dev->phy_port_cnt; ++i) { - if (!dsa_is_user_port(dev->ds, i)) - continue; - - phydev = dsa_to_port(dev->ds, i)->slave->phydev; - - /* The MAC actually cannot run in 1000 half-duplex mode. */ - phy_remove_link_mode(phydev, - ETHTOOL_LINK_MODE_1000baseT_Half_BIT); - - /* PHY does not support gigabit. */ - if (!(dev->features & GBIT_SUPPORT)) - phy_remove_link_mode(phydev, - ETHTOOL_LINK_MODE_1000baseT_Full_BIT); - } - return ret; -} -EXPORT_SYMBOL(ksz9477_switch_register); - MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>"); MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch DSA Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz9477.h b/drivers/net/dsa/microchip/ksz9477.h new file mode 100644 index 000000000000..00862c4cfb7f --- /dev/null +++ b/drivers/net/dsa/microchip/ksz9477.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Microchip KSZ9477 series Header file + * + * Copyright (C) 2017-2022 Microchip Technology Inc. + */ + +#ifndef __KSZ9477_H +#define __KSZ9477_H + +#include <net/dsa.h> +#include "ksz_common.h" + +int ksz9477_setup(struct dsa_switch *ds); +u32 ksz9477_get_port_addr(int port, int offset); +void ksz9477_cfg_port_member(struct ksz_device *dev, int port, u8 member); +void ksz9477_flush_dyn_mac_table(struct ksz_device *dev, int port); +void ksz9477_port_setup(struct ksz_device *dev, int port, bool cpu_port); +int ksz9477_set_ageing_time(struct ksz_device *dev, unsigned int msecs); +int ksz9477_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data); +int ksz9477_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); +void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr, u64 *cnt); +void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr, + u64 *dropped, u64 *cnt); +void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze); +void ksz9477_port_init_cnt(struct ksz_device *dev, int port); +int ksz9477_port_vlan_filtering(struct ksz_device *dev, int port, + bool flag, struct netlink_ext_ack *extack); +int ksz9477_port_vlan_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); +int ksz9477_port_vlan_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan); +int ksz9477_port_mirror_add(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack); +void ksz9477_port_mirror_del(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror); +int ksz9477_get_stp_reg(void); +void ksz9477_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config); +int ksz9477_fdb_dump(struct ksz_device *dev, int port, + dsa_fdb_dump_cb_t *cb, void *data); +int ksz9477_fdb_add(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db); +int ksz9477_fdb_del(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db); +int ksz9477_mdb_add(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); +int ksz9477_mdb_del(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, struct dsa_db db); +int ksz9477_change_mtu(struct ksz_device *dev, int port, int mtu); +int ksz9477_max_mtu(struct ksz_device *dev, int port); +void ksz9477_config_cpu_port(struct dsa_switch *ds); +int ksz9477_enable_stp_addr(struct ksz_device *dev); +int ksz9477_reset_switch(struct ksz_device *dev); +int ksz9477_dsa_init(struct ksz_device *dev); +int ksz9477_switch_init(struct ksz_device *dev); +void ksz9477_switch_exit(struct ksz_device *dev); + +#endif diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c b/drivers/net/dsa/microchip/ksz9477_i2c.c index f3afb8b8c4cc..3763930dc6fc 100644 --- a/drivers/net/dsa/microchip/ksz9477_i2c.c +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c @@ -41,7 +41,7 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c, if (i2c->dev.platform_data) dev->pdata = i2c->dev.platform_data; - ret = ksz9477_switch_register(dev); + ret = ksz_switch_register(dev); /* Main DSA driver may not be started yet. */ if (ret) @@ -52,16 +52,12 @@ static int ksz9477_i2c_probe(struct i2c_client *i2c, return 0; } -static int ksz9477_i2c_remove(struct i2c_client *i2c) +static void ksz9477_i2c_remove(struct i2c_client *i2c) { struct ksz_device *dev = i2c_get_clientdata(i2c); if (dev) ksz_switch_remove(dev); - - i2c_set_clientdata(i2c, NULL); - - return 0; } static void ksz9477_i2c_shutdown(struct i2c_client *i2c) @@ -71,8 +67,8 @@ static void ksz9477_i2c_shutdown(struct i2c_client *i2c) if (!dev) return; - if (dev->dev_ops->shutdown) - dev->dev_ops->shutdown(dev); + if (dev->dev_ops->reset) + dev->dev_ops->reset(dev); dsa_switch_shutdown(dev->ds); @@ -87,11 +83,34 @@ static const struct i2c_device_id ksz9477_i2c_id[] = { MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); static const struct of_device_id ksz9477_dt_ids[] = { - { .compatible = "microchip,ksz9477" }, - { .compatible = "microchip,ksz9897" }, - { .compatible = "microchip,ksz9893" }, - { .compatible = "microchip,ksz9563" }, - { .compatible = "microchip,ksz9567" }, + { + .compatible = "microchip,ksz9477", + .data = &ksz_switch_chips[KSZ9477] + }, + { + .compatible = "microchip,ksz9896", + .data = &ksz_switch_chips[KSZ9896] + }, + { + .compatible = "microchip,ksz9897", + .data = &ksz_switch_chips[KSZ9897] + }, + { + .compatible = "microchip,ksz9893", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz8563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9567", + .data = &ksz_switch_chips[KSZ9567] + }, {}, }; MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); diff --git a/drivers/net/dsa/microchip/ksz9477_reg.h b/drivers/net/dsa/microchip/ksz9477_reg.h index 16939f29faa5..53c68d286dd3 100644 --- a/drivers/net/dsa/microchip/ksz9477_reg.h +++ b/drivers/net/dsa/microchip/ksz9477_reg.h @@ -25,7 +25,6 @@ #define REG_CHIP_ID2__1 0x0002 -#define CHIP_ID_63 0x63 #define CHIP_ID_66 0x66 #define CHIP_ID_67 0x67 #define CHIP_ID_77 0x77 @@ -166,7 +165,6 @@ #define SW_DOUBLE_TAG BIT(7) #define SW_RESET BIT(1) -#define SW_START BIT(0) #define REG_SW_MAC_ADDR_0 0x0302 #define REG_SW_MAC_ADDR_1 0x0303 @@ -176,6 +174,7 @@ #define REG_SW_MAC_ADDR_5 0x0307 #define REG_SW_MTU__2 0x0308 +#define REG_SW_MTU_MASK GENMASK(13, 0) #define REG_SW_ISP_TPID__2 0x030A @@ -190,8 +189,9 @@ #define SW_VLAN_ENABLE BIT(7) #define SW_DROP_INVALID_VID BIT(6) -#define SW_AGE_CNT_M 0x7 +#define SW_AGE_CNT_M GENMASK(5, 3) #define SW_AGE_CNT_S 3 +#define SW_AGE_PERIOD_10_8_M GENMASK(10, 8) #define SW_RESV_MCAST_ENABLE BIT(2) #define SW_HASH_OPTION_M 0x03 #define SW_HASH_OPTION_CRC 1 @@ -226,6 +226,7 @@ #define SW_PRIO_LOWEST_DA_SA 3 #define REG_SW_LUE_CTRL_3 0x0313 +#define SW_AGE_PERIOD_7_0_M GENMASK(7, 0) #define REG_SW_LUE_INT_STATUS 0x0314 #define REG_SW_LUE_INT_ENABLE 0x0315 @@ -265,7 +266,6 @@ #define REG_SW_MAC_CTRL_1 0x0331 -#define MULTICAST_STORM_DISABLE BIT(6) #define SW_BACK_PRESSURE BIT(5) #define FAIR_FLOW_CTRL BIT(4) #define NO_EXC_COLLISION_DROP BIT(3) @@ -276,13 +276,9 @@ #define REG_SW_MAC_CTRL_2 0x0332 #define SW_REPLACE_VID BIT(3) -#define BROADCAST_STORM_RATE_HI 0x07 #define REG_SW_MAC_CTRL_3 0x0333 -#define BROADCAST_STORM_RATE_LO 0xFF -#define BROADCAST_STORM_RATE 0x07FF - #define REG_SW_MAC_CTRL_4 0x0334 #define SW_PASS_PAUSE BIT(3) @@ -425,12 +421,9 @@ #define REG_SW_ALU_STAT_CTRL__4 0x041C -#define ALU_STAT_INDEX_M (BIT(4) - 1) -#define ALU_STAT_INDEX_S 16 #define ALU_RESV_MCAST_INDEX_M (BIT(6) - 1) #define ALU_STAT_START BIT(7) #define ALU_RESV_MCAST_ADDR BIT(1) -#define ALU_STAT_READ BIT(0) #define REG_SW_ALU_VAL_A 0x0420 @@ -1184,35 +1177,11 @@ #define PORT_LINK_STATUS_FAIL BIT(0) /* 3 - xMII */ -#define REG_PORT_XMII_CTRL_0 0x0300 - #define PORT_SGMII_SEL BIT(7) -#define PORT_MII_FULL_DUPLEX BIT(6) -#define PORT_MII_100MBIT BIT(4) #define PORT_GRXC_ENABLE BIT(0) -#define REG_PORT_XMII_CTRL_1 0x0301 - #define PORT_RMII_CLK_SEL BIT(7) -/* S1 */ -#define PORT_MII_1000MBIT_S1 BIT(6) -/* S2 */ -#define PORT_MII_NOT_1GBIT BIT(6) #define PORT_MII_SEL_EDGE BIT(5) -#define PORT_RGMII_ID_IG_ENABLE BIT(4) -#define PORT_RGMII_ID_EG_ENABLE BIT(3) -#define PORT_MII_MAC_MODE BIT(2) -#define PORT_MII_SEL_M 0x3 -/* S1 */ -#define PORT_MII_SEL_S1 0x0 -#define PORT_RMII_SEL_S1 0x1 -#define PORT_GMII_SEL_S1 0x2 -#define PORT_RGMII_SEL_S1 0x3 -/* S2 */ -#define PORT_RGMII_SEL 0x0 -#define PORT_RMII_SEL 0x1 -#define PORT_GMII_SEL 0x2 -#define PORT_MII_SEL 0x3 /* 4 - MAC */ #define REG_PORT_MAC_CTRL_0 0x0400 @@ -1268,8 +1237,6 @@ /* 5 - MIB Counters */ #define REG_PORT_MIB_CTRL_STAT__4 0x0500 -#define MIB_COUNTER_OVERFLOW BIT(31) -#define MIB_COUNTER_VALID BIT(30) #define MIB_COUNTER_READ BIT(25) #define MIB_COUNTER_FLUSH_FREEZE BIT(24) #define MIB_COUNTER_INDEX_M (BIT(8) - 1) @@ -1585,10 +1552,6 @@ #define REG_PORT_LUE_MSTP_STATE 0x0B04 -#define PORT_TX_ENABLE BIT(2) -#define PORT_RX_ENABLE BIT(1) -#define PORT_LEARN_DISABLE BIT(0) - /* C - PTP */ #define REG_PTP_PORT_RX_DELAY__2 0x0C00 @@ -1632,11 +1595,7 @@ #define P_BCAST_STORM_CTRL REG_PORT_MAC_CTRL_0 #define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL #define P_MIRROR_CTRL REG_PORT_MRI_MIRROR_CTRL -#define P_STP_CTRL REG_PORT_LUE_MSTP_STATE #define P_PHY_CTRL REG_PORT_PHY_CTRL -#define P_NEG_RESTART_CTRL REG_PORT_PHY_CTRL -#define P_LINK_STATUS REG_PORT_PHY_STATUS -#define P_SPEED_STATUS REG_PORT_PHY_PHY_CTRL #define P_RATE_LIMIT_CTRL REG_PORT_MAC_IN_RATE_LIMIT #define S_LINK_AGING_CTRL REG_SW_LUE_CTRL_1 @@ -1656,10 +1615,6 @@ #define PTP_TRIG_UNIT_M (BIT(MAX_TRIG_UNIT) - 1) #define PTP_TS_UNIT_M (BIT(MAX_TIMESTAMP_UNIT) - 1) -/* Driver set switch broadcast storm protection at 10% rate. */ -#define BROADCAST_STORM_PROT_RATE 10 - -/* 148,800 frames * 67 ms / 100 */ -#define BROADCAST_STORM_VALUE 9969 +#define KSZ9477_MAX_FRAME_SIZE 9000 #endif /* KSZ9477_REGS_H */ diff --git a/drivers/net/dsa/microchip/ksz9477_spi.c b/drivers/net/dsa/microchip/ksz9477_spi.c deleted file mode 100644 index e3cb0e6c9f6f..000000000000 --- a/drivers/net/dsa/microchip/ksz9477_spi.c +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Microchip KSZ9477 series register access through SPI - * - * Copyright (C) 2017-2019 Microchip Technology Inc. - */ - -#include <asm/unaligned.h> - -#include <linux/delay.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/regmap.h> -#include <linux/spi/spi.h> - -#include "ksz_common.h" - -#define SPI_ADDR_SHIFT 24 -#define SPI_ADDR_ALIGN 3 -#define SPI_TURNAROUND_SHIFT 5 - -KSZ_REGMAP_TABLE(ksz9477, 32, SPI_ADDR_SHIFT, - SPI_TURNAROUND_SHIFT, SPI_ADDR_ALIGN); - -static int ksz9477_spi_probe(struct spi_device *spi) -{ - struct regmap_config rc; - struct ksz_device *dev; - int i, ret; - - dev = ksz_switch_alloc(&spi->dev, spi); - if (!dev) - return -ENOMEM; - - for (i = 0; i < ARRAY_SIZE(ksz9477_regmap_config); i++) { - rc = ksz9477_regmap_config[i]; - rc.lock_arg = &dev->regmap_mutex; - dev->regmap[i] = devm_regmap_init_spi(spi, &rc); - if (IS_ERR(dev->regmap[i])) { - ret = PTR_ERR(dev->regmap[i]); - dev_err(&spi->dev, - "Failed to initialize regmap%i: %d\n", - ksz9477_regmap_config[i].val_bits, ret); - return ret; - } - } - - if (spi->dev.platform_data) - dev->pdata = spi->dev.platform_data; - - /* setup spi */ - spi->mode = SPI_MODE_3; - ret = spi_setup(spi); - if (ret) - return ret; - - ret = ksz9477_switch_register(dev); - - /* Main DSA driver may not be started yet. */ - if (ret) - return ret; - - spi_set_drvdata(spi, dev); - - return 0; -} - -static int ksz9477_spi_remove(struct spi_device *spi) -{ - struct ksz_device *dev = spi_get_drvdata(spi); - - if (dev) - ksz_switch_remove(dev); - - spi_set_drvdata(spi, NULL); - - return 0; -} - -static void ksz9477_spi_shutdown(struct spi_device *spi) -{ - struct ksz_device *dev = spi_get_drvdata(spi); - - if (dev) - dsa_switch_shutdown(dev->ds); - - spi_set_drvdata(spi, NULL); -} - -static const struct of_device_id ksz9477_dt_ids[] = { - { .compatible = "microchip,ksz9477" }, - { .compatible = "microchip,ksz9897" }, - { .compatible = "microchip,ksz9893" }, - { .compatible = "microchip,ksz9563" }, - { .compatible = "microchip,ksz8563" }, - { .compatible = "microchip,ksz9567" }, - {}, -}; -MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); - -static struct spi_driver ksz9477_spi_driver = { - .driver = { - .name = "ksz9477-switch", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(ksz9477_dt_ids), - }, - .probe = ksz9477_spi_probe, - .remove = ksz9477_spi_remove, - .shutdown = ksz9477_spi_shutdown, -}; - -module_spi_driver(ksz9477_spi_driver); - -MODULE_ALIAS("spi:ksz9477"); -MODULE_ALIAS("spi:ksz9897"); -MODULE_ALIAS("spi:ksz9893"); -MODULE_ALIAS("spi:ksz9563"); -MODULE_ALIAS("spi:ksz8563"); -MODULE_ALIAS("spi:ksz9567"); -MODULE_AUTHOR("Woojung Huh <Woojung.Huh@microchip.com>"); -MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch SPI access Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 55dbda04ea62..d612181b3226 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -14,19 +14,1597 @@ #include <linux/phy.h> #include <linux/etherdevice.h> #include <linux/if_bridge.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/of_mdio.h> +#include <linux/of_device.h> #include <linux/of_net.h> +#include <linux/micrel_phy.h> #include <net/dsa.h> #include <net/switchdev.h> #include "ksz_common.h" +#include "ksz8.h" +#include "ksz9477.h" +#include "lan937x.h" + +#define MIB_COUNTER_NUM 0x20 + +struct ksz_stats_raw { + u64 rx_hi; + u64 rx_undersize; + u64 rx_fragments; + u64 rx_oversize; + u64 rx_jabbers; + u64 rx_symbol_err; + u64 rx_crc_err; + u64 rx_align_err; + u64 rx_mac_ctrl; + u64 rx_pause; + u64 rx_bcast; + u64 rx_mcast; + u64 rx_ucast; + u64 rx_64_or_less; + u64 rx_65_127; + u64 rx_128_255; + u64 rx_256_511; + u64 rx_512_1023; + u64 rx_1024_1522; + u64 rx_1523_2000; + u64 rx_2001; + u64 tx_hi; + u64 tx_late_col; + u64 tx_pause; + u64 tx_bcast; + u64 tx_mcast; + u64 tx_ucast; + u64 tx_deferred; + u64 tx_total_col; + u64 tx_exc_col; + u64 tx_single_col; + u64 tx_mult_col; + u64 rx_total; + u64 tx_total; + u64 rx_discards; + u64 tx_discards; +}; + +static const struct ksz_mib_names ksz88xx_mib_names[] = { + { 0x00, "rx" }, + { 0x01, "rx_hi" }, + { 0x02, "rx_undersize" }, + { 0x03, "rx_fragments" }, + { 0x04, "rx_oversize" }, + { 0x05, "rx_jabbers" }, + { 0x06, "rx_symbol_err" }, + { 0x07, "rx_crc_err" }, + { 0x08, "rx_align_err" }, + { 0x09, "rx_mac_ctrl" }, + { 0x0a, "rx_pause" }, + { 0x0b, "rx_bcast" }, + { 0x0c, "rx_mcast" }, + { 0x0d, "rx_ucast" }, + { 0x0e, "rx_64_or_less" }, + { 0x0f, "rx_65_127" }, + { 0x10, "rx_128_255" }, + { 0x11, "rx_256_511" }, + { 0x12, "rx_512_1023" }, + { 0x13, "rx_1024_1522" }, + { 0x14, "tx" }, + { 0x15, "tx_hi" }, + { 0x16, "tx_late_col" }, + { 0x17, "tx_pause" }, + { 0x18, "tx_bcast" }, + { 0x19, "tx_mcast" }, + { 0x1a, "tx_ucast" }, + { 0x1b, "tx_deferred" }, + { 0x1c, "tx_total_col" }, + { 0x1d, "tx_exc_col" }, + { 0x1e, "tx_single_col" }, + { 0x1f, "tx_mult_col" }, + { 0x100, "rx_discards" }, + { 0x101, "tx_discards" }, +}; + +static const struct ksz_mib_names ksz9477_mib_names[] = { + { 0x00, "rx_hi" }, + { 0x01, "rx_undersize" }, + { 0x02, "rx_fragments" }, + { 0x03, "rx_oversize" }, + { 0x04, "rx_jabbers" }, + { 0x05, "rx_symbol_err" }, + { 0x06, "rx_crc_err" }, + { 0x07, "rx_align_err" }, + { 0x08, "rx_mac_ctrl" }, + { 0x09, "rx_pause" }, + { 0x0A, "rx_bcast" }, + { 0x0B, "rx_mcast" }, + { 0x0C, "rx_ucast" }, + { 0x0D, "rx_64_or_less" }, + { 0x0E, "rx_65_127" }, + { 0x0F, "rx_128_255" }, + { 0x10, "rx_256_511" }, + { 0x11, "rx_512_1023" }, + { 0x12, "rx_1024_1522" }, + { 0x13, "rx_1523_2000" }, + { 0x14, "rx_2001" }, + { 0x15, "tx_hi" }, + { 0x16, "tx_late_col" }, + { 0x17, "tx_pause" }, + { 0x18, "tx_bcast" }, + { 0x19, "tx_mcast" }, + { 0x1A, "tx_ucast" }, + { 0x1B, "tx_deferred" }, + { 0x1C, "tx_total_col" }, + { 0x1D, "tx_exc_col" }, + { 0x1E, "tx_single_col" }, + { 0x1F, "tx_mult_col" }, + { 0x80, "rx_total" }, + { 0x81, "tx_total" }, + { 0x82, "rx_discards" }, + { 0x83, "tx_discards" }, +}; + +static const struct ksz_dev_ops ksz8_dev_ops = { + .setup = ksz8_setup, + .get_port_addr = ksz8_get_port_addr, + .cfg_port_member = ksz8_cfg_port_member, + .flush_dyn_mac_table = ksz8_flush_dyn_mac_table, + .port_setup = ksz8_port_setup, + .r_phy = ksz8_r_phy, + .w_phy = ksz8_w_phy, + .r_mib_cnt = ksz8_r_mib_cnt, + .r_mib_pkt = ksz8_r_mib_pkt, + .freeze_mib = ksz8_freeze_mib, + .port_init_cnt = ksz8_port_init_cnt, + .fdb_dump = ksz8_fdb_dump, + .mdb_add = ksz8_mdb_add, + .mdb_del = ksz8_mdb_del, + .vlan_filtering = ksz8_port_vlan_filtering, + .vlan_add = ksz8_port_vlan_add, + .vlan_del = ksz8_port_vlan_del, + .mirror_add = ksz8_port_mirror_add, + .mirror_del = ksz8_port_mirror_del, + .get_caps = ksz8_get_caps, + .config_cpu_port = ksz8_config_cpu_port, + .enable_stp_addr = ksz8_enable_stp_addr, + .reset = ksz8_reset_switch, + .init = ksz8_switch_init, + .exit = ksz8_switch_exit, +}; + +static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, + bool rx_pause); + +static const struct ksz_dev_ops ksz9477_dev_ops = { + .setup = ksz9477_setup, + .get_port_addr = ksz9477_get_port_addr, + .cfg_port_member = ksz9477_cfg_port_member, + .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, + .port_setup = ksz9477_port_setup, + .set_ageing_time = ksz9477_set_ageing_time, + .r_phy = ksz9477_r_phy, + .w_phy = ksz9477_w_phy, + .r_mib_cnt = ksz9477_r_mib_cnt, + .r_mib_pkt = ksz9477_r_mib_pkt, + .r_mib_stat64 = ksz_r_mib_stats64, + .freeze_mib = ksz9477_freeze_mib, + .port_init_cnt = ksz9477_port_init_cnt, + .vlan_filtering = ksz9477_port_vlan_filtering, + .vlan_add = ksz9477_port_vlan_add, + .vlan_del = ksz9477_port_vlan_del, + .mirror_add = ksz9477_port_mirror_add, + .mirror_del = ksz9477_port_mirror_del, + .get_caps = ksz9477_get_caps, + .fdb_dump = ksz9477_fdb_dump, + .fdb_add = ksz9477_fdb_add, + .fdb_del = ksz9477_fdb_del, + .mdb_add = ksz9477_mdb_add, + .mdb_del = ksz9477_mdb_del, + .change_mtu = ksz9477_change_mtu, + .max_mtu = ksz9477_max_mtu, + .phylink_mac_link_up = ksz9477_phylink_mac_link_up, + .config_cpu_port = ksz9477_config_cpu_port, + .enable_stp_addr = ksz9477_enable_stp_addr, + .reset = ksz9477_reset_switch, + .init = ksz9477_switch_init, + .exit = ksz9477_switch_exit, +}; + +static const struct ksz_dev_ops lan937x_dev_ops = { + .setup = lan937x_setup, + .teardown = lan937x_teardown, + .get_port_addr = ksz9477_get_port_addr, + .cfg_port_member = ksz9477_cfg_port_member, + .flush_dyn_mac_table = ksz9477_flush_dyn_mac_table, + .port_setup = lan937x_port_setup, + .set_ageing_time = lan937x_set_ageing_time, + .r_phy = lan937x_r_phy, + .w_phy = lan937x_w_phy, + .r_mib_cnt = ksz9477_r_mib_cnt, + .r_mib_pkt = ksz9477_r_mib_pkt, + .r_mib_stat64 = ksz_r_mib_stats64, + .freeze_mib = ksz9477_freeze_mib, + .port_init_cnt = ksz9477_port_init_cnt, + .vlan_filtering = ksz9477_port_vlan_filtering, + .vlan_add = ksz9477_port_vlan_add, + .vlan_del = ksz9477_port_vlan_del, + .mirror_add = ksz9477_port_mirror_add, + .mirror_del = ksz9477_port_mirror_del, + .get_caps = lan937x_phylink_get_caps, + .setup_rgmii_delay = lan937x_setup_rgmii_delay, + .fdb_dump = ksz9477_fdb_dump, + .fdb_add = ksz9477_fdb_add, + .fdb_del = ksz9477_fdb_del, + .mdb_add = ksz9477_mdb_add, + .mdb_del = ksz9477_mdb_del, + .change_mtu = lan937x_change_mtu, + .max_mtu = ksz9477_max_mtu, + .phylink_mac_link_up = ksz9477_phylink_mac_link_up, + .config_cpu_port = lan937x_config_cpu_port, + .enable_stp_addr = ksz9477_enable_stp_addr, + .reset = lan937x_reset_switch, + .init = lan937x_switch_init, + .exit = lan937x_switch_exit, +}; + +static const u16 ksz8795_regs[] = { + [REG_IND_CTRL_0] = 0x6E, + [REG_IND_DATA_8] = 0x70, + [REG_IND_DATA_CHECK] = 0x72, + [REG_IND_DATA_HI] = 0x71, + [REG_IND_DATA_LO] = 0x75, + [REG_IND_MIB_CHECK] = 0x74, + [REG_IND_BYTE] = 0xA0, + [P_FORCE_CTRL] = 0x0C, + [P_LINK_STATUS] = 0x0E, + [P_LOCAL_CTRL] = 0x07, + [P_NEG_RESTART_CTRL] = 0x0D, + [P_REMOTE_STATUS] = 0x08, + [P_SPEED_STATUS] = 0x09, + [S_TAIL_TAG_CTRL] = 0x0C, + [P_STP_CTRL] = 0x02, + [S_START_CTRL] = 0x01, + [S_BROADCAST_CTRL] = 0x06, + [S_MULTICAST_CTRL] = 0x04, + [P_XMII_CTRL_0] = 0x06, + [P_XMII_CTRL_1] = 0x56, +}; + +static const u32 ksz8795_masks[] = { + [PORT_802_1P_REMAPPING] = BIT(7), + [SW_TAIL_TAG_ENABLE] = BIT(1), + [MIB_COUNTER_OVERFLOW] = BIT(6), + [MIB_COUNTER_VALID] = BIT(5), + [VLAN_TABLE_FID] = GENMASK(6, 0), + [VLAN_TABLE_MEMBERSHIP] = GENMASK(11, 7), + [VLAN_TABLE_VALID] = BIT(12), + [STATIC_MAC_TABLE_VALID] = BIT(21), + [STATIC_MAC_TABLE_USE_FID] = BIT(23), + [STATIC_MAC_TABLE_FID] = GENMASK(30, 24), + [STATIC_MAC_TABLE_OVERRIDE] = BIT(26), + [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(24, 20), + [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(6, 0), + [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(8), + [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), + [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 29), + [DYNAMIC_MAC_TABLE_FID] = GENMASK(26, 20), + [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(26, 24), + [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(28, 27), + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(5), +}; + +static const u8 ksz8795_xmii_ctrl0[] = { + [P_MII_100MBIT] = 0, + [P_MII_10MBIT] = 1, + [P_MII_FULL_DUPLEX] = 0, + [P_MII_HALF_DUPLEX] = 1, +}; + +static const u8 ksz8795_xmii_ctrl1[] = { + [P_RGMII_SEL] = 3, + [P_GMII_SEL] = 2, + [P_RMII_SEL] = 1, + [P_MII_SEL] = 0, + [P_GMII_1GBIT] = 1, + [P_GMII_NOT_1GBIT] = 0, +}; + +static const u8 ksz8795_shifts[] = { + [VLAN_TABLE_MEMBERSHIP_S] = 7, + [VLAN_TABLE] = 16, + [STATIC_MAC_FWD_PORTS] = 16, + [STATIC_MAC_FID] = 24, + [DYNAMIC_MAC_ENTRIES_H] = 3, + [DYNAMIC_MAC_ENTRIES] = 29, + [DYNAMIC_MAC_FID] = 16, + [DYNAMIC_MAC_TIMESTAMP] = 27, + [DYNAMIC_MAC_SRC_PORT] = 24, +}; + +static const u16 ksz8863_regs[] = { + [REG_IND_CTRL_0] = 0x79, + [REG_IND_DATA_8] = 0x7B, + [REG_IND_DATA_CHECK] = 0x7B, + [REG_IND_DATA_HI] = 0x7C, + [REG_IND_DATA_LO] = 0x80, + [REG_IND_MIB_CHECK] = 0x80, + [P_FORCE_CTRL] = 0x0C, + [P_LINK_STATUS] = 0x0E, + [P_LOCAL_CTRL] = 0x0C, + [P_NEG_RESTART_CTRL] = 0x0D, + [P_REMOTE_STATUS] = 0x0E, + [P_SPEED_STATUS] = 0x0F, + [S_TAIL_TAG_CTRL] = 0x03, + [P_STP_CTRL] = 0x02, + [S_START_CTRL] = 0x01, + [S_BROADCAST_CTRL] = 0x06, + [S_MULTICAST_CTRL] = 0x04, +}; + +static const u32 ksz8863_masks[] = { + [PORT_802_1P_REMAPPING] = BIT(3), + [SW_TAIL_TAG_ENABLE] = BIT(6), + [MIB_COUNTER_OVERFLOW] = BIT(7), + [MIB_COUNTER_VALID] = BIT(6), + [VLAN_TABLE_FID] = GENMASK(15, 12), + [VLAN_TABLE_MEMBERSHIP] = GENMASK(18, 16), + [VLAN_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_VALID] = BIT(19), + [STATIC_MAC_TABLE_USE_FID] = BIT(21), + [STATIC_MAC_TABLE_FID] = GENMASK(29, 26), + [STATIC_MAC_TABLE_OVERRIDE] = BIT(20), + [STATIC_MAC_TABLE_FWD_PORTS] = GENMASK(18, 16), + [DYNAMIC_MAC_TABLE_ENTRIES_H] = GENMASK(5, 0), + [DYNAMIC_MAC_TABLE_MAC_EMPTY] = BIT(7), + [DYNAMIC_MAC_TABLE_NOT_READY] = BIT(7), + [DYNAMIC_MAC_TABLE_ENTRIES] = GENMASK(31, 28), + [DYNAMIC_MAC_TABLE_FID] = GENMASK(19, 16), + [DYNAMIC_MAC_TABLE_SRC_PORT] = GENMASK(21, 20), + [DYNAMIC_MAC_TABLE_TIMESTAMP] = GENMASK(23, 22), +}; + +static u8 ksz8863_shifts[] = { + [VLAN_TABLE_MEMBERSHIP_S] = 16, + [STATIC_MAC_FWD_PORTS] = 16, + [STATIC_MAC_FID] = 22, + [DYNAMIC_MAC_ENTRIES_H] = 3, + [DYNAMIC_MAC_ENTRIES] = 24, + [DYNAMIC_MAC_FID] = 16, + [DYNAMIC_MAC_TIMESTAMP] = 24, + [DYNAMIC_MAC_SRC_PORT] = 20, +}; + +static const u16 ksz9477_regs[] = { + [P_STP_CTRL] = 0x0B04, + [S_START_CTRL] = 0x0300, + [S_BROADCAST_CTRL] = 0x0332, + [S_MULTICAST_CTRL] = 0x0331, + [P_XMII_CTRL_0] = 0x0300, + [P_XMII_CTRL_1] = 0x0301, +}; + +static const u32 ksz9477_masks[] = { + [ALU_STAT_WRITE] = 0, + [ALU_STAT_READ] = 1, + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(3), +}; + +static const u8 ksz9477_shifts[] = { + [ALU_STAT_INDEX] = 16, +}; + +static const u8 ksz9477_xmii_ctrl0[] = { + [P_MII_100MBIT] = 1, + [P_MII_10MBIT] = 0, + [P_MII_FULL_DUPLEX] = 1, + [P_MII_HALF_DUPLEX] = 0, +}; + +static const u8 ksz9477_xmii_ctrl1[] = { + [P_RGMII_SEL] = 0, + [P_RMII_SEL] = 1, + [P_GMII_SEL] = 2, + [P_MII_SEL] = 3, + [P_GMII_1GBIT] = 0, + [P_GMII_NOT_1GBIT] = 1, +}; + +static const u32 lan937x_masks[] = { + [ALU_STAT_WRITE] = 1, + [ALU_STAT_READ] = 2, + [P_MII_TX_FLOW_CTRL] = BIT(5), + [P_MII_RX_FLOW_CTRL] = BIT(3), +}; + +static const u8 lan937x_shifts[] = { + [ALU_STAT_INDEX] = 8, +}; + +static const struct regmap_range ksz8563_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x000f, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0104, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x012b), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + regmap_reg_range(0x0500, 0x0519), + regmap_reg_range(0x0520, 0x054b), + regmap_reg_range(0x0550, 0x05b3), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1004, 0x100b), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1021), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1111), + regmap_reg_range(0x111a, 0x111d), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1612), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x191b), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a08), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + regmap_reg_range(0x1c00, 0x1c05), + regmap_reg_range(0x1c08, 0x1c1b), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2004, 0x200b), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2021), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2111), + regmap_reg_range(0x211a, 0x211d), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2612), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x291b), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a08), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + regmap_reg_range(0x2c00, 0x2c05), + regmap_reg_range(0x2c08, 0x2c1b), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3004, 0x300b), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3021), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3300, 0x3301), + regmap_reg_range(0x3303, 0x3303), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3612), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x391b), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a08), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + regmap_reg_range(0x3c00, 0x3c05), + regmap_reg_range(0x3c08, 0x3c1b), +}; + +static const struct regmap_access_table ksz8563_register_set = { + .yes_ranges = ksz8563_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz8563_valid_regs), +}; + +static const struct regmap_range ksz9477_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x0010, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0103, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x012b), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033b), + regmap_reg_range(0x033e, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + regmap_reg_range(0x0444, 0x044b), + regmap_reg_range(0x0450, 0x046f), + regmap_reg_range(0x0500, 0x0519), + regmap_reg_range(0x0520, 0x054b), + regmap_reg_range(0x0550, 0x05b3), + regmap_reg_range(0x0604, 0x060b), + regmap_reg_range(0x0610, 0x0612), + regmap_reg_range(0x0614, 0x062c), + regmap_reg_range(0x0640, 0x0645), + regmap_reg_range(0x0648, 0x064d), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1020), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1115), + regmap_reg_range(0x111a, 0x111f), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1613), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1820, 0x1827), + regmap_reg_range(0x1830, 0x1837), + regmap_reg_range(0x1840, 0x184b), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x191b), + regmap_reg_range(0x1920, 0x1920), + regmap_reg_range(0x1923, 0x1927), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a07), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + regmap_reg_range(0x1c00, 0x1c05), + regmap_reg_range(0x1c08, 0x1c1b), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2020), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2115), + regmap_reg_range(0x211a, 0x211f), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2613), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2820, 0x2827), + regmap_reg_range(0x2830, 0x2837), + regmap_reg_range(0x2840, 0x284b), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x291b), + regmap_reg_range(0x2920, 0x2920), + regmap_reg_range(0x2923, 0x2927), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a07), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + regmap_reg_range(0x2c00, 0x2c05), + regmap_reg_range(0x2c08, 0x2c1b), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3020), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3100, 0x3115), + regmap_reg_range(0x311a, 0x311f), + regmap_reg_range(0x3122, 0x3127), + regmap_reg_range(0x312a, 0x312b), + regmap_reg_range(0x3136, 0x3139), + regmap_reg_range(0x313e, 0x313f), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3613), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3820, 0x3827), + regmap_reg_range(0x3830, 0x3837), + regmap_reg_range(0x3840, 0x384b), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x391b), + regmap_reg_range(0x3920, 0x3920), + regmap_reg_range(0x3923, 0x3927), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a07), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + regmap_reg_range(0x3c00, 0x3c05), + regmap_reg_range(0x3c08, 0x3c1b), + + /* port 4 */ + regmap_reg_range(0x4000, 0x4001), + regmap_reg_range(0x4013, 0x4013), + regmap_reg_range(0x4017, 0x4017), + regmap_reg_range(0x401b, 0x401b), + regmap_reg_range(0x401f, 0x4020), + regmap_reg_range(0x4030, 0x4030), + regmap_reg_range(0x4100, 0x4115), + regmap_reg_range(0x411a, 0x411f), + regmap_reg_range(0x4122, 0x4127), + regmap_reg_range(0x412a, 0x412b), + regmap_reg_range(0x4136, 0x4139), + regmap_reg_range(0x413e, 0x413f), + regmap_reg_range(0x4400, 0x4401), + regmap_reg_range(0x4403, 0x4403), + regmap_reg_range(0x4410, 0x4417), + regmap_reg_range(0x4420, 0x4423), + regmap_reg_range(0x4500, 0x4507), + regmap_reg_range(0x4600, 0x4613), + regmap_reg_range(0x4800, 0x480f), + regmap_reg_range(0x4820, 0x4827), + regmap_reg_range(0x4830, 0x4837), + regmap_reg_range(0x4840, 0x484b), + regmap_reg_range(0x4900, 0x4907), + regmap_reg_range(0x4914, 0x491b), + regmap_reg_range(0x4920, 0x4920), + regmap_reg_range(0x4923, 0x4927), + regmap_reg_range(0x4a00, 0x4a03), + regmap_reg_range(0x4a04, 0x4a07), + regmap_reg_range(0x4b00, 0x4b01), + regmap_reg_range(0x4b04, 0x4b04), + regmap_reg_range(0x4c00, 0x4c05), + regmap_reg_range(0x4c08, 0x4c1b), + + /* port 5 */ + regmap_reg_range(0x5000, 0x5001), + regmap_reg_range(0x5013, 0x5013), + regmap_reg_range(0x5017, 0x5017), + regmap_reg_range(0x501b, 0x501b), + regmap_reg_range(0x501f, 0x5020), + regmap_reg_range(0x5030, 0x5030), + regmap_reg_range(0x5100, 0x5115), + regmap_reg_range(0x511a, 0x511f), + regmap_reg_range(0x5122, 0x5127), + regmap_reg_range(0x512a, 0x512b), + regmap_reg_range(0x5136, 0x5139), + regmap_reg_range(0x513e, 0x513f), + regmap_reg_range(0x5400, 0x5401), + regmap_reg_range(0x5403, 0x5403), + regmap_reg_range(0x5410, 0x5417), + regmap_reg_range(0x5420, 0x5423), + regmap_reg_range(0x5500, 0x5507), + regmap_reg_range(0x5600, 0x5613), + regmap_reg_range(0x5800, 0x580f), + regmap_reg_range(0x5820, 0x5827), + regmap_reg_range(0x5830, 0x5837), + regmap_reg_range(0x5840, 0x584b), + regmap_reg_range(0x5900, 0x5907), + regmap_reg_range(0x5914, 0x591b), + regmap_reg_range(0x5920, 0x5920), + regmap_reg_range(0x5923, 0x5927), + regmap_reg_range(0x5a00, 0x5a03), + regmap_reg_range(0x5a04, 0x5a07), + regmap_reg_range(0x5b00, 0x5b01), + regmap_reg_range(0x5b04, 0x5b04), + regmap_reg_range(0x5c00, 0x5c05), + regmap_reg_range(0x5c08, 0x5c1b), + + /* port 6 */ + regmap_reg_range(0x6000, 0x6001), + regmap_reg_range(0x6013, 0x6013), + regmap_reg_range(0x6017, 0x6017), + regmap_reg_range(0x601b, 0x601b), + regmap_reg_range(0x601f, 0x6020), + regmap_reg_range(0x6030, 0x6030), + regmap_reg_range(0x6300, 0x6301), + regmap_reg_range(0x6400, 0x6401), + regmap_reg_range(0x6403, 0x6403), + regmap_reg_range(0x6410, 0x6417), + regmap_reg_range(0x6420, 0x6423), + regmap_reg_range(0x6500, 0x6507), + regmap_reg_range(0x6600, 0x6613), + regmap_reg_range(0x6800, 0x680f), + regmap_reg_range(0x6820, 0x6827), + regmap_reg_range(0x6830, 0x6837), + regmap_reg_range(0x6840, 0x684b), + regmap_reg_range(0x6900, 0x6907), + regmap_reg_range(0x6914, 0x691b), + regmap_reg_range(0x6920, 0x6920), + regmap_reg_range(0x6923, 0x6927), + regmap_reg_range(0x6a00, 0x6a03), + regmap_reg_range(0x6a04, 0x6a07), + regmap_reg_range(0x6b00, 0x6b01), + regmap_reg_range(0x6b04, 0x6b04), + regmap_reg_range(0x6c00, 0x6c05), + regmap_reg_range(0x6c08, 0x6c1b), + + /* port 7 */ + regmap_reg_range(0x7000, 0x7001), + regmap_reg_range(0x7013, 0x7013), + regmap_reg_range(0x7017, 0x7017), + regmap_reg_range(0x701b, 0x701b), + regmap_reg_range(0x701f, 0x7020), + regmap_reg_range(0x7030, 0x7030), + regmap_reg_range(0x7200, 0x7203), + regmap_reg_range(0x7206, 0x7207), + regmap_reg_range(0x7300, 0x7301), + regmap_reg_range(0x7400, 0x7401), + regmap_reg_range(0x7403, 0x7403), + regmap_reg_range(0x7410, 0x7417), + regmap_reg_range(0x7420, 0x7423), + regmap_reg_range(0x7500, 0x7507), + regmap_reg_range(0x7600, 0x7613), + regmap_reg_range(0x7800, 0x780f), + regmap_reg_range(0x7820, 0x7827), + regmap_reg_range(0x7830, 0x7837), + regmap_reg_range(0x7840, 0x784b), + regmap_reg_range(0x7900, 0x7907), + regmap_reg_range(0x7914, 0x791b), + regmap_reg_range(0x7920, 0x7920), + regmap_reg_range(0x7923, 0x7927), + regmap_reg_range(0x7a00, 0x7a03), + regmap_reg_range(0x7a04, 0x7a07), + regmap_reg_range(0x7b00, 0x7b01), + regmap_reg_range(0x7b04, 0x7b04), + regmap_reg_range(0x7c00, 0x7c05), + regmap_reg_range(0x7c08, 0x7c1b), +}; + +static const struct regmap_access_table ksz9477_register_set = { + .yes_ranges = ksz9477_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz9477_valid_regs), +}; + +static const struct regmap_range ksz9896_valid_regs[] = { + regmap_reg_range(0x0000, 0x0003), + regmap_reg_range(0x0006, 0x0006), + regmap_reg_range(0x0010, 0x001f), + regmap_reg_range(0x0100, 0x0100), + regmap_reg_range(0x0103, 0x0107), + regmap_reg_range(0x010d, 0x010d), + regmap_reg_range(0x0110, 0x0113), + regmap_reg_range(0x0120, 0x0127), + regmap_reg_range(0x0201, 0x0201), + regmap_reg_range(0x0210, 0x0213), + regmap_reg_range(0x0300, 0x0300), + regmap_reg_range(0x0302, 0x030b), + regmap_reg_range(0x0310, 0x031b), + regmap_reg_range(0x0320, 0x032b), + regmap_reg_range(0x0330, 0x0336), + regmap_reg_range(0x0338, 0x033b), + regmap_reg_range(0x033e, 0x033e), + regmap_reg_range(0x0340, 0x035f), + regmap_reg_range(0x0370, 0x0370), + regmap_reg_range(0x0378, 0x0378), + regmap_reg_range(0x037c, 0x037d), + regmap_reg_range(0x0390, 0x0393), + regmap_reg_range(0x0400, 0x040e), + regmap_reg_range(0x0410, 0x042f), + + /* port 1 */ + regmap_reg_range(0x1000, 0x1001), + regmap_reg_range(0x1013, 0x1013), + regmap_reg_range(0x1017, 0x1017), + regmap_reg_range(0x101b, 0x101b), + regmap_reg_range(0x101f, 0x1020), + regmap_reg_range(0x1030, 0x1030), + regmap_reg_range(0x1100, 0x1115), + regmap_reg_range(0x111a, 0x111f), + regmap_reg_range(0x1122, 0x1127), + regmap_reg_range(0x112a, 0x112b), + regmap_reg_range(0x1136, 0x1139), + regmap_reg_range(0x113e, 0x113f), + regmap_reg_range(0x1400, 0x1401), + regmap_reg_range(0x1403, 0x1403), + regmap_reg_range(0x1410, 0x1417), + regmap_reg_range(0x1420, 0x1423), + regmap_reg_range(0x1500, 0x1507), + regmap_reg_range(0x1600, 0x1612), + regmap_reg_range(0x1800, 0x180f), + regmap_reg_range(0x1820, 0x1827), + regmap_reg_range(0x1830, 0x1837), + regmap_reg_range(0x1840, 0x184b), + regmap_reg_range(0x1900, 0x1907), + regmap_reg_range(0x1914, 0x1915), + regmap_reg_range(0x1a00, 0x1a03), + regmap_reg_range(0x1a04, 0x1a07), + regmap_reg_range(0x1b00, 0x1b01), + regmap_reg_range(0x1b04, 0x1b04), + + /* port 2 */ + regmap_reg_range(0x2000, 0x2001), + regmap_reg_range(0x2013, 0x2013), + regmap_reg_range(0x2017, 0x2017), + regmap_reg_range(0x201b, 0x201b), + regmap_reg_range(0x201f, 0x2020), + regmap_reg_range(0x2030, 0x2030), + regmap_reg_range(0x2100, 0x2115), + regmap_reg_range(0x211a, 0x211f), + regmap_reg_range(0x2122, 0x2127), + regmap_reg_range(0x212a, 0x212b), + regmap_reg_range(0x2136, 0x2139), + regmap_reg_range(0x213e, 0x213f), + regmap_reg_range(0x2400, 0x2401), + regmap_reg_range(0x2403, 0x2403), + regmap_reg_range(0x2410, 0x2417), + regmap_reg_range(0x2420, 0x2423), + regmap_reg_range(0x2500, 0x2507), + regmap_reg_range(0x2600, 0x2612), + regmap_reg_range(0x2800, 0x280f), + regmap_reg_range(0x2820, 0x2827), + regmap_reg_range(0x2830, 0x2837), + regmap_reg_range(0x2840, 0x284b), + regmap_reg_range(0x2900, 0x2907), + regmap_reg_range(0x2914, 0x2915), + regmap_reg_range(0x2a00, 0x2a03), + regmap_reg_range(0x2a04, 0x2a07), + regmap_reg_range(0x2b00, 0x2b01), + regmap_reg_range(0x2b04, 0x2b04), + + /* port 3 */ + regmap_reg_range(0x3000, 0x3001), + regmap_reg_range(0x3013, 0x3013), + regmap_reg_range(0x3017, 0x3017), + regmap_reg_range(0x301b, 0x301b), + regmap_reg_range(0x301f, 0x3020), + regmap_reg_range(0x3030, 0x3030), + regmap_reg_range(0x3100, 0x3115), + regmap_reg_range(0x311a, 0x311f), + regmap_reg_range(0x3122, 0x3127), + regmap_reg_range(0x312a, 0x312b), + regmap_reg_range(0x3136, 0x3139), + regmap_reg_range(0x313e, 0x313f), + regmap_reg_range(0x3400, 0x3401), + regmap_reg_range(0x3403, 0x3403), + regmap_reg_range(0x3410, 0x3417), + regmap_reg_range(0x3420, 0x3423), + regmap_reg_range(0x3500, 0x3507), + regmap_reg_range(0x3600, 0x3612), + regmap_reg_range(0x3800, 0x380f), + regmap_reg_range(0x3820, 0x3827), + regmap_reg_range(0x3830, 0x3837), + regmap_reg_range(0x3840, 0x384b), + regmap_reg_range(0x3900, 0x3907), + regmap_reg_range(0x3914, 0x3915), + regmap_reg_range(0x3a00, 0x3a03), + regmap_reg_range(0x3a04, 0x3a07), + regmap_reg_range(0x3b00, 0x3b01), + regmap_reg_range(0x3b04, 0x3b04), + + /* port 4 */ + regmap_reg_range(0x4000, 0x4001), + regmap_reg_range(0x4013, 0x4013), + regmap_reg_range(0x4017, 0x4017), + regmap_reg_range(0x401b, 0x401b), + regmap_reg_range(0x401f, 0x4020), + regmap_reg_range(0x4030, 0x4030), + regmap_reg_range(0x4100, 0x4115), + regmap_reg_range(0x411a, 0x411f), + regmap_reg_range(0x4122, 0x4127), + regmap_reg_range(0x412a, 0x412b), + regmap_reg_range(0x4136, 0x4139), + regmap_reg_range(0x413e, 0x413f), + regmap_reg_range(0x4400, 0x4401), + regmap_reg_range(0x4403, 0x4403), + regmap_reg_range(0x4410, 0x4417), + regmap_reg_range(0x4420, 0x4423), + regmap_reg_range(0x4500, 0x4507), + regmap_reg_range(0x4600, 0x4612), + regmap_reg_range(0x4800, 0x480f), + regmap_reg_range(0x4820, 0x4827), + regmap_reg_range(0x4830, 0x4837), + regmap_reg_range(0x4840, 0x484b), + regmap_reg_range(0x4900, 0x4907), + regmap_reg_range(0x4914, 0x4915), + regmap_reg_range(0x4a00, 0x4a03), + regmap_reg_range(0x4a04, 0x4a07), + regmap_reg_range(0x4b00, 0x4b01), + regmap_reg_range(0x4b04, 0x4b04), + + /* port 5 */ + regmap_reg_range(0x5000, 0x5001), + regmap_reg_range(0x5013, 0x5013), + regmap_reg_range(0x5017, 0x5017), + regmap_reg_range(0x501b, 0x501b), + regmap_reg_range(0x501f, 0x5020), + regmap_reg_range(0x5030, 0x5030), + regmap_reg_range(0x5100, 0x5115), + regmap_reg_range(0x511a, 0x511f), + regmap_reg_range(0x5122, 0x5127), + regmap_reg_range(0x512a, 0x512b), + regmap_reg_range(0x5136, 0x5139), + regmap_reg_range(0x513e, 0x513f), + regmap_reg_range(0x5400, 0x5401), + regmap_reg_range(0x5403, 0x5403), + regmap_reg_range(0x5410, 0x5417), + regmap_reg_range(0x5420, 0x5423), + regmap_reg_range(0x5500, 0x5507), + regmap_reg_range(0x5600, 0x5612), + regmap_reg_range(0x5800, 0x580f), + regmap_reg_range(0x5820, 0x5827), + regmap_reg_range(0x5830, 0x5837), + regmap_reg_range(0x5840, 0x584b), + regmap_reg_range(0x5900, 0x5907), + regmap_reg_range(0x5914, 0x5915), + regmap_reg_range(0x5a00, 0x5a03), + regmap_reg_range(0x5a04, 0x5a07), + regmap_reg_range(0x5b00, 0x5b01), + regmap_reg_range(0x5b04, 0x5b04), + + /* port 6 */ + regmap_reg_range(0x6000, 0x6001), + regmap_reg_range(0x6013, 0x6013), + regmap_reg_range(0x6017, 0x6017), + regmap_reg_range(0x601b, 0x601b), + regmap_reg_range(0x601f, 0x6020), + regmap_reg_range(0x6030, 0x6030), + regmap_reg_range(0x6100, 0x6115), + regmap_reg_range(0x611a, 0x611f), + regmap_reg_range(0x6122, 0x6127), + regmap_reg_range(0x612a, 0x612b), + regmap_reg_range(0x6136, 0x6139), + regmap_reg_range(0x613e, 0x613f), + regmap_reg_range(0x6300, 0x6301), + regmap_reg_range(0x6400, 0x6401), + regmap_reg_range(0x6403, 0x6403), + regmap_reg_range(0x6410, 0x6417), + regmap_reg_range(0x6420, 0x6423), + regmap_reg_range(0x6500, 0x6507), + regmap_reg_range(0x6600, 0x6612), + regmap_reg_range(0x6800, 0x680f), + regmap_reg_range(0x6820, 0x6827), + regmap_reg_range(0x6830, 0x6837), + regmap_reg_range(0x6840, 0x684b), + regmap_reg_range(0x6900, 0x6907), + regmap_reg_range(0x6914, 0x6915), + regmap_reg_range(0x6a00, 0x6a03), + regmap_reg_range(0x6a04, 0x6a07), + regmap_reg_range(0x6b00, 0x6b01), + regmap_reg_range(0x6b04, 0x6b04), +}; + +static const struct regmap_access_table ksz9896_register_set = { + .yes_ranges = ksz9896_valid_regs, + .n_yes_ranges = ARRAY_SIZE(ksz9896_valid_regs), +}; + +const struct ksz_chip_data ksz_switch_chips[] = { + [KSZ8563] = { + .chip_id = KSZ8563_CHIP_ID, + .dev_name = "KSZ8563", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + .ops = &ksz9477_dev_ops, + .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 = ksz8795_xmii_ctrl1, /* Same as ksz8795 */ + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .supports_rgmii = {false, false, true}, + .internal_phy = {true, true, false}, + .gbit_capable = {false, false, true}, + .wr_table = &ksz8563_register_set, + .rd_table = &ksz8563_register_set, + }, + + [KSZ8795] = { + .chip_id = KSZ8795_CHIP_ID, + .dev_name = "KSZ8795", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ops = &ksz8_dev_ops, + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz8795_regs, + .masks = ksz8795_masks, + .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [KSZ8794] = { + /* WARNING + * ======= + * KSZ8794 is similar to KSZ8795, except the port map + * contains a gap between external and CPU ports, the + * port map is NOT continuous. The per-port register + * map is shifted accordingly too, i.e. registers at + * offset 0x40 are NOT used on KSZ8794 and they ARE + * used on KSZ8795 for external port 3. + * external cpu + * KSZ8794 0,1,2 4 + * KSZ8795 0,1,2,3 4 + * KSZ8765 0,1,2,3 4 + * port_cnt is configured as 5, even though it is 4 + */ + .chip_id = KSZ8794_CHIP_ID, + .dev_name = "KSZ8794", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ops = &ksz8_dev_ops, + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz8795_regs, + .masks = ksz8795_masks, + .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, false, false}, + }, + + [KSZ8765] = { + .chip_id = KSZ8765_CHIP_ID, + .dev_name = "KSZ8765", + .num_vlans = 4096, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total cpu and user ports */ + .ops = &ksz8_dev_ops, + .ksz87xx_eee_link_erratum = true, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz8795_regs, + .masks = ksz8795_masks, + .shifts = ksz8795_shifts, + .xmii_ctrl0 = ksz8795_xmii_ctrl0, + .xmii_ctrl1 = ksz8795_xmii_ctrl1, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [KSZ8830] = { + .chip_id = KSZ8830_CHIP_ID, + .dev_name = "KSZ8863/KSZ8873", + .num_vlans = 16, + .num_alus = 0, + .num_statics = 8, + .cpu_ports = 0x4, /* can be configured as cpu port */ + .port_cnt = 3, + .ops = &ksz8_dev_ops, + .mib_names = ksz88xx_mib_names, + .mib_cnt = ARRAY_SIZE(ksz88xx_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz8863_regs, + .masks = ksz8863_masks, + .shifts = ksz8863_shifts, + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .internal_phy = {true, true, false}, + }, + + [KSZ9477] = { + .chip_id = KSZ9477_CHIP_ID, + .dev_name = "KSZ9477", + .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 = 4, + .ops = &ksz9477_dev_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}, + .wr_table = &ksz9477_register_set, + .rd_table = &ksz9477_register_set, + }, + + [KSZ9896] = { + .chip_id = KSZ9896_CHIP_ID, + .dev_name = "KSZ9896", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x3F, /* can be configured as cpu port */ + .port_cnt = 6, /* total physical port count */ + .port_nirqs = 2, + .ops = &ksz9477_dev_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}, + .supports_rmii = {false, false, false, false, + false, true}, + .supports_rgmii = {false, false, false, false, + false, true}, + .internal_phy = {true, true, true, true, + true, false}, + .gbit_capable = {true, true, true, true, true, true}, + .wr_table = &ksz9896_register_set, + .rd_table = &ksz9896_register_set, + }, + + [KSZ9897] = { + .chip_id = KSZ9897_CHIP_ID, + .dev_name = "KSZ9897", + .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, + .ops = &ksz9477_dev_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, true}, + .supports_rmii = {false, false, false, false, + false, true, true}, + .supports_rgmii = {false, false, false, false, + false, true, true}, + .internal_phy = {true, true, true, true, + true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, + }, + + [KSZ9893] = { + .chip_id = KSZ9893_CHIP_ID, + .dev_name = "KSZ9893", + .num_vlans = 4096, + .num_alus = 4096, + .num_statics = 16, + .cpu_ports = 0x07, /* can be configured as cpu port */ + .port_cnt = 3, /* total port count */ + .port_nirqs = 2, + .ops = &ksz9477_dev_ops, + .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 = ksz8795_xmii_ctrl1, /* Same as ksz8795 */ + .supports_mii = {false, false, true}, + .supports_rmii = {false, false, true}, + .supports_rgmii = {false, false, true}, + .internal_phy = {true, true, false}, + .gbit_capable = {true, true, true}, + }, + + [KSZ9567] = { + .chip_id = KSZ9567_CHIP_ID, + .dev_name = "KSZ9567", + .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 = 3, + .ops = &ksz9477_dev_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, true}, + .supports_rmii = {false, false, false, false, + false, true, true}, + .supports_rgmii = {false, false, false, false, + false, true, true}, + .internal_phy = {true, true, true, true, + true, false, false}, + .gbit_capable = {true, true, true, true, true, true, true}, + }, + + [LAN9370] = { + .chip_id = LAN9370_CHIP_ID, + .dev_name = "LAN9370", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x10, /* can be configured as cpu port */ + .port_cnt = 5, /* total physical port count */ + .port_nirqs = 6, + .ops = &lan937x_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = lan937x_masks, + .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, true}, + .supports_rmii = {false, false, false, false, true}, + .supports_rgmii = {false, false, false, false, true}, + .internal_phy = {true, true, true, true, false}, + }, + + [LAN9371] = { + .chip_id = LAN9371_CHIP_ID, + .dev_name = "LAN9371", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 6, /* total physical port count */ + .port_nirqs = 6, + .ops = &lan937x_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = lan937x_masks, + .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, true, true}, + .supports_rmii = {false, false, false, false, true, true}, + .supports_rgmii = {false, false, false, false, true, true}, + .internal_phy = {true, true, true, true, false, false}, + }, + + [LAN9372] = { + .chip_id = LAN9372_CHIP_ID, + .dev_name = "LAN9372", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 8, /* total physical port count */ + .port_nirqs = 6, + .ops = &lan937x_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = lan937x_masks, + .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, true, + false, false, true, true}, + }, + + [LAN9373] = { + .chip_id = LAN9373_CHIP_ID, + .dev_name = "LAN9373", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x38, /* can be configured as cpu port */ + .port_cnt = 5, /* total physical port count */ + .port_nirqs = 6, + .ops = &lan937x_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = lan937x_masks, + .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, false, + false, false, true, true}, + }, + + [LAN9374] = { + .chip_id = LAN9374_CHIP_ID, + .dev_name = "LAN9374", + .num_vlans = 4096, + .num_alus = 1024, + .num_statics = 256, + .cpu_ports = 0x30, /* can be configured as cpu port */ + .port_cnt = 8, /* total physical port count */ + .port_nirqs = 6, + .ops = &lan937x_dev_ops, + .mib_names = ksz9477_mib_names, + .mib_cnt = ARRAY_SIZE(ksz9477_mib_names), + .reg_mib_cnt = MIB_COUNTER_NUM, + .regs = ksz9477_regs, + .masks = lan937x_masks, + .shifts = lan937x_shifts, + .xmii_ctrl0 = ksz9477_xmii_ctrl0, + .xmii_ctrl1 = ksz9477_xmii_ctrl1, + .supports_mii = {false, false, false, false, + true, true, false, false}, + .supports_rmii = {false, false, false, false, + true, true, false, false}, + .supports_rgmii = {false, false, false, false, + true, true, false, false}, + .internal_phy = {true, true, true, true, + false, false, true, true}, + }, +}; +EXPORT_SYMBOL_GPL(ksz_switch_chips); + +static const struct ksz_chip_data *ksz_lookup_info(unsigned int prod_num) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ksz_switch_chips); i++) { + const struct ksz_chip_data *chip = &ksz_switch_chips[i]; + + if (chip->chip_id == prod_num) + return chip; + } + + return NULL; +} + +static int ksz_check_device_id(struct ksz_device *dev) +{ + const struct ksz_chip_data *dt_chip_data; + + dt_chip_data = of_device_get_match_data(dev->dev); + + /* Check for Device Tree and Chip ID */ + if (dt_chip_data->chip_id != dev->chip_id) { + dev_err(dev->dev, + "Device tree specifies chip %s but found %s, please fix it!\n", + dt_chip_data->dev_name, dev->info->dev_name); + return -ENODEV; + } + + return 0; +} + +static void ksz_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + struct ksz_device *dev = ds->priv; + + config->legacy_pre_march2020 = false; + + if (dev->info->supports_mii[port]) + __set_bit(PHY_INTERFACE_MODE_MII, config->supported_interfaces); + + if (dev->info->supports_rmii[port]) + __set_bit(PHY_INTERFACE_MODE_RMII, + config->supported_interfaces); + + if (dev->info->supports_rgmii[port]) + phy_interface_set_rgmii(config->supported_interfaces); + + if (dev->info->internal_phy[port]) { + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); + /* Compatibility for phylib's default interface type when the + * phy-mode property is absent + */ + __set_bit(PHY_INTERFACE_MODE_GMII, + config->supported_interfaces); + } + + if (dev->dev_ops->get_caps) + dev->dev_ops->get_caps(dev, port, config); +} + +void ksz_r_mib_stats64(struct ksz_device *dev, int port) +{ + struct ethtool_pause_stats *pstats; + struct rtnl_link_stats64 *stats; + struct ksz_stats_raw *raw; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + stats = &mib->stats64; + pstats = &mib->pause_stats; + raw = (struct ksz_stats_raw *)mib->counters; + + spin_lock(&mib->stats64_lock); + + stats->rx_packets = raw->rx_bcast + raw->rx_mcast + raw->rx_ucast + + raw->rx_pause; + stats->tx_packets = raw->tx_bcast + raw->tx_mcast + raw->tx_ucast + + raw->tx_pause; + + /* HW counters are counting bytes + FCS which is not acceptable + * for rtnl_link_stats64 interface + */ + stats->rx_bytes = raw->rx_total - stats->rx_packets * ETH_FCS_LEN; + stats->tx_bytes = raw->tx_total - stats->tx_packets * ETH_FCS_LEN; + + stats->rx_length_errors = raw->rx_undersize + raw->rx_fragments + + raw->rx_oversize; + + stats->rx_crc_errors = raw->rx_crc_err; + stats->rx_frame_errors = raw->rx_align_err; + stats->rx_dropped = raw->rx_discards; + stats->rx_errors = stats->rx_length_errors + stats->rx_crc_errors + + stats->rx_frame_errors + stats->rx_dropped; + + stats->tx_window_errors = raw->tx_late_col; + stats->tx_fifo_errors = raw->tx_discards; + stats->tx_aborted_errors = raw->tx_exc_col; + stats->tx_errors = stats->tx_window_errors + stats->tx_fifo_errors + + stats->tx_aborted_errors; + + stats->multicast = raw->rx_mcast; + stats->collisions = raw->tx_total_col; + + pstats->tx_pause_frames = raw->tx_pause; + pstats->rx_pause_frames = raw->rx_pause; + + spin_unlock(&mib->stats64_lock); +} + +static void ksz_get_stats64(struct dsa_switch *ds, int port, + struct rtnl_link_stats64 *s) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + + spin_lock(&mib->stats64_lock); + memcpy(s, &mib->stats64, sizeof(*s)); + spin_unlock(&mib->stats64_lock); +} + +static void ksz_get_pause_stats(struct dsa_switch *ds, int port, + struct ethtool_pause_stats *pause_stats) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port_mib *mib; + + mib = &dev->ports[port].mib; + + spin_lock(&mib->stats64_lock); + memcpy(pause_stats, &mib->pause_stats, sizeof(*pause_stats)); + spin_unlock(&mib->stats64_lock); +} -void ksz_update_port_member(struct ksz_device *dev, int port) +static void ksz_get_strings(struct dsa_switch *ds, int port, + u32 stringset, uint8_t *buf) +{ + struct ksz_device *dev = ds->priv; + int i; + + if (stringset != ETH_SS_STATS) + return; + + for (i = 0; i < dev->info->mib_cnt; i++) { + memcpy(buf + i * ETH_GSTRING_LEN, + dev->info->mib_names[i].string, ETH_GSTRING_LEN); + } +} + +static void ksz_update_port_member(struct ksz_device *dev, int port) { struct ksz_port *p = &dev->ports[port]; struct dsa_switch *ds = dev->ds; u8 port_member = 0, cpu_port; const struct dsa_port *dp; - int i; + int i, j; if (!dsa_is_user_port(ds, port)) return; @@ -45,19 +1623,411 @@ void ksz_update_port_member(struct ksz_device *dev, int port) continue; if (!dsa_port_bridge_same(dp, other_dp)) continue; + if (other_p->stp_state != BR_STATE_FORWARDING) + continue; - if (other_p->stp_state == BR_STATE_FORWARDING && - p->stp_state == BR_STATE_FORWARDING) { + if (p->stp_state == BR_STATE_FORWARDING) { val |= BIT(port); port_member |= BIT(i); } + /* Retain port [i]'s relationship to other ports than [port] */ + for (j = 0; j < ds->num_ports; j++) { + const struct dsa_port *third_dp; + struct ksz_port *third_p; + + if (j == i) + continue; + if (j == port) + continue; + if (!dsa_is_user_port(ds, j)) + continue; + third_p = &dev->ports[j]; + if (third_p->stp_state != BR_STATE_FORWARDING) + continue; + third_dp = dsa_to_port(ds, j); + if (dsa_port_bridge_same(other_dp, third_dp)) + val |= BIT(j); + } + dev->dev_ops->cfg_port_member(dev, i, val | cpu_port); } dev->dev_ops->cfg_port_member(dev, port, port_member | cpu_port); } -EXPORT_SYMBOL_GPL(ksz_update_port_member); + +static int ksz_sw_mdio_read(struct mii_bus *bus, int addr, int regnum) +{ + struct ksz_device *dev = bus->priv; + u16 val; + int ret; + + if (regnum & MII_ADDR_C45) + return -EOPNOTSUPP; + + ret = dev->dev_ops->r_phy(dev, addr, regnum, &val); + if (ret < 0) + return ret; + + return val; +} + +static int ksz_sw_mdio_write(struct mii_bus *bus, int addr, int regnum, + u16 val) +{ + struct ksz_device *dev = bus->priv; + + if (regnum & MII_ADDR_C45) + return -EOPNOTSUPP; + + return dev->dev_ops->w_phy(dev, addr, regnum, val); +} + +static int ksz_irq_phy_setup(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy; + int irq; + int ret; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) { + if (BIT(phy) & ds->phys_mii_mask) { + irq = irq_find_mapping(dev->ports[phy].pirq.domain, + PORT_SRC_PHY_INT); + if (irq < 0) { + ret = irq; + goto out; + } + ds->slave_mii_bus->irq[phy] = irq; + } + } + return 0; +out: + while (phy--) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); + + return ret; +} + +static void ksz_irq_phy_free(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + int phy; + + for (phy = 0; phy < KSZ_MAX_NUM_PORTS; phy++) + if (BIT(phy) & ds->phys_mii_mask) + irq_dispose_mapping(ds->slave_mii_bus->irq[phy]); +} + +static int ksz_mdio_register(struct ksz_device *dev) +{ + struct dsa_switch *ds = dev->ds; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret; + + mdio_np = of_get_child_by_name(dev->dev->of_node, "mdio"); + if (!mdio_np) + return 0; + + bus = devm_mdiobus_alloc(ds->dev); + if (!bus) { + of_node_put(mdio_np); + return -ENOMEM; + } + + bus->priv = dev; + bus->read = ksz_sw_mdio_read; + bus->write = ksz_sw_mdio_write; + bus->name = "ksz slave smi"; + snprintf(bus->id, MII_BUS_ID_SIZE, "SMI-%d", ds->index); + bus->parent = ds->dev; + bus->phy_mask = ~ds->phys_mii_mask; + + ds->slave_mii_bus = bus; + + if (dev->irq > 0) { + ret = ksz_irq_phy_setup(dev); + if (ret) { + of_node_put(mdio_np); + return ret; + } + } + + ret = devm_of_mdiobus_register(ds->dev, bus, mdio_np); + if (ret) { + dev_err(ds->dev, "unable to register MDIO bus %s\n", + bus->id); + if (dev->irq > 0) + ksz_irq_phy_free(dev); + } + + of_node_put(mdio_np); + + return ret; +} + +static void ksz_irq_mask(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + kirq->masked |= BIT(d->hwirq); +} + +static void ksz_irq_unmask(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + kirq->masked &= ~BIT(d->hwirq); +} + +static void ksz_irq_bus_lock(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + + mutex_lock(&kirq->dev->lock_irq); +} + +static void ksz_irq_bus_sync_unlock(struct irq_data *d) +{ + struct ksz_irq *kirq = irq_data_get_irq_chip_data(d); + struct ksz_device *dev = kirq->dev; + int ret; + + ret = ksz_write32(dev, kirq->reg_mask, kirq->masked); + if (ret) + dev_err(dev->dev, "failed to change IRQ mask\n"); + + mutex_unlock(&dev->lock_irq); +} + +static const struct irq_chip ksz_irq_chip = { + .name = "ksz-irq", + .irq_mask = ksz_irq_mask, + .irq_unmask = ksz_irq_unmask, + .irq_bus_lock = ksz_irq_bus_lock, + .irq_bus_sync_unlock = ksz_irq_bus_sync_unlock, +}; + +static int ksz_irq_domain_map(struct irq_domain *d, + unsigned int irq, irq_hw_number_t hwirq) +{ + irq_set_chip_data(irq, d->host_data); + irq_set_chip_and_handler(irq, &ksz_irq_chip, handle_level_irq); + irq_set_noprobe(irq); + + return 0; +} + +static const struct irq_domain_ops ksz_irq_domain_ops = { + .map = ksz_irq_domain_map, + .xlate = irq_domain_xlate_twocell, +}; + +static void ksz_irq_free(struct ksz_irq *kirq) +{ + int irq, virq; + + free_irq(kirq->irq_num, kirq); + + for (irq = 0; irq < kirq->nirqs; irq++) { + virq = irq_find_mapping(kirq->domain, irq); + irq_dispose_mapping(virq); + } + + irq_domain_remove(kirq->domain); +} + +static irqreturn_t ksz_irq_thread_fn(int irq, void *dev_id) +{ + struct ksz_irq *kirq = dev_id; + unsigned int nhandled = 0; + struct ksz_device *dev; + unsigned int sub_irq; + u8 data; + int ret; + u8 n; + + dev = kirq->dev; + + /* Read interrupt status register */ + ret = ksz_read8(dev, kirq->reg_status, &data); + if (ret) + goto out; + + for (n = 0; n < kirq->nirqs; ++n) { + if (data & BIT(n)) { + sub_irq = irq_find_mapping(kirq->domain, n); + handle_nested_irq(sub_irq); + ++nhandled; + } + } +out: + return (nhandled > 0 ? IRQ_HANDLED : IRQ_NONE); +} + +static int ksz_irq_common_setup(struct ksz_device *dev, struct ksz_irq *kirq) +{ + int ret, n; + + kirq->dev = dev; + kirq->masked = ~0; + + kirq->domain = irq_domain_add_simple(dev->dev->of_node, kirq->nirqs, 0, + &ksz_irq_domain_ops, kirq); + if (!kirq->domain) + return -ENOMEM; + + for (n = 0; n < kirq->nirqs; n++) + irq_create_mapping(kirq->domain, n); + + ret = request_threaded_irq(kirq->irq_num, NULL, ksz_irq_thread_fn, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + kirq->name, kirq); + if (ret) + goto out; + + return 0; + +out: + ksz_irq_free(kirq); + + return ret; +} + +static int ksz_girq_setup(struct ksz_device *dev) +{ + struct ksz_irq *girq = &dev->girq; + + girq->nirqs = dev->info->port_cnt; + girq->reg_mask = REG_SW_PORT_INT_MASK__1; + girq->reg_status = REG_SW_PORT_INT_STATUS__1; + snprintf(girq->name, sizeof(girq->name), "global_port_irq"); + + girq->irq_num = dev->irq; + + return ksz_irq_common_setup(dev, girq); +} + +static int ksz_pirq_setup(struct ksz_device *dev, u8 p) +{ + struct ksz_irq *pirq = &dev->ports[p].pirq; + + pirq->nirqs = dev->info->port_nirqs; + pirq->reg_mask = dev->dev_ops->get_port_addr(p, REG_PORT_INT_MASK); + pirq->reg_status = dev->dev_ops->get_port_addr(p, REG_PORT_INT_STATUS); + snprintf(pirq->name, sizeof(pirq->name), "port_irq-%d", p); + + pirq->irq_num = irq_find_mapping(dev->girq.domain, p); + if (pirq->irq_num < 0) + return pirq->irq_num; + + return ksz_irq_common_setup(dev, pirq); +} + +static int ksz_setup(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + struct dsa_port *dp; + struct ksz_port *p; + const u16 *regs; + int ret; + + 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; + + ret = dev->dev_ops->reset(dev); + if (ret) { + dev_err(ds->dev, "failed to reset switch\n"); + return ret; + } + + /* set broadcast storm protection 10% rate */ + regmap_update_bits(dev->regmap[1], regs[S_BROADCAST_CTRL], + BROADCAST_STORM_RATE, + (BROADCAST_STORM_VALUE * + BROADCAST_STORM_PROT_RATE) / 100); + + dev->dev_ops->config_cpu_port(ds); + + dev->dev_ops->enable_stp_addr(dev); + + regmap_update_bits(dev->regmap[0], regs[S_MULTICAST_CTRL], + MULTICAST_STORM_DISABLE, MULTICAST_STORM_DISABLE); + + ksz_init_mib_timer(dev); + + ds->configure_vlan_while_not_filtering = false; + + if (dev->dev_ops->setup) { + ret = dev->dev_ops->setup(ds); + if (ret) + return ret; + } + + /* Start with learning disabled on standalone user ports, and enabled + * on the CPU port. In lack of other finer mechanisms, learning on the + * CPU port will avoid flooding bridge local addresses on the network + * in some cases. + */ + p = &dev->ports[dev->cpu_port]; + p->learning = true; + + if (dev->irq > 0) { + ret = ksz_girq_setup(dev); + if (ret) + return ret; + + dsa_switch_for_each_user_port(dp, dev->ds) { + ret = ksz_pirq_setup(dev, dp->index); + if (ret) + goto out_girq; + } + } + + ret = ksz_mdio_register(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to register the mdio"); + goto out_pirq; + } + + /* start switch */ + regmap_update_bits(dev->regmap[0], regs[S_START_CTRL], + SW_START, SW_START); + + return 0; + +out_pirq: + if (dev->irq > 0) + dsa_switch_for_each_user_port(dp, dev->ds) + ksz_irq_free(&dev->ports[dp->index].pirq); +out_girq: + if (dev->irq > 0) + ksz_irq_free(&dev->girq); + + return ret; +} + +static void ksz_teardown(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + struct dsa_port *dp; + + if (dev->irq > 0) { + dsa_switch_for_each_user_port(dp, dev->ds) + ksz_irq_free(&dev->ports[dp->index].pirq); + + ksz_irq_free(&dev->girq); + } + + if (dev->dev_ops->teardown) + dev->dev_ops->teardown(ds); +} static void port_r_cnt(struct ksz_device *dev, int port) { @@ -65,17 +2035,17 @@ static void port_r_cnt(struct ksz_device *dev, int port) u64 *dropped; /* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->reg_mib_cnt) { + while (mib->cnt_ptr < dev->info->reg_mib_cnt) { dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; } /* last one in storage */ - dropped = &mib->counters[dev->mib_cnt]; + dropped = &mib->counters[dev->info->mib_cnt]; /* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */ - while (mib->cnt_ptr < dev->mib_cnt) { + while (mib->cnt_ptr < dev->info->mib_cnt) { dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr, dropped, &mib->counters[mib->cnt_ptr]); ++mib->cnt_ptr; @@ -91,7 +2061,7 @@ static void ksz_mib_read_work(struct work_struct *work) struct ksz_port *p; int i; - for (i = 0; i < dev->port_cnt; i++) { + for (i = 0; i < dev->info->port_cnt; i++) { if (dsa_is_unused_port(dev->ds, i)) continue; @@ -106,10 +2076,14 @@ static void ksz_mib_read_work(struct work_struct *work) const struct dsa_port *dp = dsa_to_port(dev->ds, i); if (!netif_carrier_ok(dp->slave)) - mib->cnt_ptr = dev->reg_mib_cnt; + mib->cnt_ptr = dev->info->reg_mib_cnt; } port_r_cnt(dev, i); p->read = false; + + if (dev->dev_ops->r_mib_stat64) + dev->dev_ops->r_mib_stat64(dev, i); + mutex_unlock(&mib->cnt_mutex); } @@ -122,34 +2096,59 @@ void ksz_init_mib_timer(struct ksz_device *dev) INIT_DELAYED_WORK(&dev->mib_read, ksz_mib_read_work); - for (i = 0; i < dev->port_cnt; i++) + for (i = 0; i < dev->info->port_cnt; i++) { + struct ksz_port_mib *mib = &dev->ports[i].mib; + dev->dev_ops->port_init_cnt(dev, i); + + mib->cnt_ptr = 0; + memset(mib->counters, 0, dev->info->mib_cnt * sizeof(u64)); + } } -EXPORT_SYMBOL_GPL(ksz_init_mib_timer); -int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) +static int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg) { struct ksz_device *dev = ds->priv; u16 val = 0xffff; + int ret; - dev->dev_ops->r_phy(dev, addr, reg, &val); + ret = dev->dev_ops->r_phy(dev, addr, reg, &val); + if (ret) + return ret; return val; } -EXPORT_SYMBOL_GPL(ksz_phy_read16); -int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) +static int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val) { struct ksz_device *dev = ds->priv; + int ret; - dev->dev_ops->w_phy(dev, addr, reg, val); + ret = dev->dev_ops->w_phy(dev, addr, reg, val); + if (ret) + return ret; return 0; } -EXPORT_SYMBOL_GPL(ksz_phy_write16); -void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, - phy_interface_t interface) +static u32 ksz_get_phy_flags(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (dev->chip_id == KSZ8830_CHIP_ID) { + /* Silicon Errata Sheet (DS80000830A): + * Port 1 does not work with LinkMD Cable-Testing. + * Port 1 does not respond to received PAUSE control frames. + */ + if (!port) + return MICREL_KSZ8_P1_ERRATA; + } + + return 0; +} + +static void ksz_mac_link_down(struct dsa_switch *ds, int port, + unsigned int mode, phy_interface_t interface) { struct ksz_device *dev = ds->priv; struct ksz_port *p = &dev->ports[port]; @@ -160,20 +2159,19 @@ void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, if (dev->mib_read_interval) schedule_delayed_work(&dev->mib_read, 0); } -EXPORT_SYMBOL_GPL(ksz_mac_link_down); -int ksz_sset_count(struct dsa_switch *ds, int port, int sset) +static int ksz_sset_count(struct dsa_switch *ds, int port, int sset) { struct ksz_device *dev = ds->priv; if (sset != ETH_SS_STATS) return 0; - return dev->mib_cnt; + return dev->info->mib_cnt; } -EXPORT_SYMBOL_GPL(ksz_sset_count); -void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) +static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, + uint64_t *buf) { const struct dsa_port *dp = dsa_to_port(ds, port); struct ksz_device *dev = ds->priv; @@ -184,16 +2182,16 @@ void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf) /* Only read dropped counters if no link. */ if (!netif_carrier_ok(dp->slave)) - mib->cnt_ptr = dev->reg_mib_cnt; + mib->cnt_ptr = dev->info->reg_mib_cnt; port_r_cnt(dev, port); - memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64)); + memcpy(buf, mib->counters, dev->info->mib_cnt * sizeof(u64)); mutex_unlock(&mib->cnt_mutex); } -EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats); -int ksz_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, - bool *tx_fwd_offload) +static int ksz_port_bridge_join(struct dsa_switch *ds, int port, + struct dsa_bridge bridge, + bool *tx_fwd_offload, + struct netlink_ext_ack *extack) { /* port_stp_state_set() will be called after to put the port in * appropriate state so there is no need to do anything. @@ -201,133 +2199,93 @@ int ksz_port_bridge_join(struct dsa_switch *ds, int port, return 0; } -EXPORT_SYMBOL_GPL(ksz_port_bridge_join); -void ksz_port_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_bridge bridge) +static void ksz_port_bridge_leave(struct dsa_switch *ds, int port, + struct dsa_bridge bridge) { /* port_stp_state_set() will be called after to put the port in * forwarding state so there is no need to do anything. */ } -EXPORT_SYMBOL_GPL(ksz_port_bridge_leave); -void ksz_port_fast_age(struct dsa_switch *ds, int port) +static void ksz_port_fast_age(struct dsa_switch *ds, int port) { struct ksz_device *dev = ds->priv; dev->dev_ops->flush_dyn_mac_table(dev, port); } -EXPORT_SYMBOL_GPL(ksz_port_fast_age); -int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, - void *data) +static int ksz_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) { struct ksz_device *dev = ds->priv; - int ret = 0; - u16 i = 0; - u16 entries = 0; - u8 timestamp = 0; - u8 fid; - u8 member; - struct alu_struct alu; - do { - alu.is_static = false; - ret = dev->dev_ops->r_dyn_mac_table(dev, i, alu.mac, &fid, - &member, ×tamp, - &entries); - if (!ret && (member & BIT(port))) { - ret = cb(alu.mac, alu.fid, alu.is_static, data); - if (ret) - break; - } - i++; - } while (i < entries); - if (i >= entries) - ret = 0; + if (!dev->dev_ops->set_ageing_time) + return -EOPNOTSUPP; - return ret; + return dev->dev_ops->set_ageing_time(dev, msecs); } -EXPORT_SYMBOL_GPL(ksz_port_fdb_dump); -int ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int ksz_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct ksz_device *dev = ds->priv; - struct alu_struct alu; - int index; - int empty = 0; - alu.port_forward = 0; - for (index = 0; index < dev->num_statics; index++) { - if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { - /* Found one already in static MAC table. */ - if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && - alu.fid == mdb->vid) - break; - /* Remember the first empty entry. */ - } else if (!empty) { - empty = index + 1; - } - } + if (!dev->dev_ops->fdb_add) + return -EOPNOTSUPP; - /* no available entry */ - if (index == dev->num_statics && !empty) - return -ENOSPC; + return dev->dev_ops->fdb_add(dev, port, addr, vid, db); +} - /* add entry */ - if (index == dev->num_statics) { - index = empty - 1; - memset(&alu, 0, sizeof(alu)); - memcpy(alu.mac, mdb->addr, ETH_ALEN); - alu.is_static = true; - } - alu.port_forward |= BIT(port); - if (mdb->vid) { - alu.is_use_fid = true; +static int ksz_port_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, + u16 vid, struct dsa_db db) +{ + struct ksz_device *dev = ds->priv; - /* Need a way to map VID to FID. */ - alu.fid = mdb->vid; - } - dev->dev_ops->w_sta_mac_table(dev, index, &alu); + if (!dev->dev_ops->fdb_del) + return -EOPNOTSUPP; - return 0; + return dev->dev_ops->fdb_del(dev, port, addr, vid, db); } -EXPORT_SYMBOL_GPL(ksz_port_mdb_add); -int ksz_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb) +static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, + dsa_fdb_dump_cb_t *cb, void *data) { struct ksz_device *dev = ds->priv; - struct alu_struct alu; - int index; - for (index = 0; index < dev->num_statics; index++) { - if (!dev->dev_ops->r_sta_mac_table(dev, index, &alu)) { - /* Found one already in static MAC table. */ - if (!memcmp(alu.mac, mdb->addr, ETH_ALEN) && - alu.fid == mdb->vid) - break; - } - } + if (!dev->dev_ops->fdb_dump) + return -EOPNOTSUPP; + + return dev->dev_ops->fdb_dump(dev, port, cb, data); +} + +static int ksz_port_mdb_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct ksz_device *dev = ds->priv; - /* no available entry */ - if (index == dev->num_statics) - goto exit; + if (!dev->dev_ops->mdb_add) + return -EOPNOTSUPP; - /* clear port */ - alu.port_forward &= ~BIT(port); - if (!alu.port_forward) - alu.is_static = false; - dev->dev_ops->w_sta_mac_table(dev, index, &alu); + return dev->dev_ops->mdb_add(dev, port, mdb, db); +} -exit: - return 0; +static int ksz_port_mdb_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->mdb_del) + return -EOPNOTSUPP; + + return dev->dev_ops->mdb_del(dev, port, mdb, db); } -EXPORT_SYMBOL_GPL(ksz_port_mdb_del); -int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) +static int ksz_enable_port(struct dsa_switch *ds, int port, + struct phy_device *phy) { struct ksz_device *dev = ds->priv; @@ -343,7 +2301,537 @@ int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy) return 0; } -EXPORT_SYMBOL_GPL(ksz_enable_port); + +void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *p; + const u16 *regs; + u8 data; + + regs = dev->info->regs; + + ksz_pread8(dev, port, regs[P_STP_CTRL], &data); + data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); + + p = &dev->ports[port]; + + switch (state) { + case BR_STATE_DISABLED: + data |= PORT_LEARN_DISABLE; + break; + case BR_STATE_LISTENING: + data |= (PORT_RX_ENABLE | PORT_LEARN_DISABLE); + break; + case BR_STATE_LEARNING: + data |= PORT_RX_ENABLE; + if (!p->learning) + data |= PORT_LEARN_DISABLE; + break; + case BR_STATE_FORWARDING: + data |= (PORT_TX_ENABLE | PORT_RX_ENABLE); + if (!p->learning) + data |= PORT_LEARN_DISABLE; + break; + case BR_STATE_BLOCKING: + data |= PORT_LEARN_DISABLE; + break; + default: + dev_err(ds->dev, "invalid STP state: %d\n", state); + return; + } + + ksz_pwrite8(dev, port, regs[P_STP_CTRL], data); + + p->stp_state = state; + + ksz_update_port_member(dev, port); +} + +static int ksz_port_pre_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~BR_LEARNING) + return -EINVAL; + + return 0; +} + +static int ksz_port_bridge_flags(struct dsa_switch *ds, int port, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct ksz_device *dev = ds->priv; + struct ksz_port *p = &dev->ports[port]; + + if (flags.mask & BR_LEARNING) { + p->learning = !!(flags.val & BR_LEARNING); + + /* Make the change take effect immediately */ + ksz_port_stp_state_set(ds, port, p->stp_state); + } + + return 0; +} + +static enum dsa_tag_protocol ksz_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol mp) +{ + struct ksz_device *dev = ds->priv; + enum dsa_tag_protocol proto = DSA_TAG_PROTO_NONE; + + if (dev->chip_id == KSZ8795_CHIP_ID || + dev->chip_id == KSZ8794_CHIP_ID || + dev->chip_id == KSZ8765_CHIP_ID) + proto = DSA_TAG_PROTO_KSZ8795; + + if (dev->chip_id == KSZ8830_CHIP_ID || + dev->chip_id == KSZ8563_CHIP_ID || + dev->chip_id == KSZ9893_CHIP_ID) + proto = DSA_TAG_PROTO_KSZ9893; + + if (dev->chip_id == KSZ9477_CHIP_ID || + dev->chip_id == KSZ9896_CHIP_ID || + dev->chip_id == KSZ9897_CHIP_ID || + dev->chip_id == KSZ9567_CHIP_ID) + proto = DSA_TAG_PROTO_KSZ9477; + + if (is_lan937x(dev)) + proto = DSA_TAG_PROTO_LAN937X_VALUE; + + return proto; +} + +static int ksz_port_vlan_filtering(struct dsa_switch *ds, int port, + bool flag, struct netlink_ext_ack *extack) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->vlan_filtering) + return -EOPNOTSUPP; + + return dev->dev_ops->vlan_filtering(dev, port, flag, extack); +} + +static int ksz_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->vlan_add) + return -EOPNOTSUPP; + + return dev->dev_ops->vlan_add(dev, port, vlan, extack); +} + +static int ksz_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->vlan_del) + return -EOPNOTSUPP; + + return dev->dev_ops->vlan_del(dev, port, vlan); +} + +static int ksz_port_mirror_add(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->mirror_add) + return -EOPNOTSUPP; + + return dev->dev_ops->mirror_add(dev, port, mirror, ingress, extack); +} + +static void ksz_port_mirror_del(struct dsa_switch *ds, int port, + struct dsa_mall_mirror_tc_entry *mirror) +{ + struct ksz_device *dev = ds->priv; + + if (dev->dev_ops->mirror_del) + dev->dev_ops->mirror_del(dev, port, mirror); +} + +static int ksz_change_mtu(struct dsa_switch *ds, int port, int mtu) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->change_mtu) + return -EOPNOTSUPP; + + return dev->dev_ops->change_mtu(dev, port, mtu); +} + +static int ksz_max_mtu(struct dsa_switch *ds, int port) +{ + struct ksz_device *dev = ds->priv; + + if (!dev->dev_ops->max_mtu) + return -EOPNOTSUPP; + + return dev->dev_ops->max_mtu(dev, port); +} + +static void ksz_set_xmii(struct ksz_device *dev, int port, + phy_interface_t interface) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + struct ksz_port *p = &dev->ports[port]; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + data8 &= ~(P_MII_SEL_M | P_RGMII_ID_IG_ENABLE | + P_RGMII_ID_EG_ENABLE); + + switch (interface) { + case PHY_INTERFACE_MODE_MII: + data8 |= bitval[P_MII_SEL]; + break; + case PHY_INTERFACE_MODE_RMII: + data8 |= bitval[P_RMII_SEL]; + break; + case PHY_INTERFACE_MODE_GMII: + data8 |= bitval[P_GMII_SEL]; + break; + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_RGMII_TXID: + case PHY_INTERFACE_MODE_RGMII_RXID: + data8 |= bitval[P_RGMII_SEL]; + /* On KSZ9893, disable RGMII in-band status support */ + if (dev->chip_id == KSZ9893_CHIP_ID || + dev->chip_id == KSZ8563_CHIP_ID) + data8 &= ~P_MII_MAC_MODE; + break; + default: + dev_err(dev->dev, "Unsupported interface '%s' for port %d\n", + phy_modes(interface), port); + return; + } + + if (p->rgmii_tx_val) + data8 |= P_RGMII_ID_EG_ENABLE; + + if (p->rgmii_rx_val) + data8 |= P_RGMII_ID_IG_ENABLE; + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); +} + +phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + phy_interface_t interface; + u8 data8; + u8 val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_MII_SEL_M, data8); + + if (val == bitval[P_MII_SEL]) { + if (gbit) + interface = PHY_INTERFACE_MODE_GMII; + else + interface = PHY_INTERFACE_MODE_MII; + } else if (val == bitval[P_RMII_SEL]) { + interface = PHY_INTERFACE_MODE_RGMII; + } else { + interface = PHY_INTERFACE_MODE_RGMII; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_TXID; + if (data8 & P_RGMII_ID_IG_ENABLE) { + interface = PHY_INTERFACE_MODE_RGMII_RXID; + if (data8 & P_RGMII_ID_EG_ENABLE) + interface = PHY_INTERFACE_MODE_RGMII_ID; + } + } + + return interface; +} + +static void ksz_phylink_mac_config(struct dsa_switch *ds, int port, + unsigned int mode, + const struct phylink_link_state *state) +{ + struct ksz_device *dev = ds->priv; + + if (ksz_is_ksz88x3(dev)) + return; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return; + + if (phylink_autoneg_inband(mode)) { + dev_err(dev->dev, "In-band AN not supported!\n"); + return; + } + + ksz_set_xmii(dev, port, state->interface); + + if (dev->dev_ops->phylink_mac_config) + dev->dev_ops->phylink_mac_config(dev, port, mode, state); + + if (dev->dev_ops->setup_rgmii_delay) + dev->dev_ops->setup_rgmii_delay(dev, port); +} + +bool ksz_get_gbit(struct ksz_device *dev, int port) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + bool gbit = false; + u8 data8; + bool val; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + val = FIELD_GET(P_GMII_1GBIT_M, data8); + + if (val == bitval[P_GMII_1GBIT]) + gbit = true; + + return gbit; +} + +static void ksz_set_gbit(struct ksz_device *dev, int port, bool gbit) +{ + const u8 *bitval = dev->info->xmii_ctrl1; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_1], &data8); + + data8 &= ~P_GMII_1GBIT_M; + + if (gbit) + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_1GBIT]); + else + data8 |= FIELD_PREP(P_GMII_1GBIT_M, bitval[P_GMII_NOT_1GBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_1], data8); +} + +static void ksz_set_100_10mbit(struct ksz_device *dev, int port, int speed) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u16 *regs = dev->info->regs; + u8 data8; + + ksz_pread8(dev, port, regs[P_XMII_CTRL_0], &data8); + + data8 &= ~P_MII_100MBIT_M; + + if (speed == SPEED_100) + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_100MBIT]); + else + data8 |= FIELD_PREP(P_MII_100MBIT_M, bitval[P_MII_10MBIT]); + + /* Write the updated value */ + ksz_pwrite8(dev, port, regs[P_XMII_CTRL_0], data8); +} + +static void ksz_port_set_xmii_speed(struct ksz_device *dev, int port, int speed) +{ + if (speed == SPEED_1000) + ksz_set_gbit(dev, port, true); + else + ksz_set_gbit(dev, port, false); + + if (speed == SPEED_100 || speed == SPEED_10) + ksz_set_100_10mbit(dev, port, speed); +} + +static void ksz_duplex_flowctrl(struct ksz_device *dev, int port, int duplex, + bool tx_pause, bool rx_pause) +{ + const u8 *bitval = dev->info->xmii_ctrl0; + const u32 *masks = dev->info->masks; + const u16 *regs = dev->info->regs; + u8 mask; + u8 val; + + mask = P_MII_DUPLEX_M | masks[P_MII_TX_FLOW_CTRL] | + masks[P_MII_RX_FLOW_CTRL]; + + if (duplex == DUPLEX_FULL) + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_FULL_DUPLEX]); + else + val = FIELD_PREP(P_MII_DUPLEX_M, bitval[P_MII_HALF_DUPLEX]); + + if (tx_pause) + val |= masks[P_MII_TX_FLOW_CTRL]; + + if (rx_pause) + val |= masks[P_MII_RX_FLOW_CTRL]; + + ksz_prmw8(dev, port, regs[P_XMII_CTRL_0], mask, val); +} + +static void ksz9477_phylink_mac_link_up(struct ksz_device *dev, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, + bool rx_pause) +{ + struct ksz_port *p; + + p = &dev->ports[port]; + + /* Internal PHYs */ + if (dev->info->internal_phy[port]) + return; + + p->phydev.speed = speed; + + ksz_port_set_xmii_speed(dev, port, speed); + + ksz_duplex_flowctrl(dev, port, duplex, tx_pause, rx_pause); +} + +static void ksz_phylink_mac_link_up(struct dsa_switch *ds, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, bool rx_pause) +{ + struct ksz_device *dev = ds->priv; + + if (dev->dev_ops->phylink_mac_link_up) + dev->dev_ops->phylink_mac_link_up(dev, port, mode, interface, + phydev, speed, duplex, + tx_pause, rx_pause); +} + +static int ksz_switch_detect(struct ksz_device *dev) +{ + u8 id1, id2, id4; + u16 id16; + u32 id32; + int ret; + + /* read chip id */ + ret = ksz_read16(dev, REG_CHIP_ID0, &id16); + if (ret) + return ret; + + id1 = FIELD_GET(SW_FAMILY_ID_M, id16); + id2 = FIELD_GET(SW_CHIP_ID_M, id16); + + switch (id1) { + case KSZ87_FAMILY_ID: + if (id2 == KSZ87_CHIP_ID_95) { + u8 val; + + dev->chip_id = KSZ8795_CHIP_ID; + + ksz_read8(dev, KSZ8_PORT_STATUS_0, &val); + if (val & KSZ8_PORT_FIBER_MODE) + dev->chip_id = KSZ8765_CHIP_ID; + } else if (id2 == KSZ87_CHIP_ID_94) { + dev->chip_id = KSZ8794_CHIP_ID; + } else { + return -ENODEV; + } + break; + case KSZ88_FAMILY_ID: + if (id2 == KSZ88_CHIP_ID_63) + dev->chip_id = KSZ8830_CHIP_ID; + else + return -ENODEV; + break; + default: + ret = ksz_read32(dev, REG_CHIP_ID0, &id32); + if (ret) + return ret; + + dev->chip_rev = FIELD_GET(SW_REV_ID_M, id32); + id32 &= ~0xFF; + + switch (id32) { + case KSZ9477_CHIP_ID: + case KSZ9896_CHIP_ID: + case KSZ9897_CHIP_ID: + case KSZ9567_CHIP_ID: + case LAN9370_CHIP_ID: + case LAN9371_CHIP_ID: + case LAN9372_CHIP_ID: + case LAN9373_CHIP_ID: + case LAN9374_CHIP_ID: + dev->chip_id = id32; + break; + case KSZ9893_CHIP_ID: + ret = ksz_read8(dev, REG_CHIP_ID4, + &id4); + if (ret) + return ret; + + if (id4 == SKU_ID_KSZ8563) + dev->chip_id = KSZ8563_CHIP_ID; + else + dev->chip_id = KSZ9893_CHIP_ID; + + break; + default: + dev_err(dev->dev, + "unsupported switch detected %x)\n", id32); + return -ENODEV; + } + } + return 0; +} + +static const struct dsa_switch_ops ksz_switch_ops = { + .get_tag_protocol = ksz_get_tag_protocol, + .get_phy_flags = ksz_get_phy_flags, + .setup = ksz_setup, + .teardown = ksz_teardown, + .phy_read = ksz_phy_read16, + .phy_write = ksz_phy_write16, + .phylink_get_caps = ksz_phylink_get_caps, + .phylink_mac_config = ksz_phylink_mac_config, + .phylink_mac_link_up = ksz_phylink_mac_link_up, + .phylink_mac_link_down = ksz_mac_link_down, + .port_enable = ksz_enable_port, + .set_ageing_time = ksz_set_ageing_time, + .get_strings = ksz_get_strings, + .get_ethtool_stats = ksz_get_ethtool_stats, + .get_sset_count = ksz_sset_count, + .port_bridge_join = ksz_port_bridge_join, + .port_bridge_leave = ksz_port_bridge_leave, + .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 = ksz_port_fast_age, + .port_vlan_filtering = ksz_port_vlan_filtering, + .port_vlan_add = ksz_port_vlan_add, + .port_vlan_del = ksz_port_vlan_del, + .port_fdb_dump = ksz_port_fdb_dump, + .port_fdb_add = ksz_port_fdb_add, + .port_fdb_del = ksz_port_fdb_del, + .port_mdb_add = ksz_port_mdb_add, + .port_mdb_del = ksz_port_mdb_del, + .port_mirror_add = ksz_port_mirror_add, + .port_mirror_del = ksz_port_mirror_del, + .get_stats64 = ksz_get_stats64, + .get_pause_stats = ksz_get_pause_stats, + .port_change_mtu = ksz_change_mtu, + .port_max_mtu = ksz_max_mtu, +}; struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) { @@ -356,6 +2844,7 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) ds->dev = base; ds->num_ports = DSA_MAX_PORTS; + ds->ops = &ksz_switch_ops; swdev = devm_kzalloc(base, sizeof(*swdev), GFP_KERNEL); if (!swdev) @@ -371,13 +2860,51 @@ struct ksz_device *ksz_switch_alloc(struct device *base, void *priv) } EXPORT_SYMBOL(ksz_switch_alloc); -int ksz_switch_register(struct ksz_device *dev, - const struct ksz_dev_ops *ops) +static void ksz_parse_rgmii_delay(struct ksz_device *dev, int port_num, + struct device_node *port_dn) { + phy_interface_t phy_mode = dev->ports[port_num].interface; + int rx_delay = -1, tx_delay = -1; + + if (!phy_interface_mode_is_rgmii(phy_mode)) + return; + + of_property_read_u32(port_dn, "rx-internal-delay-ps", &rx_delay); + of_property_read_u32(port_dn, "tx-internal-delay-ps", &tx_delay); + + if (rx_delay == -1 && tx_delay == -1) { + dev_warn(dev->dev, + "Port %d interpreting RGMII delay settings based on \"phy-mode\" property, " + "please update device tree to specify \"rx-internal-delay-ps\" and " + "\"tx-internal-delay-ps\"", + port_num); + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_RXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + rx_delay = 2000; + + if (phy_mode == PHY_INTERFACE_MODE_RGMII_TXID || + phy_mode == PHY_INTERFACE_MODE_RGMII_ID) + tx_delay = 2000; + } + + if (rx_delay < 0) + rx_delay = 0; + if (tx_delay < 0) + tx_delay = 0; + + dev->ports[port_num].rgmii_rx_val = rx_delay; + dev->ports[port_num].rgmii_tx_val = tx_delay; +} + +int ksz_switch_register(struct ksz_device *dev) +{ + const struct ksz_chip_data *info; struct device_node *port, *ports; phy_interface_t interface; unsigned int port_num; int ret; + int i; if (dev->pdata) dev->chip_id = dev->pdata->chip_id; @@ -399,19 +2926,57 @@ int ksz_switch_register(struct ksz_device *dev, mutex_init(&dev->alu_mutex); mutex_init(&dev->vlan_mutex); - dev->dev_ops = ops; + ret = ksz_switch_detect(dev); + if (ret) + return ret; - if (dev->dev_ops->detect(dev)) - return -EINVAL; + info = ksz_lookup_info(dev->chip_id); + if (!info) + return -ENODEV; + + /* Update the compatible info with the probed one */ + dev->info = info; + + dev_info(dev->dev, "found switch: %s, rev %i\n", + dev->info->dev_name, dev->chip_rev); + + ret = ksz_check_device_id(dev); + if (ret) + return ret; + + dev->dev_ops = dev->info->ops; ret = dev->dev_ops->init(dev); if (ret) return ret; + dev->ports = devm_kzalloc(dev->dev, + dev->info->port_cnt * sizeof(struct ksz_port), + GFP_KERNEL); + if (!dev->ports) + return -ENOMEM; + + for (i = 0; i < dev->info->port_cnt; i++) { + spin_lock_init(&dev->ports[i].mib.stats64_lock); + mutex_init(&dev->ports[i].mib.cnt_mutex); + dev->ports[i].mib.counters = + devm_kzalloc(dev->dev, + sizeof(u64) * (dev->info->mib_cnt + 1), + GFP_KERNEL); + if (!dev->ports[i].mib.counters) + return -ENOMEM; + + dev->ports[i].ksz_dev = dev; + dev->ports[i].num = i; + } + + /* set the real number of ports */ + dev->ds->num_ports = dev->info->port_cnt; + /* Host port interface will be self detected, or specifically set in * device tree. */ - for (port_num = 0; port_num < dev->port_cnt; ++port_num) + for (port_num = 0; port_num < dev->info->port_cnt; ++port_num) dev->ports[port_num].interface = PHY_INTERFACE_MODE_NA; if (dev->dev->of_node) { ret = of_get_phy_mode(dev->dev->of_node, &interface); @@ -420,20 +2985,31 @@ int ksz_switch_register(struct ksz_device *dev, ports = of_get_child_by_name(dev->dev->of_node, "ethernet-ports"); if (!ports) ports = of_get_child_by_name(dev->dev->of_node, "ports"); - if (ports) + if (ports) { for_each_available_child_of_node(ports, port) { if (of_property_read_u32(port, "reg", &port_num)) continue; if (!(dev->port_mask & BIT(port_num))) { of_node_put(port); + of_node_put(ports); return -EINVAL; } of_get_phy_mode(port, &dev->ports[port_num].interface); + + ksz_parse_rgmii_delay(dev, port_num, port); } + of_node_put(ports); + } dev->synclko_125 = of_property_read_bool(dev->dev->of_node, "microchip,synclko-125"); + dev->synclko_disable = of_property_read_bool(dev->dev->of_node, + "microchip,synclko-disable"); + if (dev->synclko_125 && dev->synclko_disable) { + dev_err(dev->dev, "inconsistent synclko settings\n"); + return -EINVAL; + } } ret = dsa_register_switch(dev->ds); @@ -443,12 +3019,12 @@ int ksz_switch_register(struct ksz_device *dev, } /* Read MIB counters every 30 seconds to avoid overflow. */ - dev->mib_read_interval = msecs_to_jiffies(30000); + dev->mib_read_interval = msecs_to_jiffies(5000); /* Start the MIB timer. */ schedule_delayed_work(&dev->mib_read, 0); - return 0; + return ret; } EXPORT_SYMBOL(ksz_switch_register); diff --git a/drivers/net/dsa/microchip/ksz_common.h b/drivers/net/dsa/microchip/ksz_common.h index df8ae59c8525..9cfa179575ce 100644 --- a/drivers/net/dsa/microchip/ksz_common.h +++ b/drivers/net/dsa/microchip/ksz_common.h @@ -13,6 +13,11 @@ #include <linux/phy.h> #include <linux/regmap.h> #include <net/dsa.h> +#include <linux/irq.h> + +#define KSZ_MAX_NUM_PORTS 8 + +struct ksz_device; struct vlan_table { u32 table[3]; @@ -22,29 +27,86 @@ struct ksz_port_mib { struct mutex cnt_mutex; /* structure access */ u8 cnt_ptr; u64 *counters; + struct rtnl_link_stats64 stats64; + struct ethtool_pause_stats pause_stats; + struct spinlock stats64_lock; +}; + +struct ksz_mib_names { + int index; + char string[ETH_GSTRING_LEN]; +}; + +struct ksz_chip_data { + u32 chip_id; + const char *dev_name; + int num_vlans; + int num_alus; + int num_statics; + int cpu_ports; + int port_cnt; + u8 port_nirqs; + const struct ksz_dev_ops *ops; + bool phy_errata_9477; + bool ksz87xx_eee_link_erratum; + const struct ksz_mib_names *mib_names; + int mib_cnt; + u8 reg_mib_cnt; + const u16 *regs; + const u32 *masks; + const u8 *shifts; + const u8 *xmii_ctrl0; + const u8 *xmii_ctrl1; + int stp_ctrl_reg; + int broadcast_ctrl_reg; + int multicast_ctrl_reg; + int start_ctrl_reg; + bool supports_mii[KSZ_MAX_NUM_PORTS]; + bool supports_rmii[KSZ_MAX_NUM_PORTS]; + bool supports_rgmii[KSZ_MAX_NUM_PORTS]; + bool internal_phy[KSZ_MAX_NUM_PORTS]; + bool gbit_capable[KSZ_MAX_NUM_PORTS]; + const struct regmap_access_table *wr_table; + const struct regmap_access_table *rd_table; +}; + +struct ksz_irq { + u16 masked; + u16 reg_mask; + u16 reg_status; + struct irq_domain *domain; + int nirqs; + int irq_num; + char name[16]; + struct ksz_device *dev; }; struct ksz_port { bool remove_tag; /* Remove Tag flag set, for ksz8795 only */ + bool learning; int stp_state; struct phy_device phydev; u32 on:1; /* port is not disabled by hardware */ - u32 phy:1; /* port has a PHY */ u32 fiber:1; /* port is fiber */ - u32 sgmii:1; /* port is SGMII */ u32 force:1; u32 read:1; /* read MIB counters in background */ u32 freeze:1; /* MIB counter freeze is enabled */ struct ksz_port_mib mib; phy_interface_t interface; + u16 max_frame; + u32 rgmii_tx_val; + u32 rgmii_rx_val; + struct ksz_device *ksz_dev; + struct ksz_irq pirq; + u8 num; }; struct ksz_device { struct dsa_switch *ds; struct ksz_platform_data *pdata; - const char *name; + const struct ksz_chip_data *info; struct mutex dev_mutex; /* device access */ struct mutex regmap_mutex; /* regmap access */ @@ -56,25 +118,18 @@ struct ksz_device { struct regmap *regmap[3]; void *priv; + int irq; struct gpio_desc *reset_gpio; /* Optional reset GPIO */ /* chip specific data */ u32 chip_id; - int num_vlans; - int num_alus; - int num_statics; + u8 chip_rev; int cpu_port; /* port connected to CPU */ - int cpu_ports; /* port bitmap can be cpu port */ int phy_port_cnt; - int port_cnt; - u8 reg_mib_cnt; - int mib_cnt; - const struct mib_names *mib_names; phy_interface_t compat_interface; - u32 regs_size; - bool phy_errata_9477; bool synclko_125; + bool synclko_disable; struct vlan_table *vlan_cache; @@ -83,10 +138,124 @@ struct ksz_device { unsigned long mib_read_interval; u16 mirror_rx; u16 mirror_tx; - u32 features; /* chip specific features */ - u32 overrides; /* chip functions set by user */ - u16 host_mask; u16 port_mask; + struct mutex lock_irq; /* IRQ Access */ + struct ksz_irq girq; +}; + +/* List of supported models */ +enum ksz_model { + KSZ8563, + KSZ8795, + KSZ8794, + KSZ8765, + KSZ8830, + KSZ9477, + KSZ9896, + KSZ9897, + KSZ9893, + KSZ9567, + LAN9370, + LAN9371, + LAN9372, + LAN9373, + LAN9374, +}; + +enum ksz_chip_id { + KSZ8563_CHIP_ID = 0x8563, + KSZ8795_CHIP_ID = 0x8795, + KSZ8794_CHIP_ID = 0x8794, + KSZ8765_CHIP_ID = 0x8765, + KSZ8830_CHIP_ID = 0x8830, + KSZ9477_CHIP_ID = 0x00947700, + KSZ9896_CHIP_ID = 0x00989600, + KSZ9897_CHIP_ID = 0x00989700, + KSZ9893_CHIP_ID = 0x00989300, + KSZ9567_CHIP_ID = 0x00956700, + LAN9370_CHIP_ID = 0x00937000, + LAN9371_CHIP_ID = 0x00937100, + LAN9372_CHIP_ID = 0x00937200, + LAN9373_CHIP_ID = 0x00937300, + LAN9374_CHIP_ID = 0x00937400, +}; + +enum ksz_regs { + REG_IND_CTRL_0, + REG_IND_DATA_8, + REG_IND_DATA_CHECK, + REG_IND_DATA_HI, + REG_IND_DATA_LO, + REG_IND_MIB_CHECK, + REG_IND_BYTE, + P_FORCE_CTRL, + P_LINK_STATUS, + P_LOCAL_CTRL, + P_NEG_RESTART_CTRL, + P_REMOTE_STATUS, + P_SPEED_STATUS, + S_TAIL_TAG_CTRL, + P_STP_CTRL, + S_START_CTRL, + S_BROADCAST_CTRL, + S_MULTICAST_CTRL, + P_XMII_CTRL_0, + P_XMII_CTRL_1, +}; + +enum ksz_masks { + PORT_802_1P_REMAPPING, + SW_TAIL_TAG_ENABLE, + MIB_COUNTER_OVERFLOW, + MIB_COUNTER_VALID, + VLAN_TABLE_FID, + VLAN_TABLE_MEMBERSHIP, + VLAN_TABLE_VALID, + STATIC_MAC_TABLE_VALID, + STATIC_MAC_TABLE_USE_FID, + STATIC_MAC_TABLE_FID, + STATIC_MAC_TABLE_OVERRIDE, + STATIC_MAC_TABLE_FWD_PORTS, + DYNAMIC_MAC_TABLE_ENTRIES_H, + DYNAMIC_MAC_TABLE_MAC_EMPTY, + DYNAMIC_MAC_TABLE_NOT_READY, + DYNAMIC_MAC_TABLE_ENTRIES, + DYNAMIC_MAC_TABLE_FID, + DYNAMIC_MAC_TABLE_SRC_PORT, + DYNAMIC_MAC_TABLE_TIMESTAMP, + ALU_STAT_WRITE, + ALU_STAT_READ, + P_MII_TX_FLOW_CTRL, + P_MII_RX_FLOW_CTRL, +}; + +enum ksz_shifts { + VLAN_TABLE_MEMBERSHIP_S, + VLAN_TABLE, + STATIC_MAC_FWD_PORTS, + STATIC_MAC_FID, + DYNAMIC_MAC_ENTRIES_H, + DYNAMIC_MAC_ENTRIES, + DYNAMIC_MAC_FID, + DYNAMIC_MAC_TIMESTAMP, + DYNAMIC_MAC_SRC_PORT, + ALU_STAT_INDEX, +}; + +enum ksz_xmii_ctrl0 { + P_MII_100MBIT, + P_MII_10MBIT, + P_MII_FULL_DUPLEX, + P_MII_HALF_DUPLEX, +}; + +enum ksz_xmii_ctrl1 { + P_RGMII_SEL, + P_RMII_SEL, + P_GMII_SEL, + P_MII_SEL, + P_GMII_1GBIT, + P_GMII_NOT_1GBIT, }; struct alu_struct { @@ -109,63 +278,77 @@ struct alu_struct { }; struct ksz_dev_ops { + int (*setup)(struct dsa_switch *ds); + void (*teardown)(struct dsa_switch *ds); u32 (*get_port_addr)(int port, int offset); void (*cfg_port_member)(struct ksz_device *dev, int port, u8 member); void (*flush_dyn_mac_table)(struct ksz_device *dev, int port); void (*port_cleanup)(struct ksz_device *dev, int port); void (*port_setup)(struct ksz_device *dev, int port, bool cpu_port); - void (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); - void (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val); - int (*r_dyn_mac_table)(struct ksz_device *dev, u16 addr, u8 *mac_addr, - u8 *fid, u8 *src_port, u8 *timestamp, - u16 *entries); - int (*r_sta_mac_table)(struct ksz_device *dev, u16 addr, - struct alu_struct *alu); - void (*w_sta_mac_table)(struct ksz_device *dev, u16 addr, - struct alu_struct *alu); + int (*set_ageing_time)(struct ksz_device *dev, unsigned int msecs); + int (*r_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 *val); + int (*w_phy)(struct ksz_device *dev, u16 phy, u16 reg, u16 val); void (*r_mib_cnt)(struct ksz_device *dev, int port, u16 addr, u64 *cnt); void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr, u64 *dropped, u64 *cnt); + void (*r_mib_stat64)(struct ksz_device *dev, int port); + int (*vlan_filtering)(struct ksz_device *dev, int port, + bool flag, struct netlink_ext_ack *extack); + int (*vlan_add)(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack); + int (*vlan_del)(struct ksz_device *dev, int port, + const struct switchdev_obj_port_vlan *vlan); + int (*mirror_add)(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror, + bool ingress, struct netlink_ext_ack *extack); + void (*mirror_del)(struct ksz_device *dev, int port, + struct dsa_mall_mirror_tc_entry *mirror); + int (*fdb_add)(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db); + int (*fdb_del)(struct ksz_device *dev, int port, + const unsigned char *addr, u16 vid, struct dsa_db db); + int (*fdb_dump)(struct ksz_device *dev, int port, + dsa_fdb_dump_cb_t *cb, void *data); + int (*mdb_add)(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); + int (*mdb_del)(struct ksz_device *dev, int port, + const struct switchdev_obj_port_mdb *mdb, + struct dsa_db db); + void (*get_caps)(struct ksz_device *dev, int port, + struct phylink_config *config); + int (*change_mtu)(struct ksz_device *dev, int port, int mtu); + int (*max_mtu)(struct ksz_device *dev, int port); void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze); void (*port_init_cnt)(struct ksz_device *dev, int port); - int (*shutdown)(struct ksz_device *dev); - int (*detect)(struct ksz_device *dev); + void (*phylink_mac_config)(struct ksz_device *dev, int port, + unsigned int mode, + const struct phylink_link_state *state); + void (*phylink_mac_link_up)(struct ksz_device *dev, int port, + unsigned int mode, + phy_interface_t interface, + struct phy_device *phydev, int speed, + int duplex, bool tx_pause, bool rx_pause); + void (*setup_rgmii_delay)(struct ksz_device *dev, int port); + void (*config_cpu_port)(struct dsa_switch *ds); + int (*enable_stp_addr)(struct ksz_device *dev); + int (*reset)(struct ksz_device *dev); int (*init)(struct ksz_device *dev); void (*exit)(struct ksz_device *dev); }; struct ksz_device *ksz_switch_alloc(struct device *base, void *priv); -int ksz_switch_register(struct ksz_device *dev, - const struct ksz_dev_ops *ops); +int ksz_switch_register(struct ksz_device *dev); void ksz_switch_remove(struct ksz_device *dev); -int ksz8_switch_register(struct ksz_device *dev); -int ksz9477_switch_register(struct ksz_device *dev); - -void ksz_update_port_member(struct ksz_device *dev, int port); void ksz_init_mib_timer(struct ksz_device *dev); - -/* Common DSA access functions */ - -int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg); -int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val); -void ksz_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, - phy_interface_t interface); -int ksz_sset_count(struct dsa_switch *ds, int port, int sset); -void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf); -int ksz_port_bridge_join(struct dsa_switch *ds, int port, - struct dsa_bridge bridge, bool *tx_fwd_offload); -void ksz_port_bridge_leave(struct dsa_switch *ds, int port, - struct dsa_bridge bridge); -void ksz_port_fast_age(struct dsa_switch *ds, int port); -int ksz_port_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, - void *data); -int ksz_port_mdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); -int ksz_port_mdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_mdb *mdb); -int ksz_enable_port(struct dsa_switch *ds, int port, struct phy_device *phy); +void ksz_r_mib_stats64(struct ksz_device *dev, int port); +void ksz_port_stp_state_set(struct dsa_switch *ds, int port, u8 state); +bool ksz_get_gbit(struct ksz_device *dev, int port); +phy_interface_t ksz_get_xmii(struct ksz_device *dev, int port, bool gbit); +extern const struct ksz_chip_data ksz_switch_chips[]; /* Common register access functions */ @@ -174,6 +357,10 @@ static inline int ksz_read8(struct ksz_device *dev, u32 reg, u8 *val) unsigned int value; int ret = regmap_read(dev->regmap[0], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 8bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -183,6 +370,10 @@ static inline int ksz_read16(struct ksz_device *dev, u32 reg, u16 *val) unsigned int value; int ret = regmap_read(dev->regmap[1], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 16bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -192,6 +383,10 @@ static inline int ksz_read32(struct ksz_device *dev, u32 reg, u32 *val) unsigned int value; int ret = regmap_read(dev->regmap[2], reg, &value); + if (ret) + dev_err(dev->dev, "can't read 32bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + *val = value; return ret; } @@ -202,7 +397,10 @@ static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val) int ret; ret = regmap_bulk_read(dev->regmap[2], reg, value, 2); - if (!ret) + if (ret) + dev_err(dev->dev, "can't read 64bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + else *val = (u64)value[0] << 32 | value[1]; return ret; @@ -210,17 +408,38 @@ static inline int ksz_read64(struct ksz_device *dev, u32 reg, u64 *val) static inline int ksz_write8(struct ksz_device *dev, u32 reg, u8 value) { - return regmap_write(dev->regmap[0], reg, value); + int ret; + + ret = regmap_write(dev->regmap[0], reg, value); + if (ret) + dev_err(dev->dev, "can't write 8bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write16(struct ksz_device *dev, u32 reg, u16 value) { - return regmap_write(dev->regmap[1], reg, value); + int ret; + + ret = regmap_write(dev->regmap[1], reg, value); + if (ret) + dev_err(dev->dev, "can't write 16bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write32(struct ksz_device *dev, u32 reg, u32 value) { - return regmap_write(dev->regmap[2], reg, value); + int ret; + + ret = regmap_write(dev->regmap[2], reg, value); + if (ret) + dev_err(dev->dev, "can't write 32bit reg: 0x%x %pe\n", reg, + ERR_PTR(ret)); + + return ret; } static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value) @@ -235,40 +454,50 @@ static inline int ksz_write64(struct ksz_device *dev, u32 reg, u64 value) return regmap_bulk_write(dev->regmap[2], reg, val, 2); } -static inline void ksz_pread8(struct ksz_device *dev, int port, int offset, - u8 *data) +static inline int ksz_pread8(struct ksz_device *dev, int port, int offset, + u8 *data) +{ + return ksz_read8(dev, dev->dev_ops->get_port_addr(port, offset), data); +} + +static inline int ksz_pread16(struct ksz_device *dev, int port, int offset, + u16 *data) { - ksz_read8(dev, dev->dev_ops->get_port_addr(port, offset), data); + return ksz_read16(dev, dev->dev_ops->get_port_addr(port, offset), data); } -static inline void ksz_pread16(struct ksz_device *dev, int port, int offset, - u16 *data) +static inline int ksz_pread32(struct ksz_device *dev, int port, int offset, + u32 *data) { - ksz_read16(dev, dev->dev_ops->get_port_addr(port, offset), data); + return ksz_read32(dev, dev->dev_ops->get_port_addr(port, offset), data); } -static inline void ksz_pread32(struct ksz_device *dev, int port, int offset, - u32 *data) +static inline int ksz_pwrite8(struct ksz_device *dev, int port, int offset, + u8 data) { - ksz_read32(dev, dev->dev_ops->get_port_addr(port, offset), data); + return ksz_write8(dev, dev->dev_ops->get_port_addr(port, offset), data); } -static inline void ksz_pwrite8(struct ksz_device *dev, int port, int offset, - u8 data) +static inline int ksz_pwrite16(struct ksz_device *dev, int port, int offset, + u16 data) { - ksz_write8(dev, dev->dev_ops->get_port_addr(port, offset), data); + return ksz_write16(dev, dev->dev_ops->get_port_addr(port, offset), + data); } -static inline void ksz_pwrite16(struct ksz_device *dev, int port, int offset, - u16 data) +static inline int ksz_pwrite32(struct ksz_device *dev, int port, int offset, + u32 data) { - ksz_write16(dev, dev->dev_ops->get_port_addr(port, offset), data); + return ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset), + data); } -static inline void ksz_pwrite32(struct ksz_device *dev, int port, int offset, - u32 data) +static inline void ksz_prmw8(struct ksz_device *dev, int port, int offset, + u8 mask, u8 val) { - ksz_write32(dev, dev->dev_ops->get_port_addr(port, offset), data); + regmap_update_bits(dev->regmap[0], + dev->dev_ops->get_port_addr(port, offset), + mask, val); } static inline void ksz_regmap_lock(void *__mtx) @@ -283,6 +512,79 @@ static inline void ksz_regmap_unlock(void *__mtx) mutex_unlock(mtx); } +static inline bool ksz_is_ksz88x3(struct ksz_device *dev) +{ + return dev->chip_id == KSZ8830_CHIP_ID; +} + +static inline int is_lan937x(struct ksz_device *dev) +{ + return dev->chip_id == LAN9370_CHIP_ID || + dev->chip_id == LAN9371_CHIP_ID || + dev->chip_id == LAN9372_CHIP_ID || + dev->chip_id == LAN9373_CHIP_ID || + dev->chip_id == LAN9374_CHIP_ID; +} + +/* STP State Defines */ +#define PORT_TX_ENABLE BIT(2) +#define PORT_RX_ENABLE BIT(1) +#define PORT_LEARN_DISABLE BIT(0) + +/* Switch ID Defines */ +#define REG_CHIP_ID0 0x00 + +#define SW_FAMILY_ID_M GENMASK(15, 8) +#define KSZ87_FAMILY_ID 0x87 +#define KSZ88_FAMILY_ID 0x88 + +#define KSZ8_PORT_STATUS_0 0x08 +#define KSZ8_PORT_FIBER_MODE BIT(7) + +#define SW_CHIP_ID_M GENMASK(7, 4) +#define KSZ87_CHIP_ID_94 0x6 +#define KSZ87_CHIP_ID_95 0x9 +#define KSZ88_CHIP_ID_63 0x3 + +#define SW_REV_ID_M GENMASK(7, 4) + +/* KSZ9893, KSZ9563, KSZ8563 specific register */ +#define REG_CHIP_ID4 0x0f +#define SKU_ID_KSZ8563 0x3c + +/* Driver set switch broadcast storm protection at 10% rate. */ +#define BROADCAST_STORM_PROT_RATE 10 + +/* 148,800 frames * 67 ms / 100 */ +#define BROADCAST_STORM_VALUE 9969 + +#define BROADCAST_STORM_RATE_HI 0x07 +#define BROADCAST_STORM_RATE_LO 0xFF +#define BROADCAST_STORM_RATE 0x07FF + +#define MULTICAST_STORM_DISABLE BIT(6) + +#define SW_START 0x01 + +/* xMII configuration */ +#define P_MII_DUPLEX_M BIT(6) +#define P_MII_100MBIT_M BIT(4) + +#define P_GMII_1GBIT_M BIT(6) +#define P_RGMII_ID_IG_ENABLE BIT(4) +#define P_RGMII_ID_EG_ENABLE BIT(3) +#define P_MII_MAC_MODE BIT(2) +#define P_MII_SEL_M 0x3 + +/* Interrupt */ +#define REG_SW_PORT_INT_STATUS__1 0x001B +#define REG_SW_PORT_INT_MASK__1 0x001F + +#define REG_PORT_INT_STATUS 0x001B +#define REG_PORT_INT_MASK 0x001F + +#define PORT_SRC_PHY_INT 1 + /* Regmap tables generation */ #define KSZ_SPI_OP_RD 3 #define KSZ_SPI_OP_WR 2 diff --git a/drivers/net/dsa/microchip/ksz_spi.c b/drivers/net/dsa/microchip/ksz_spi.c new file mode 100644 index 000000000000..1b6ab891b986 --- /dev/null +++ b/drivers/net/dsa/microchip/ksz_spi.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Microchip ksz series register access through SPI + * + * Copyright (C) 2017 Microchip Technology Inc. + * Tristram Ha <Tristram.Ha@microchip.com> + */ + +#include <asm/unaligned.h> + +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include "ksz_common.h" + +#define KSZ8795_SPI_ADDR_SHIFT 12 +#define KSZ8795_SPI_ADDR_ALIGN 3 +#define KSZ8795_SPI_TURNAROUND_SHIFT 1 + +#define KSZ8863_SPI_ADDR_SHIFT 8 +#define KSZ8863_SPI_ADDR_ALIGN 8 +#define KSZ8863_SPI_TURNAROUND_SHIFT 0 + +#define KSZ9477_SPI_ADDR_SHIFT 24 +#define KSZ9477_SPI_ADDR_ALIGN 3 +#define KSZ9477_SPI_TURNAROUND_SHIFT 5 + +KSZ_REGMAP_TABLE(ksz8795, 16, KSZ8795_SPI_ADDR_SHIFT, + KSZ8795_SPI_TURNAROUND_SHIFT, KSZ8795_SPI_ADDR_ALIGN); + +KSZ_REGMAP_TABLE(ksz8863, 16, KSZ8863_SPI_ADDR_SHIFT, + KSZ8863_SPI_TURNAROUND_SHIFT, KSZ8863_SPI_ADDR_ALIGN); + +KSZ_REGMAP_TABLE(ksz9477, 32, KSZ9477_SPI_ADDR_SHIFT, + KSZ9477_SPI_TURNAROUND_SHIFT, KSZ9477_SPI_ADDR_ALIGN); + +static int ksz_spi_probe(struct spi_device *spi) +{ + const struct regmap_config *regmap_config; + const struct ksz_chip_data *chip; + struct device *ddev = &spi->dev; + struct regmap_config rc; + struct ksz_device *dev; + int i, ret = 0; + + dev = ksz_switch_alloc(&spi->dev, spi); + if (!dev) + return -ENOMEM; + + chip = device_get_match_data(ddev); + if (!chip) + return -EINVAL; + + if (chip->chip_id == KSZ8830_CHIP_ID) + regmap_config = ksz8863_regmap_config; + else if (chip->chip_id == KSZ8795_CHIP_ID || + chip->chip_id == KSZ8794_CHIP_ID || + chip->chip_id == KSZ8765_CHIP_ID) + regmap_config = ksz8795_regmap_config; + else + regmap_config = ksz9477_regmap_config; + + for (i = 0; i < ARRAY_SIZE(ksz8795_regmap_config); i++) { + rc = regmap_config[i]; + rc.lock_arg = &dev->regmap_mutex; + rc.wr_table = chip->wr_table; + rc.rd_table = chip->rd_table; + dev->regmap[i] = devm_regmap_init_spi(spi, &rc); + + if (IS_ERR(dev->regmap[i])) { + ret = PTR_ERR(dev->regmap[i]); + dev_err(&spi->dev, + "Failed to initialize regmap%i: %d\n", + regmap_config[i].val_bits, ret); + return ret; + } + } + + if (spi->dev.platform_data) + dev->pdata = spi->dev.platform_data; + + /* setup spi */ + spi->mode = SPI_MODE_3; + ret = spi_setup(spi); + if (ret) + return ret; + + dev->irq = spi->irq; + + ret = ksz_switch_register(dev); + + /* Main DSA driver may not be started yet. */ + if (ret) + return ret; + + spi_set_drvdata(spi, dev); + + return 0; +} + +static void ksz_spi_remove(struct spi_device *spi) +{ + struct ksz_device *dev = spi_get_drvdata(spi); + + if (dev) + ksz_switch_remove(dev); +} + +static void ksz_spi_shutdown(struct spi_device *spi) +{ + struct ksz_device *dev = spi_get_drvdata(spi); + + if (!dev) + return; + + if (dev->dev_ops->reset) + dev->dev_ops->reset(dev); + + dsa_switch_shutdown(dev->ds); + + spi_set_drvdata(spi, NULL); +} + +static const struct of_device_id ksz_dt_ids[] = { + { + .compatible = "microchip,ksz8765", + .data = &ksz_switch_chips[KSZ8765] + }, + { + .compatible = "microchip,ksz8794", + .data = &ksz_switch_chips[KSZ8794] + }, + { + .compatible = "microchip,ksz8795", + .data = &ksz_switch_chips[KSZ8795] + }, + { + .compatible = "microchip,ksz8863", + .data = &ksz_switch_chips[KSZ8830] + }, + { + .compatible = "microchip,ksz8873", + .data = &ksz_switch_chips[KSZ8830] + }, + { + .compatible = "microchip,ksz9477", + .data = &ksz_switch_chips[KSZ9477] + }, + { + .compatible = "microchip,ksz9896", + .data = &ksz_switch_chips[KSZ9896] + }, + { + .compatible = "microchip,ksz9897", + .data = &ksz_switch_chips[KSZ9897] + }, + { + .compatible = "microchip,ksz9893", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz9563", + .data = &ksz_switch_chips[KSZ9893] + }, + { + .compatible = "microchip,ksz8563", + .data = &ksz_switch_chips[KSZ8563] + }, + { + .compatible = "microchip,ksz9567", + .data = &ksz_switch_chips[KSZ9567] + }, + { + .compatible = "microchip,lan9370", + .data = &ksz_switch_chips[LAN9370] + }, + { + .compatible = "microchip,lan9371", + .data = &ksz_switch_chips[LAN9371] + }, + { + .compatible = "microchip,lan9372", + .data = &ksz_switch_chips[LAN9372] + }, + { + .compatible = "microchip,lan9373", + .data = &ksz_switch_chips[LAN9373] + }, + { + .compatible = "microchip,lan9374", + .data = &ksz_switch_chips[LAN9374] + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ksz_dt_ids); + +static const struct spi_device_id ksz_spi_ids[] = { + { "ksz8765" }, + { "ksz8794" }, + { "ksz8795" }, + { "ksz8863" }, + { "ksz8873" }, + { "ksz9477" }, + { "ksz9896" }, + { "ksz9897" }, + { "ksz9893" }, + { "ksz9563" }, + { "ksz8563" }, + { "ksz9567" }, + { "lan9370" }, + { "lan9371" }, + { "lan9372" }, + { "lan9373" }, + { "lan9374" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, ksz_spi_ids); + +static struct spi_driver ksz_spi_driver = { + .driver = { + .name = "ksz-switch", + .owner = THIS_MODULE, + .of_match_table = ksz_dt_ids, + }, + .id_table = ksz_spi_ids, + .probe = ksz_spi_probe, + .remove = ksz_spi_remove, + .shutdown = ksz_spi_shutdown, +}; + +module_spi_driver(ksz_spi_driver); + +MODULE_ALIAS("spi:ksz9477"); +MODULE_ALIAS("spi:ksz9896"); +MODULE_ALIAS("spi:ksz9897"); +MODULE_ALIAS("spi:ksz9893"); +MODULE_ALIAS("spi:ksz9563"); +MODULE_ALIAS("spi:ksz8563"); +MODULE_ALIAS("spi:ksz9567"); +MODULE_ALIAS("spi:lan937x"); +MODULE_AUTHOR("Tristram Ha <Tristram.Ha@microchip.com>"); +MODULE_DESCRIPTION("Microchip ksz Series Switch SPI Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/lan937x.h b/drivers/net/dsa/microchip/lan937x.h new file mode 100644 index 000000000000..8e9e66d6728d --- /dev/null +++ b/drivers/net/dsa/microchip/lan937x.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Microchip lan937x dev ops headers + * Copyright (C) 2019-2022 Microchip Technology Inc. + */ + +#ifndef __LAN937X_CFG_H +#define __LAN937X_CFG_H + +int lan937x_reset_switch(struct ksz_device *dev); +int lan937x_setup(struct dsa_switch *ds); +void lan937x_teardown(struct dsa_switch *ds); +void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port); +void lan937x_config_cpu_port(struct dsa_switch *ds); +int lan937x_switch_init(struct ksz_device *dev); +void lan937x_switch_exit(struct ksz_device *dev); +int lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data); +int lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val); +int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu); +void lan937x_phylink_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config); +void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port); +int lan937x_set_ageing_time(struct ksz_device *dev, unsigned int msecs); +#endif diff --git a/drivers/net/dsa/microchip/lan937x_main.c b/drivers/net/dsa/microchip/lan937x_main.c new file mode 100644 index 000000000000..7e4f307a0387 --- /dev/null +++ b/drivers/net/dsa/microchip/lan937x_main.c @@ -0,0 +1,391 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Microchip LAN937X switch driver main logic + * Copyright (C) 2019-2022 Microchip Technology Inc. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/iopoll.h> +#include <linux/phy.h> +#include <linux/of_net.h> +#include <linux/if_bridge.h> +#include <linux/if_vlan.h> +#include <linux/math.h> +#include <net/dsa.h> +#include <net/switchdev.h> + +#include "lan937x_reg.h" +#include "ksz_common.h" +#include "lan937x.h" + +static int lan937x_cfg(struct ksz_device *dev, u32 addr, u8 bits, bool set) +{ + return regmap_update_bits(dev->regmap[0], addr, bits, set ? bits : 0); +} + +static int lan937x_port_cfg(struct ksz_device *dev, int port, int offset, + u8 bits, bool set) +{ + return regmap_update_bits(dev->regmap[0], PORT_CTRL_ADDR(port, offset), + bits, set ? bits : 0); +} + +static int lan937x_enable_spi_indirect_access(struct ksz_device *dev) +{ + u16 data16; + int ret; + + /* Enable Phy access through SPI */ + ret = lan937x_cfg(dev, REG_GLOBAL_CTRL_0, SW_PHY_REG_BLOCK, false); + if (ret < 0) + return ret; + + ret = ksz_read16(dev, REG_VPHY_SPECIAL_CTRL__2, &data16); + if (ret < 0) + return ret; + + /* Allow SPI access */ + data16 |= VPHY_SPI_INDIRECT_ENABLE; + + return ksz_write16(dev, REG_VPHY_SPECIAL_CTRL__2, data16); +} + +static int lan937x_vphy_ind_addr_wr(struct ksz_device *dev, int addr, int reg) +{ + u16 addr_base = REG_PORT_T1_PHY_CTRL_BASE; + u16 temp; + + /* get register address based on the logical port */ + temp = PORT_CTRL_ADDR(addr, (addr_base + (reg << 2))); + + return ksz_write16(dev, REG_VPHY_IND_ADDR__2, temp); +} + +static int lan937x_internal_phy_write(struct ksz_device *dev, int addr, int reg, + u16 val) +{ + unsigned int value; + int ret; + + /* Check for internal phy port */ + if (!dev->info->internal_phy[addr]) + return -EOPNOTSUPP; + + ret = lan937x_vphy_ind_addr_wr(dev, addr, reg); + if (ret < 0) + return ret; + + /* Write the data to be written to the VPHY reg */ + ret = ksz_write16(dev, REG_VPHY_IND_DATA__2, val); + if (ret < 0) + return ret; + + /* Write the Write En and Busy bit */ + ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2, + (VPHY_IND_WRITE | VPHY_IND_BUSY)); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2, + value, !(value & VPHY_IND_BUSY), 10, + 1000); + if (ret < 0) { + dev_err(dev->dev, "Failed to write phy register\n"); + return ret; + } + + return 0; +} + +static int lan937x_internal_phy_read(struct ksz_device *dev, int addr, int reg, + u16 *val) +{ + unsigned int value; + int ret; + + /* Check for internal phy port, return 0xffff for non-existent phy */ + if (!dev->info->internal_phy[addr]) + return 0xffff; + + ret = lan937x_vphy_ind_addr_wr(dev, addr, reg); + if (ret < 0) + return ret; + + /* Write Read and Busy bit to start the transaction */ + ret = ksz_write16(dev, REG_VPHY_IND_CTRL__2, VPHY_IND_BUSY); + if (ret < 0) + return ret; + + ret = regmap_read_poll_timeout(dev->regmap[1], REG_VPHY_IND_CTRL__2, + value, !(value & VPHY_IND_BUSY), 10, + 1000); + if (ret < 0) { + dev_err(dev->dev, "Failed to read phy register\n"); + return ret; + } + + /* Read the VPHY register which has the PHY data */ + return ksz_read16(dev, REG_VPHY_IND_DATA__2, val); +} + +int lan937x_r_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 *data) +{ + return lan937x_internal_phy_read(dev, addr, reg, data); +} + +int lan937x_w_phy(struct ksz_device *dev, u16 addr, u16 reg, u16 val) +{ + return lan937x_internal_phy_write(dev, addr, reg, val); +} + +int lan937x_reset_switch(struct ksz_device *dev) +{ + u32 data32; + int ret; + + /* reset switch */ + ret = lan937x_cfg(dev, REG_SW_OPERATION, SW_RESET, true); + if (ret < 0) + return ret; + + /* Enable Auto Aging */ + ret = lan937x_cfg(dev, REG_SW_LUE_CTRL_1, SW_LINK_AUTO_AGING, true); + if (ret < 0) + return ret; + + /* disable interrupts */ + ret = ksz_write32(dev, REG_SW_INT_MASK__4, SWITCH_INT_MASK); + if (ret < 0) + return ret; + + ret = ksz_write32(dev, REG_SW_INT_STATUS__4, POR_READY_INT); + if (ret < 0) + return ret; + + ret = ksz_write32(dev, REG_SW_PORT_INT_MASK__4, 0xFF); + if (ret < 0) + return ret; + + return ksz_read32(dev, REG_SW_PORT_INT_STATUS__4, &data32); +} + +void lan937x_port_setup(struct ksz_device *dev, int port, bool cpu_port) +{ + const u32 *masks = dev->info->masks; + const u16 *regs = dev->info->regs; + struct dsa_switch *ds = dev->ds; + u8 member; + + /* enable tag tail for host port */ + if (cpu_port) + lan937x_port_cfg(dev, port, REG_PORT_CTRL_0, + PORT_TAIL_TAG_ENABLE, true); + + /* set back pressure for half duplex */ + lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_1, PORT_BACK_PRESSURE, + true); + + /* enable 802.1p priority */ + lan937x_port_cfg(dev, port, P_PRIO_CTRL, PORT_802_1P_PRIO_ENABLE, true); + + if (!dev->info->internal_phy[port]) + lan937x_port_cfg(dev, port, regs[P_XMII_CTRL_0], + masks[P_MII_TX_FLOW_CTRL] | + masks[P_MII_RX_FLOW_CTRL], + true); + + if (cpu_port) + member = dsa_user_ports(ds); + else + member = BIT(dsa_upstream_port(ds, port)); + + dev->dev_ops->cfg_port_member(dev, port, member); +} + +void lan937x_config_cpu_port(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + struct dsa_port *dp; + + dsa_switch_for_each_cpu_port(dp, ds) { + if (dev->info->cpu_ports & (1 << dp->index)) { + dev->cpu_port = dp->index; + + /* enable cpu port */ + lan937x_port_setup(dev, dp->index, true); + } + } + + dsa_switch_for_each_user_port(dp, ds) { + ksz_port_stp_state_set(ds, dp->index, BR_STATE_DISABLED); + } +} + +int lan937x_change_mtu(struct ksz_device *dev, int port, int new_mtu) +{ + struct dsa_switch *ds = dev->ds; + int ret; + + new_mtu += VLAN_ETH_HLEN + ETH_FCS_LEN; + + if (dsa_is_cpu_port(ds, port)) + new_mtu += LAN937X_TAG_LEN; + + if (new_mtu >= FR_MIN_SIZE) + ret = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, + PORT_JUMBO_PACKET, true); + else + ret = lan937x_port_cfg(dev, port, REG_PORT_MAC_CTRL_0, + PORT_JUMBO_PACKET, false); + if (ret < 0) { + dev_err(ds->dev, "failed to enable jumbo\n"); + return ret; + } + + /* Write the frame size in PORT_MAX_FR_SIZE register */ + ksz_pwrite16(dev, port, PORT_MAX_FR_SIZE, new_mtu); + + return 0; +} + +int lan937x_set_ageing_time(struct ksz_device *dev, unsigned int msecs) +{ + u32 secs = msecs / 1000; + u32 value; + int ret; + + value = FIELD_GET(SW_AGE_PERIOD_7_0_M, secs); + + ret = ksz_write8(dev, REG_SW_AGE_PERIOD__1, value); + if (ret < 0) + return ret; + + value = FIELD_GET(SW_AGE_PERIOD_19_8_M, secs); + + return ksz_write16(dev, REG_SW_AGE_PERIOD__2, value); +} + +static void lan937x_set_tune_adj(struct ksz_device *dev, int port, + u16 reg, u8 val) +{ + u16 data16; + + ksz_pread16(dev, port, reg, &data16); + + /* Update tune Adjust */ + data16 |= FIELD_PREP(PORT_TUNE_ADJ, val); + ksz_pwrite16(dev, port, reg, data16); + + /* write DLL reset to take effect */ + data16 |= PORT_DLL_RESET; + ksz_pwrite16(dev, port, reg, data16); +} + +static void lan937x_set_rgmii_tx_delay(struct ksz_device *dev, int port) +{ + u8 val; + + /* Apply different codes based on the ports as per characterization + * results + */ + val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_TX_DELAY_2NS : + RGMII_2_TX_DELAY_2NS; + + lan937x_set_tune_adj(dev, port, REG_PORT_XMII_CTRL_5, val); +} + +static void lan937x_set_rgmii_rx_delay(struct ksz_device *dev, int port) +{ + u8 val; + + val = (port == LAN937X_RGMII_1_PORT) ? RGMII_1_RX_DELAY_2NS : + RGMII_2_RX_DELAY_2NS; + + lan937x_set_tune_adj(dev, port, REG_PORT_XMII_CTRL_4, val); +} + +void lan937x_phylink_get_caps(struct ksz_device *dev, int port, + struct phylink_config *config) +{ + config->mac_capabilities = MAC_100FD; + + if (dev->info->supports_rgmii[port]) { + /* MII/RMII/RGMII ports */ + config->mac_capabilities |= MAC_ASYM_PAUSE | MAC_SYM_PAUSE | + MAC_100HD | MAC_10 | MAC_1000FD; + } +} + +void lan937x_setup_rgmii_delay(struct ksz_device *dev, int port) +{ + struct ksz_port *p = &dev->ports[port]; + + if (p->rgmii_tx_val) { + lan937x_set_rgmii_tx_delay(dev, port); + dev_info(dev->dev, "Applied rgmii tx delay for the port %d\n", + port); + } + + if (p->rgmii_rx_val) { + lan937x_set_rgmii_rx_delay(dev, port); + dev_info(dev->dev, "Applied rgmii rx delay for the port %d\n", + port); + } +} + +int lan937x_switch_init(struct ksz_device *dev) +{ + dev->port_mask = (1 << dev->info->port_cnt) - 1; + + return 0; +} + +int lan937x_setup(struct dsa_switch *ds) +{ + struct ksz_device *dev = ds->priv; + int ret; + + /* enable Indirect Access from SPI to the VPHY registers */ + ret = lan937x_enable_spi_indirect_access(dev); + if (ret < 0) { + dev_err(dev->dev, "failed to enable spi indirect access"); + return ret; + } + + /* The VLAN aware is a global setting. Mixed vlan + * filterings are not supported. + */ + ds->vlan_filtering_is_global = true; + + /* Enable aggressive back off for half duplex & UNH mode */ + lan937x_cfg(dev, REG_SW_MAC_CTRL_0, + (SW_PAUSE_UNH_MODE | SW_NEW_BACKOFF | SW_AGGR_BACKOFF), + true); + + /* If NO_EXC_COLLISION_DROP bit is set, the switch will not drop + * packets when 16 or more collisions occur + */ + lan937x_cfg(dev, REG_SW_MAC_CTRL_1, NO_EXC_COLLISION_DROP, true); + + /* enable global MIB counter freeze function */ + lan937x_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true); + + /* disable CLK125 & CLK25, 1: disable, 0: enable */ + lan937x_cfg(dev, REG_SW_GLOBAL_OUTPUT_CTRL__1, + (SW_CLK125_ENB | SW_CLK25_ENB), true); + + return 0; +} + +void lan937x_teardown(struct dsa_switch *ds) +{ + +} + +void lan937x_switch_exit(struct ksz_device *dev) +{ + lan937x_reset_switch(dev); +} + +MODULE_AUTHOR("Arun Ramadoss <arun.ramadoss@microchip.com>"); +MODULE_DESCRIPTION("Microchip LAN937x Series Switch DSA Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/dsa/microchip/lan937x_reg.h b/drivers/net/dsa/microchip/lan937x_reg.h new file mode 100644 index 000000000000..5bc16a4c4441 --- /dev/null +++ b/drivers/net/dsa/microchip/lan937x_reg.h @@ -0,0 +1,202 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Microchip LAN937X switch register definitions + * Copyright (C) 2019-2021 Microchip Technology Inc. + */ +#ifndef __LAN937X_REG_H +#define __LAN937X_REG_H + +#define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12)) + +/* 0 - Operation */ +#define REG_GLOBAL_CTRL_0 0x0007 + +#define SW_PHY_REG_BLOCK BIT(7) +#define SW_FAST_MODE BIT(3) +#define SW_FAST_MODE_OVERRIDE BIT(2) + +#define REG_SW_INT_STATUS__4 0x0010 +#define REG_SW_INT_MASK__4 0x0014 + +#define LUE_INT BIT(31) +#define TRIG_TS_INT BIT(30) +#define APB_TIMEOUT_INT BIT(29) +#define OVER_TEMP_INT BIT(28) +#define HSR_INT BIT(27) +#define PIO_INT BIT(26) +#define POR_READY_INT BIT(25) + +#define SWITCH_INT_MASK \ + (LUE_INT | TRIG_TS_INT | APB_TIMEOUT_INT | OVER_TEMP_INT | HSR_INT | \ + PIO_INT | POR_READY_INT) + +#define REG_SW_PORT_INT_STATUS__4 0x0018 +#define REG_SW_PORT_INT_MASK__4 0x001C + +/* 1 - Global */ +#define REG_SW_GLOBAL_OUTPUT_CTRL__1 0x0103 +#define SW_CLK125_ENB BIT(1) +#define SW_CLK25_ENB BIT(0) + +/* 3 - Operation Control */ +#define REG_SW_OPERATION 0x0300 + +#define SW_DOUBLE_TAG BIT(7) +#define SW_OVER_TEMP_ENABLE BIT(2) +#define SW_RESET BIT(1) + +#define REG_SW_LUE_CTRL_0 0x0310 + +#define SW_VLAN_ENABLE BIT(7) +#define SW_DROP_INVALID_VID BIT(6) +#define SW_AGE_CNT_M 0x7 +#define SW_AGE_CNT_S 3 +#define SW_RESV_MCAST_ENABLE BIT(2) + +#define REG_SW_LUE_CTRL_1 0x0311 + +#define UNICAST_LEARN_DISABLE BIT(7) +#define SW_FLUSH_STP_TABLE BIT(5) +#define SW_FLUSH_MSTP_TABLE BIT(4) +#define SW_SRC_ADDR_FILTER BIT(3) +#define SW_AGING_ENABLE BIT(2) +#define SW_FAST_AGING BIT(1) +#define SW_LINK_AUTO_AGING BIT(0) + +#define REG_SW_AGE_PERIOD__1 0x0313 +#define SW_AGE_PERIOD_7_0_M GENMASK(7, 0) + +#define REG_SW_AGE_PERIOD__2 0x0320 +#define SW_AGE_PERIOD_19_8_M GENMASK(19, 8) + +#define REG_SW_MAC_CTRL_0 0x0330 +#define SW_NEW_BACKOFF BIT(7) +#define SW_PAUSE_UNH_MODE BIT(1) +#define SW_AGGR_BACKOFF BIT(0) + +#define REG_SW_MAC_CTRL_1 0x0331 +#define SW_SHORT_IFG BIT(7) +#define MULTICAST_STORM_DISABLE BIT(6) +#define SW_BACK_PRESSURE BIT(5) +#define FAIR_FLOW_CTRL BIT(4) +#define NO_EXC_COLLISION_DROP BIT(3) +#define SW_LEGAL_PACKET_DISABLE BIT(1) +#define SW_PASS_SHORT_FRAME BIT(0) + +#define REG_SW_MAC_CTRL_6 0x0336 +#define SW_MIB_COUNTER_FLUSH BIT(7) +#define SW_MIB_COUNTER_FREEZE BIT(6) + +/* 4 - LUE */ +#define REG_SW_ALU_STAT_CTRL__4 0x041C + +#define REG_SW_ALU_VAL_B 0x0424 +#define ALU_V_OVERRIDE BIT(31) +#define ALU_V_USE_FID BIT(30) +#define ALU_V_PORT_MAP 0xFF + +/* 7 - VPhy */ +#define REG_VPHY_IND_ADDR__2 0x075C +#define REG_VPHY_IND_DATA__2 0x0760 + +#define REG_VPHY_IND_CTRL__2 0x0768 + +#define VPHY_IND_WRITE BIT(1) +#define VPHY_IND_BUSY BIT(0) + +#define REG_VPHY_SPECIAL_CTRL__2 0x077C +#define VPHY_SMI_INDIRECT_ENABLE BIT(15) +#define VPHY_SW_LOOPBACK BIT(14) +#define VPHY_MDIO_INTERNAL_ENABLE BIT(13) +#define VPHY_SPI_INDIRECT_ENABLE BIT(12) +#define VPHY_PORT_MODE_M 0x3 +#define VPHY_PORT_MODE_S 8 +#define VPHY_MODE_RGMII 0 +#define VPHY_MODE_MII_PHY 1 +#define VPHY_MODE_SGMII 2 +#define VPHY_MODE_RMII_PHY 3 +#define VPHY_SW_COLLISION_TEST BIT(7) +#define VPHY_SPEED_DUPLEX_STAT_M 0x7 +#define VPHY_SPEED_DUPLEX_STAT_S 2 +#define VPHY_SPEED_1000 BIT(4) +#define VPHY_SPEED_100 BIT(3) +#define VPHY_FULL_DUPLEX BIT(2) + +/* Port Registers */ + +/* 0 - Operation */ +#define REG_PORT_INT_STATUS 0x001B +#define REG_PORT_INT_MASK 0x001F + +#define PORT_TAS_INT BIT(5) +#define PORT_QCI_INT BIT(4) +#define PORT_SGMII_INT BIT(3) +#define PORT_PTP_INT BIT(2) +#define PORT_PHY_INT BIT(1) +#define PORT_ACL_INT BIT(0) + +#define PORT_SRC_PHY_INT 1 + +#define REG_PORT_CTRL_0 0x0020 + +#define PORT_MAC_LOOPBACK BIT(7) +#define PORT_MAC_REMOTE_LOOPBACK BIT(6) +#define PORT_K2L_INSERT_ENABLE BIT(5) +#define PORT_K2L_DEBUG_ENABLE BIT(4) +#define PORT_TAIL_TAG_ENABLE BIT(2) +#define PORT_QUEUE_SPLIT_ENABLE 0x3 + +/* 1 - Phy */ +#define REG_PORT_T1_PHY_CTRL_BASE 0x0100 + +/* 3 - xMII */ +#define PORT_SGMII_SEL BIT(7) +#define PORT_GRXC_ENABLE BIT(0) + +#define PORT_MII_SEL_EDGE BIT(5) + +#define REG_PORT_XMII_CTRL_4 0x0304 +#define REG_PORT_XMII_CTRL_5 0x0306 + +#define PORT_DLL_RESET BIT(15) +#define PORT_TUNE_ADJ GENMASK(13, 7) + +/* 4 - MAC */ +#define REG_PORT_MAC_CTRL_0 0x0400 +#define PORT_CHECK_LENGTH BIT(2) +#define PORT_BROADCAST_STORM BIT(1) +#define PORT_JUMBO_PACKET BIT(0) + +#define REG_PORT_MAC_CTRL_1 0x0401 +#define PORT_BACK_PRESSURE BIT(3) +#define PORT_PASS_ALL BIT(0) + +#define PORT_MAX_FR_SIZE 0x404 +#define FR_MIN_SIZE 1522 + +/* 8 - Classification and Policing */ +#define REG_PORT_MRI_PRIO_CTRL 0x0801 +#define PORT_HIGHEST_PRIO BIT(7) +#define PORT_OR_PRIO BIT(6) +#define PORT_MAC_PRIO_ENABLE BIT(4) +#define PORT_VLAN_PRIO_ENABLE BIT(3) +#define PORT_802_1P_PRIO_ENABLE BIT(2) +#define PORT_DIFFSERV_PRIO_ENABLE BIT(1) +#define PORT_ACL_PRIO_ENABLE BIT(0) + +#define P_PRIO_CTRL REG_PORT_MRI_PRIO_CTRL + +/* The port number as per the datasheet */ +#define RGMII_2_PORT_NUM 5 +#define RGMII_1_PORT_NUM 6 + +#define LAN937X_RGMII_2_PORT (RGMII_2_PORT_NUM - 1) +#define LAN937X_RGMII_1_PORT (RGMII_1_PORT_NUM - 1) + +#define RGMII_1_TX_DELAY_2NS 2 +#define RGMII_2_TX_DELAY_2NS 0 +#define RGMII_1_RX_DELAY_2NS 0x1B +#define RGMII_2_RX_DELAY_2NS 0x14 + +#define LAN937X_TAG_LEN 2 + +#endif |