19c472a0dSDavid Yang // SPDX-License-Identifier: GPL-2.0-or-later 29c472a0dSDavid Yang /* 39c472a0dSDavid Yang * Driver for Motorcomm YT921x Switch 49c472a0dSDavid Yang * 59c472a0dSDavid Yang * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII, 69c472a0dSDavid Yang * be sure to do your own checks before porting to another chip. 79c472a0dSDavid Yang * 89c472a0dSDavid Yang * Copyright (c) 2025 David Yang 99c472a0dSDavid Yang */ 109c472a0dSDavid Yang 119c472a0dSDavid Yang #include <linux/dcbnl.h> 129c472a0dSDavid Yang #include <linux/etherdevice.h> 139c472a0dSDavid Yang #include <linux/if_bridge.h> 149c472a0dSDavid Yang #include <linux/if_hsr.h> 159c472a0dSDavid Yang #include <linux/if_vlan.h> 169c472a0dSDavid Yang #include <linux/mdio.h> 179c472a0dSDavid Yang #include <linux/module.h> 189c472a0dSDavid Yang #include <linux/of.h> 199c472a0dSDavid Yang #include <linux/of_mdio.h> 209c472a0dSDavid Yang #include <linux/of_net.h> 219c472a0dSDavid Yang #include <linux/sort.h> 22ed445358SDavid Yang #include <linux/u64_stats_sync.h> 239c472a0dSDavid Yang 249c472a0dSDavid Yang #include <net/dsa.h> 259c472a0dSDavid Yang #include <net/dscp.h> 269c472a0dSDavid Yang #include <net/ieee8021q.h> 279c472a0dSDavid Yang 289c472a0dSDavid Yang #include "chip.h" 299af9a816SDavid Yang #include "leds.h" 30b9eca1c2SDavid Yang #include "mdio_bus.h" 3125265e7cSDavid Yang #include "mib.h" 324b8fbb87SDavid Yang #include "pcs.h" 33e9423a64SDavid Yang #include "smi.h" 34ba13bd27SDavid Yang #include "tc.h" 359c472a0dSDavid Yang 369c472a0dSDavid Yang static const struct yt921x_info yt921x_infos[] = { 379c472a0dSDavid Yang { 389c472a0dSDavid Yang "YT9215SC", YT9215_MAJOR, 1, 0, 39b6d27629SDavid Yang GENMASK(4, 0), 40605597afSDavid Yang BIT(9), 41b6d27629SDavid Yang BIT(8) | BIT(9), 429c472a0dSDavid Yang }, 439c472a0dSDavid Yang { 449c472a0dSDavid Yang "YT9215S", YT9215_MAJOR, 2, 0, 45b6d27629SDavid Yang GENMASK(4, 0), 46605597afSDavid Yang BIT(9), 47605597afSDavid Yang BIT(8), 489c472a0dSDavid Yang }, 499c472a0dSDavid Yang { 509c472a0dSDavid Yang "YT9215RB", YT9215_MAJOR, 3, 0, 51b6d27629SDavid Yang GENMASK(4, 0), 52b6d27629SDavid Yang BIT(8) | BIT(9), 53605597afSDavid Yang 0, 549c472a0dSDavid Yang }, 559c472a0dSDavid Yang { 569c472a0dSDavid Yang "YT9214NB", YT9215_MAJOR, 3, 2, 57b6d27629SDavid Yang BIT(1) | BIT(3), 58605597afSDavid Yang BIT(9), 59605597afSDavid Yang BIT(8), 609c472a0dSDavid Yang }, 619c472a0dSDavid Yang { 629c472a0dSDavid Yang "YT9213NB", YT9215_MAJOR, 3, 3, 63b6d27629SDavid Yang BIT(1) | BIT(3), 64b6d27629SDavid Yang BIT(9), 65605597afSDavid Yang BIT(9), 669c472a0dSDavid Yang }, 679c472a0dSDavid Yang { 689c472a0dSDavid Yang "YT9218N", YT9218_MAJOR, 0, 0, 69b6d27629SDavid Yang GENMASK(7, 0), 709c472a0dSDavid Yang 0, 71605597afSDavid Yang 0, 729c472a0dSDavid Yang }, 739c472a0dSDavid Yang { 749c472a0dSDavid Yang "YT9218MB", YT9218_MAJOR, 1, 0, 75b6d27629SDavid Yang GENMASK(7, 0), 76b6d27629SDavid Yang BIT(8) | BIT(9), 77605597afSDavid Yang BIT(8) | BIT(9), 789c472a0dSDavid Yang }, 799c472a0dSDavid Yang {} 809c472a0dSDavid Yang }; 819c472a0dSDavid Yang 829c472a0dSDavid Yang #define YT921X_VID_UNWARE 4095 839c472a0dSDavid Yang 849c472a0dSDavid Yang struct yt921x_reg_mdio { 859c472a0dSDavid Yang struct mii_bus *bus; 869c472a0dSDavid Yang int addr; 879c472a0dSDavid Yang /* SWITCH_ID_1 / SWITCH_ID_0 of the device 889c472a0dSDavid Yang * 899c472a0dSDavid Yang * This is a way to multiplex multiple devices on the same MII phyaddr 909c472a0dSDavid Yang * and should be configurable in DT. However, MDIO core simply doesn't 919c472a0dSDavid Yang * allow multiple devices over one reg addr, so this is a fixed value 929c472a0dSDavid Yang * for now until a solution is found. 939c472a0dSDavid Yang * 949c472a0dSDavid Yang * Keep this because we need switchid to form MII regaddrs anyway. 959c472a0dSDavid Yang */ 969c472a0dSDavid Yang unsigned char switchid; 979c472a0dSDavid Yang }; 989c472a0dSDavid Yang 999c472a0dSDavid Yang /* TODO: SPI/I2C */ 1009c472a0dSDavid Yang 1019c472a0dSDavid Yang static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) 1029c472a0dSDavid Yang { 1039c472a0dSDavid Yang struct yt921x_reg_mdio *mdio = context; 1049c472a0dSDavid Yang struct mii_bus *bus = mdio->bus; 1059c472a0dSDavid Yang int addr = mdio->addr; 1069c472a0dSDavid Yang u32 reg_addr; 1079c472a0dSDavid Yang u32 reg_data; 1089c472a0dSDavid Yang u32 val; 1099c472a0dSDavid Yang int res; 1109c472a0dSDavid Yang 1119c472a0dSDavid Yang /* Hold the mdio bus lock to avoid (un)locking for 4 times */ 1129c472a0dSDavid Yang mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); 1139c472a0dSDavid Yang 1149c472a0dSDavid Yang reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | 1159c472a0dSDavid Yang YT921X_SMI_READ; 1169c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); 1179c472a0dSDavid Yang if (res) 1189c472a0dSDavid Yang goto end; 1199c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); 1209c472a0dSDavid Yang if (res) 1219c472a0dSDavid Yang goto end; 1229c472a0dSDavid Yang 1239c472a0dSDavid Yang reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | 1249c472a0dSDavid Yang YT921X_SMI_READ; 1259c472a0dSDavid Yang res = __mdiobus_read(bus, addr, reg_data); 1269c472a0dSDavid Yang if (res < 0) 1279c472a0dSDavid Yang goto end; 1289c472a0dSDavid Yang val = (u16)res; 1299c472a0dSDavid Yang res = __mdiobus_read(bus, addr, reg_data); 1309c472a0dSDavid Yang if (res < 0) 1319c472a0dSDavid Yang goto end; 1329c472a0dSDavid Yang val = (val << 16) | (u16)res; 1339c472a0dSDavid Yang 1349c472a0dSDavid Yang *valp = val; 1359c472a0dSDavid Yang res = 0; 1369c472a0dSDavid Yang 1379c472a0dSDavid Yang end: 1389c472a0dSDavid Yang mutex_unlock(&bus->mdio_lock); 1399c472a0dSDavid Yang return res; 1409c472a0dSDavid Yang } 1419c472a0dSDavid Yang 1429c472a0dSDavid Yang static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val) 1439c472a0dSDavid Yang { 1449c472a0dSDavid Yang struct yt921x_reg_mdio *mdio = context; 1459c472a0dSDavid Yang struct mii_bus *bus = mdio->bus; 1469c472a0dSDavid Yang int addr = mdio->addr; 1479c472a0dSDavid Yang u32 reg_addr; 1489c472a0dSDavid Yang u32 reg_data; 1499c472a0dSDavid Yang int res; 1509c472a0dSDavid Yang 1519c472a0dSDavid Yang mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); 1529c472a0dSDavid Yang 1539c472a0dSDavid Yang reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | 1549c472a0dSDavid Yang YT921X_SMI_WRITE; 1559c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); 1569c472a0dSDavid Yang if (res) 1579c472a0dSDavid Yang goto end; 1589c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); 1599c472a0dSDavid Yang if (res) 1609c472a0dSDavid Yang goto end; 1619c472a0dSDavid Yang 1629c472a0dSDavid Yang reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | 1639c472a0dSDavid Yang YT921X_SMI_WRITE; 1649c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16)); 1659c472a0dSDavid Yang if (res) 1669c472a0dSDavid Yang goto end; 1679c472a0dSDavid Yang res = __mdiobus_write(bus, addr, reg_data, (u16)val); 1689c472a0dSDavid Yang if (res) 1699c472a0dSDavid Yang goto end; 1709c472a0dSDavid Yang 1719c472a0dSDavid Yang res = 0; 1729c472a0dSDavid Yang 1739c472a0dSDavid Yang end: 1749c472a0dSDavid Yang mutex_unlock(&bus->mdio_lock); 1759c472a0dSDavid Yang return res; 1769c472a0dSDavid Yang } 1779c472a0dSDavid Yang 1789c472a0dSDavid Yang static const struct yt921x_reg_ops yt921x_reg_ops_mdio = { 1799c472a0dSDavid Yang .read = yt921x_reg_mdio_read, 1809c472a0dSDavid Yang .write = yt921x_reg_mdio_write, 1819c472a0dSDavid Yang }; 1829c472a0dSDavid Yang 1839c472a0dSDavid Yang /* TODO: SPI/I2C */ 1849c472a0dSDavid Yang 1859c472a0dSDavid Yang static int 1869c472a0dSDavid Yang yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e) 1879c472a0dSDavid Yang { 1889c472a0dSDavid Yang /* Poor datasheet for EEE operations; don't ask if you are confused */ 1899c472a0dSDavid Yang 1909c472a0dSDavid Yang bool enable = e->eee_enabled; 1919c472a0dSDavid Yang u16 new_mask; 1929c472a0dSDavid Yang int res; 1939c472a0dSDavid Yang 1949c472a0dSDavid Yang /* Enable / disable global EEE */ 1959c472a0dSDavid Yang new_mask = priv->eee_ports_mask; 1969c472a0dSDavid Yang new_mask &= ~BIT(port); 1979c472a0dSDavid Yang new_mask |= !enable ? 0 : BIT(port); 1989c472a0dSDavid Yang 1999c472a0dSDavid Yang if (!!new_mask != !!priv->eee_ports_mask) { 2009c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC, 2019c472a0dSDavid Yang YT921X_PON_STRAP_EEE, !!new_mask); 2029c472a0dSDavid Yang if (res) 2039c472a0dSDavid Yang return res; 2049c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL, 2059c472a0dSDavid Yang YT921X_PON_STRAP_EEE, !!new_mask); 2069c472a0dSDavid Yang if (res) 2079c472a0dSDavid Yang return res; 2089c472a0dSDavid Yang } 2099c472a0dSDavid Yang 2109c472a0dSDavid Yang priv->eee_ports_mask = new_mask; 2119c472a0dSDavid Yang 2129c472a0dSDavid Yang /* Enable / disable port EEE */ 2139c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL, 2149c472a0dSDavid Yang YT921X_EEE_CTRL_ENn(port), enable); 2159c472a0dSDavid Yang if (res) 2169c472a0dSDavid Yang return res; 2179c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port), 2189c472a0dSDavid Yang YT921X_EEE_VAL_DATA, enable); 2199c472a0dSDavid Yang if (res) 2209c472a0dSDavid Yang return res; 2219c472a0dSDavid Yang 2229c472a0dSDavid Yang return 0; 2239c472a0dSDavid Yang } 2249c472a0dSDavid Yang 2259c472a0dSDavid Yang static int 2269c472a0dSDavid Yang yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) 2279c472a0dSDavid Yang { 228cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 2299c472a0dSDavid Yang int res; 2309c472a0dSDavid Yang 2319c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 2329c472a0dSDavid Yang res = yt921x_set_eee(priv, port, e); 2339c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 2349c472a0dSDavid Yang 2359c472a0dSDavid Yang return res; 2369c472a0dSDavid Yang } 2379c472a0dSDavid Yang 2389c472a0dSDavid Yang static int 2399c472a0dSDavid Yang yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 2409c472a0dSDavid Yang { 2419c472a0dSDavid Yang /* Only serves as packet filter, since the frame size is always set to 2429c472a0dSDavid Yang * maximum after reset 2439c472a0dSDavid Yang */ 2449c472a0dSDavid Yang 245cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 2469c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 2479c472a0dSDavid Yang int frame_size; 2489c472a0dSDavid Yang int res; 2499c472a0dSDavid Yang 2509c472a0dSDavid Yang frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN; 2519c472a0dSDavid Yang if (dsa_port_is_cpu(dp)) 2529c472a0dSDavid Yang frame_size += YT921X_TAG_LEN; 2539c472a0dSDavid Yang 2549c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 2559c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port), 2569c472a0dSDavid Yang YT921X_MAC_FRAME_SIZE_M, 2579c472a0dSDavid Yang YT921X_MAC_FRAME_SIZE(frame_size)); 2589c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 2599c472a0dSDavid Yang 2609c472a0dSDavid Yang return res; 2619c472a0dSDavid Yang } 2629c472a0dSDavid Yang 2639c472a0dSDavid Yang static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) 2649c472a0dSDavid Yang { 2659c472a0dSDavid Yang /* Only called for user ports, exclude tag len here */ 2669c472a0dSDavid Yang return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; 2679c472a0dSDavid Yang } 2689c472a0dSDavid Yang 2699c472a0dSDavid Yang static int yt921x_lag_hash(struct yt921x_priv *priv, u32 ctrl, bool unique_lag, 2709c472a0dSDavid Yang struct netlink_ext_ack *extack) 2719c472a0dSDavid Yang { 2729c472a0dSDavid Yang u32 val; 2739c472a0dSDavid Yang int res; 2749c472a0dSDavid Yang 2759c472a0dSDavid Yang /* Hash Mode is global. Make sure the same Hash Mode is set to all the 2769c472a0dSDavid Yang * 2 possible lags. 2779c472a0dSDavid Yang * If we are the unique LAG we can set whatever hash mode we want. 2789c472a0dSDavid Yang * To change hash mode it's needed to remove all LAG and change the mode 2799c472a0dSDavid Yang * with the latest. 2809c472a0dSDavid Yang */ 2819c472a0dSDavid Yang if (unique_lag) { 2829c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_LAG_HASH, ctrl); 2839c472a0dSDavid Yang if (res) 2849c472a0dSDavid Yang return res; 2859c472a0dSDavid Yang } else { 2869c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_LAG_HASH, &val); 2879c472a0dSDavid Yang if (res) 2889c472a0dSDavid Yang return res; 2899c472a0dSDavid Yang 2909c472a0dSDavid Yang if (val != ctrl) { 2919c472a0dSDavid Yang NL_SET_ERR_MSG_MOD(extack, 2929c472a0dSDavid Yang "Mismatched Hash Mode across different lags is not supported"); 2939c472a0dSDavid Yang return -EOPNOTSUPP; 2949c472a0dSDavid Yang } 2959c472a0dSDavid Yang } 2969c472a0dSDavid Yang 2979c472a0dSDavid Yang return 0; 2989c472a0dSDavid Yang } 2999c472a0dSDavid Yang 3009c472a0dSDavid Yang static int yt921x_lag_set(struct yt921x_priv *priv, u8 index, u16 ports_mask) 3019c472a0dSDavid Yang { 3029c472a0dSDavid Yang unsigned long targets_mask = ports_mask; 3039c472a0dSDavid Yang unsigned int cnt; 3049c472a0dSDavid Yang u32 ctrl; 3059c472a0dSDavid Yang int port; 3069c472a0dSDavid Yang int res; 3079c472a0dSDavid Yang 3089c472a0dSDavid Yang cnt = 0; 3099c472a0dSDavid Yang for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { 3109c472a0dSDavid Yang ctrl = YT921X_LAG_MEMBER_PORT(port); 3119c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_LAG_MEMBERnm(index, cnt), 3129c472a0dSDavid Yang ctrl); 3139c472a0dSDavid Yang if (res) 3149c472a0dSDavid Yang return res; 3159c472a0dSDavid Yang 3169c472a0dSDavid Yang cnt++; 3179c472a0dSDavid Yang } 3189c472a0dSDavid Yang 3199c472a0dSDavid Yang ctrl = YT921X_LAG_GROUP_PORTS(ports_mask) | 3209c472a0dSDavid Yang YT921X_LAG_GROUP_MEMBER_NUM(cnt); 3219c472a0dSDavid Yang return yt921x_reg_write(priv, YT921X_LAG_GROUPn(index), ctrl); 3229c472a0dSDavid Yang } 3239c472a0dSDavid Yang 3249c472a0dSDavid Yang static int 3259c472a0dSDavid Yang yt921x_dsa_port_lag_leave(struct dsa_switch *ds, int port, struct dsa_lag lag) 3269c472a0dSDavid Yang { 327cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 3289c472a0dSDavid Yang struct dsa_port *dp; 3299c472a0dSDavid Yang u32 ctrl; 3309c472a0dSDavid Yang int res; 3319c472a0dSDavid Yang 3329c472a0dSDavid Yang if (!lag.id) 3339c472a0dSDavid Yang return -EINVAL; 3349c472a0dSDavid Yang 3359c472a0dSDavid Yang ctrl = 0; 3369c472a0dSDavid Yang dsa_lag_foreach_port(dp, ds->dst, &lag) 3379c472a0dSDavid Yang ctrl |= BIT(dp->index); 3389c472a0dSDavid Yang 3399c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 3409c472a0dSDavid Yang res = yt921x_lag_set(priv, lag.id - 1, ctrl); 3419c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 3429c472a0dSDavid Yang 3439c472a0dSDavid Yang return res; 3449c472a0dSDavid Yang } 3459c472a0dSDavid Yang 3469c472a0dSDavid Yang static int 3479c472a0dSDavid Yang yt921x_dsa_port_lag_check(struct dsa_switch *ds, struct dsa_lag lag, 3489c472a0dSDavid Yang struct netdev_lag_upper_info *info, 3499c472a0dSDavid Yang struct netlink_ext_ack *extack) 3509c472a0dSDavid Yang { 3519c472a0dSDavid Yang unsigned int members; 3529c472a0dSDavid Yang struct dsa_port *dp; 3539c472a0dSDavid Yang 3549c472a0dSDavid Yang if (!lag.id) 3559c472a0dSDavid Yang return -EINVAL; 3569c472a0dSDavid Yang 3579c472a0dSDavid Yang members = 0; 3589c472a0dSDavid Yang dsa_lag_foreach_port(dp, ds->dst, &lag) 3599c472a0dSDavid Yang /* Includes the port joining the LAG */ 3609c472a0dSDavid Yang members++; 3619c472a0dSDavid Yang 3629c472a0dSDavid Yang if (members > YT921X_LAG_PORT_NUM) { 3639c472a0dSDavid Yang NL_SET_ERR_MSG_MOD(extack, 3649c472a0dSDavid Yang "Cannot offload more than 4 LAG ports"); 3659c472a0dSDavid Yang return -EOPNOTSUPP; 3669c472a0dSDavid Yang } 3679c472a0dSDavid Yang 3689c472a0dSDavid Yang if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) { 3699c472a0dSDavid Yang NL_SET_ERR_MSG_MOD(extack, 3709c472a0dSDavid Yang "Can only offload LAG using hash TX type"); 3719c472a0dSDavid Yang return -EOPNOTSUPP; 3729c472a0dSDavid Yang } 3739c472a0dSDavid Yang 3749c472a0dSDavid Yang if (info->hash_type != NETDEV_LAG_HASH_L2 && 3759c472a0dSDavid Yang info->hash_type != NETDEV_LAG_HASH_L23 && 3769c472a0dSDavid Yang info->hash_type != NETDEV_LAG_HASH_L34) { 3779c472a0dSDavid Yang NL_SET_ERR_MSG_MOD(extack, 3789c472a0dSDavid Yang "Can only offload L2 or L2+L3 or L3+L4 TX hash"); 3799c472a0dSDavid Yang return -EOPNOTSUPP; 3809c472a0dSDavid Yang } 3819c472a0dSDavid Yang 3829c472a0dSDavid Yang return 0; 3839c472a0dSDavid Yang } 3849c472a0dSDavid Yang 3859c472a0dSDavid Yang static int 3869c472a0dSDavid Yang yt921x_dsa_port_lag_join(struct dsa_switch *ds, int port, struct dsa_lag lag, 3879c472a0dSDavid Yang struct netdev_lag_upper_info *info, 3889c472a0dSDavid Yang struct netlink_ext_ack *extack) 3899c472a0dSDavid Yang { 390cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 3919c472a0dSDavid Yang struct dsa_port *dp; 3929c472a0dSDavid Yang bool unique_lag; 3939c472a0dSDavid Yang unsigned int i; 3949c472a0dSDavid Yang u32 ctrl; 3959c472a0dSDavid Yang int res; 3969c472a0dSDavid Yang 3979c472a0dSDavid Yang res = yt921x_dsa_port_lag_check(ds, lag, info, extack); 3989c472a0dSDavid Yang if (res) 3999c472a0dSDavid Yang return res; 4009c472a0dSDavid Yang 4019c472a0dSDavid Yang ctrl = 0; 4029c472a0dSDavid Yang switch (info->hash_type) { 4039c472a0dSDavid Yang case NETDEV_LAG_HASH_L34: 4049c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_DST; 4059c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_SRC; 4069c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_PROTO; 4079c472a0dSDavid Yang 4089c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_L4_DPORT; 4099c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_L4_SPORT; 4109c472a0dSDavid Yang break; 4119c472a0dSDavid Yang case NETDEV_LAG_HASH_L23: 4129c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_MAC_DA; 4139c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_MAC_SA; 4149c472a0dSDavid Yang 4159c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_DST; 4169c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_SRC; 4179c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_IP_PROTO; 4189c472a0dSDavid Yang break; 4199c472a0dSDavid Yang case NETDEV_LAG_HASH_L2: 4209c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_MAC_DA; 4219c472a0dSDavid Yang ctrl |= YT921X_LAG_HASH_MAC_SA; 4229c472a0dSDavid Yang break; 4239c472a0dSDavid Yang default: 4249c472a0dSDavid Yang return -EOPNOTSUPP; 4259c472a0dSDavid Yang } 4269c472a0dSDavid Yang 4279c472a0dSDavid Yang /* Check if we are the unique configured LAG */ 4289c472a0dSDavid Yang unique_lag = true; 4299c472a0dSDavid Yang dsa_lags_foreach_id(i, ds->dst) 4309c472a0dSDavid Yang if (i != lag.id && dsa_lag_by_id(ds->dst, i)) { 4319c472a0dSDavid Yang unique_lag = false; 4329c472a0dSDavid Yang break; 4339c472a0dSDavid Yang } 4349c472a0dSDavid Yang 4359c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 4369c472a0dSDavid Yang do { 4379c472a0dSDavid Yang res = yt921x_lag_hash(priv, ctrl, unique_lag, extack); 4389c472a0dSDavid Yang if (res) 4399c472a0dSDavid Yang break; 4409c472a0dSDavid Yang 4419c472a0dSDavid Yang ctrl = 0; 4429c472a0dSDavid Yang dsa_lag_foreach_port(dp, ds->dst, &lag) 4439c472a0dSDavid Yang ctrl |= BIT(dp->index); 4449c472a0dSDavid Yang res = yt921x_lag_set(priv, lag.id - 1, ctrl); 4459c472a0dSDavid Yang } while (0); 4469c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 4479c472a0dSDavid Yang 4489c472a0dSDavid Yang return res; 4499c472a0dSDavid Yang } 4509c472a0dSDavid Yang 4519c472a0dSDavid Yang static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp) 4529c472a0dSDavid Yang { 453cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 4549c472a0dSDavid Yang u32 val = YT921X_FDB_RESULT_DONE; 4559c472a0dSDavid Yang int res; 4569c472a0dSDavid Yang 4579c472a0dSDavid Yang res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE, 4589c472a0dSDavid Yang &val); 4599c472a0dSDavid Yang if (res) { 4609c472a0dSDavid Yang dev_err(dev, "FDB probably stuck\n"); 4619c472a0dSDavid Yang return res; 4629c472a0dSDavid Yang } 4639c472a0dSDavid Yang 4649c472a0dSDavid Yang *valp = val; 4659c472a0dSDavid Yang return 0; 4669c472a0dSDavid Yang } 4679c472a0dSDavid Yang 4689c472a0dSDavid Yang static int 4699c472a0dSDavid Yang yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, 4709c472a0dSDavid Yang u16 vid, u32 ctrl1) 4719c472a0dSDavid Yang { 4729c472a0dSDavid Yang u32 ctrl; 4739c472a0dSDavid Yang int res; 4749c472a0dSDavid Yang 4759c472a0dSDavid Yang ctrl = ethaddr_hi4_to_u32(addr); 4769c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); 4779c472a0dSDavid Yang if (res) 4789c472a0dSDavid Yang return res; 4799c472a0dSDavid Yang 4809c472a0dSDavid Yang ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | ethaddr_lo2_to_u32(addr); 4819c472a0dSDavid Yang return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); 4829c472a0dSDavid Yang } 4839c472a0dSDavid Yang 4849c472a0dSDavid Yang static int 4859c472a0dSDavid Yang yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 4869c472a0dSDavid Yang u16 *indexp) 4879c472a0dSDavid Yang { 4889c472a0dSDavid Yang u32 ctrl; 4899c472a0dSDavid Yang u32 val; 4909c472a0dSDavid Yang int res; 4919c472a0dSDavid Yang 4929c472a0dSDavid Yang res = yt921x_fdb_in01(priv, addr, vid, 0); 4939c472a0dSDavid Yang if (res) 4949c472a0dSDavid Yang return res; 4959c472a0dSDavid Yang 4969c472a0dSDavid Yang ctrl = 0; 4979c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 4989c472a0dSDavid Yang if (res) 4999c472a0dSDavid Yang return res; 5009c472a0dSDavid Yang 5019c472a0dSDavid Yang ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; 5029c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 5039c472a0dSDavid Yang if (res) 5049c472a0dSDavid Yang return res; 5059c472a0dSDavid Yang 5069c472a0dSDavid Yang res = yt921x_fdb_wait(priv, &val); 5079c472a0dSDavid Yang if (res) 5089c472a0dSDavid Yang return res; 5099c472a0dSDavid Yang if (val & YT921X_FDB_RESULT_NOTFOUND) { 5109c472a0dSDavid Yang *indexp = YT921X_FDB_NUM; 5119c472a0dSDavid Yang return 0; 5129c472a0dSDavid Yang } 5139c472a0dSDavid Yang 5149c472a0dSDavid Yang *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); 5159c472a0dSDavid Yang return 0; 5169c472a0dSDavid Yang } 5179c472a0dSDavid Yang 5189c472a0dSDavid Yang static int 5199c472a0dSDavid Yang yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp, 5209c472a0dSDavid Yang u16 *ports_maskp, u16 *indexp, u8 *statusp) 5219c472a0dSDavid Yang { 522cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 5239c472a0dSDavid Yang u16 index; 5249c472a0dSDavid Yang u32 data0; 5259c472a0dSDavid Yang u32 data1; 5269c472a0dSDavid Yang u32 data2; 5279c472a0dSDavid Yang u32 val; 5289c472a0dSDavid Yang int res; 5299c472a0dSDavid Yang 5309c472a0dSDavid Yang res = yt921x_fdb_wait(priv, &val); 5319c472a0dSDavid Yang if (res) 5329c472a0dSDavid Yang return res; 5339c472a0dSDavid Yang if (val & YT921X_FDB_RESULT_NOTFOUND) { 5349c472a0dSDavid Yang *ports_maskp = 0; 5359c472a0dSDavid Yang return 0; 5369c472a0dSDavid Yang } 5379c472a0dSDavid Yang index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); 5389c472a0dSDavid Yang 5399c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1); 5409c472a0dSDavid Yang if (res) 5419c472a0dSDavid Yang return res; 5429c472a0dSDavid Yang if ((data1 & YT921X_FDB_IO1_STATUS_M) == 5439c472a0dSDavid Yang YT921X_FDB_IO1_STATUS_INVALID) { 5449c472a0dSDavid Yang *ports_maskp = 0; 5459c472a0dSDavid Yang return 0; 5469c472a0dSDavid Yang } 5479c472a0dSDavid Yang 5489c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0); 5499c472a0dSDavid Yang if (res) 5509c472a0dSDavid Yang return res; 5519c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2); 5529c472a0dSDavid Yang if (res) 5539c472a0dSDavid Yang return res; 5549c472a0dSDavid Yang 5559c472a0dSDavid Yang addr[0] = data0 >> 24; 5569c472a0dSDavid Yang addr[1] = data0 >> 16; 5579c472a0dSDavid Yang addr[2] = data0 >> 8; 5589c472a0dSDavid Yang addr[3] = data0; 5599c472a0dSDavid Yang addr[4] = data1 >> 8; 5609c472a0dSDavid Yang addr[5] = data1; 5619c472a0dSDavid Yang *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1); 5629c472a0dSDavid Yang *indexp = index; 5639c472a0dSDavid Yang *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2); 5649c472a0dSDavid Yang *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1); 5659c472a0dSDavid Yang 5669c472a0dSDavid Yang dev_dbg(dev, 5679c472a0dSDavid Yang "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n", 5689c472a0dSDavid Yang __func__, *indexp, addr[0], addr[1], addr[2], addr[3], 5699c472a0dSDavid Yang addr[4], addr[5], *vidp, *ports_maskp, *statusp); 5709c472a0dSDavid Yang return 0; 5719c472a0dSDavid Yang } 5729c472a0dSDavid Yang 5739c472a0dSDavid Yang static int 5749c472a0dSDavid Yang yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask, 5759c472a0dSDavid Yang dsa_fdb_dump_cb_t *cb, void *data) 5769c472a0dSDavid Yang { 5779c472a0dSDavid Yang unsigned char addr[ETH_ALEN]; 5789c472a0dSDavid Yang u8 status; 5799c472a0dSDavid Yang u16 pmask; 5809c472a0dSDavid Yang u16 index; 5819c472a0dSDavid Yang u32 ctrl; 5829c472a0dSDavid Yang u16 vid; 5839c472a0dSDavid Yang int res; 5849c472a0dSDavid Yang 5859c472a0dSDavid Yang ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX | 5869c472a0dSDavid Yang YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; 5879c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 5889c472a0dSDavid Yang if (res) 5899c472a0dSDavid Yang return res; 5909c472a0dSDavid Yang res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status); 5919c472a0dSDavid Yang if (res) 5929c472a0dSDavid Yang return res; 5939c472a0dSDavid Yang if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { 5949c472a0dSDavid Yang res = cb(addr, vid, 5959c472a0dSDavid Yang status == YT921X_FDB_ENTRY_STATUS_STATIC, data); 5969c472a0dSDavid Yang if (res) 5979c472a0dSDavid Yang return res; 5989c472a0dSDavid Yang } 5999c472a0dSDavid Yang 6009c472a0dSDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 6019c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 6029c472a0dSDavid Yang if (res) 6039c472a0dSDavid Yang return res; 6049c472a0dSDavid Yang 6059c472a0dSDavid Yang index = 0; 6069c472a0dSDavid Yang do { 6079c472a0dSDavid Yang ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | 6089c472a0dSDavid Yang YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT | 6099c472a0dSDavid Yang YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START; 6109c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 6119c472a0dSDavid Yang if (res) 6129c472a0dSDavid Yang return res; 6139c472a0dSDavid Yang 6149c472a0dSDavid Yang res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, 6159c472a0dSDavid Yang &status); 6169c472a0dSDavid Yang if (res) 6179c472a0dSDavid Yang return res; 6189c472a0dSDavid Yang if (!pmask) 6199c472a0dSDavid Yang break; 6209c472a0dSDavid Yang 6219c472a0dSDavid Yang if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { 6229c472a0dSDavid Yang res = cb(addr, vid, 6239c472a0dSDavid Yang status == YT921X_FDB_ENTRY_STATUS_STATIC, 6249c472a0dSDavid Yang data); 6259c472a0dSDavid Yang if (res) 6269c472a0dSDavid Yang return res; 6279c472a0dSDavid Yang } 6289c472a0dSDavid Yang 6299c472a0dSDavid Yang /* Never call GET_NEXT with 4095, otherwise it will hang 6309c472a0dSDavid Yang * forever until a reset! 6319c472a0dSDavid Yang */ 6329c472a0dSDavid Yang } while (index < YT921X_FDB_NUM - 1); 6339c472a0dSDavid Yang 6349c472a0dSDavid Yang return 0; 6359c472a0dSDavid Yang } 6369c472a0dSDavid Yang 6379c472a0dSDavid Yang static int 6389c472a0dSDavid Yang yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid, 6399c472a0dSDavid Yang bool flush_static) 6409c472a0dSDavid Yang { 6419c472a0dSDavid Yang u32 ctrl; 6429c472a0dSDavid Yang u32 val; 6439c472a0dSDavid Yang int res; 6449c472a0dSDavid Yang 6459c472a0dSDavid Yang if (vid < 4096) { 6469c472a0dSDavid Yang ctrl = YT921X_FDB_IO1_FID(vid); 6479c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); 6489c472a0dSDavid Yang if (res) 6499c472a0dSDavid Yang return res; 6509c472a0dSDavid Yang } 6519c472a0dSDavid Yang 6529c472a0dSDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 6539c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 6549c472a0dSDavid Yang if (res) 6559c472a0dSDavid Yang return res; 6569c472a0dSDavid Yang 6579c472a0dSDavid Yang ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START; 6589c472a0dSDavid Yang if (vid >= 4096) 6599c472a0dSDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_PORT; 6609c472a0dSDavid Yang else 6619c472a0dSDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID; 6629c472a0dSDavid Yang if (flush_static) 6639c472a0dSDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_STATIC; 6649c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 6659c472a0dSDavid Yang if (res) 6669c472a0dSDavid Yang return res; 6679c472a0dSDavid Yang 6689c472a0dSDavid Yang res = yt921x_fdb_wait(priv, &val); 6699c472a0dSDavid Yang if (res) 6709c472a0dSDavid Yang return res; 6719c472a0dSDavid Yang 6729c472a0dSDavid Yang return 0; 6739c472a0dSDavid Yang } 6749c472a0dSDavid Yang 6759c472a0dSDavid Yang static int 6769c472a0dSDavid Yang yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static) 6779c472a0dSDavid Yang { 6789c472a0dSDavid Yang return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static); 6799c472a0dSDavid Yang } 6809c472a0dSDavid Yang 6819c472a0dSDavid Yang static int 682*5d7f3375SDavid Yang yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u32 ctrl1, 683*5d7f3375SDavid Yang u32 ctrl2) 6849c472a0dSDavid Yang { 6859c472a0dSDavid Yang u32 ctrl; 6869c472a0dSDavid Yang u32 val; 6879c472a0dSDavid Yang int res; 6889c472a0dSDavid Yang 6899c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1); 6909c472a0dSDavid Yang if (res) 6919c472a0dSDavid Yang return res; 6929c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2); 6939c472a0dSDavid Yang if (res) 6949c472a0dSDavid Yang return res; 6959c472a0dSDavid Yang 6969c472a0dSDavid Yang ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | 6979c472a0dSDavid Yang YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; 6989c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 6999c472a0dSDavid Yang if (res) 7009c472a0dSDavid Yang return res; 7019c472a0dSDavid Yang 7029c472a0dSDavid Yang return yt921x_fdb_wait(priv, &val); 7039c472a0dSDavid Yang } 7049c472a0dSDavid Yang 7059c472a0dSDavid Yang static int 7069c472a0dSDavid Yang yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 7079c472a0dSDavid Yang u16 ports_mask) 7089c472a0dSDavid Yang { 7099c472a0dSDavid Yang u32 ctrl; 7109c472a0dSDavid Yang u32 val; 7119c472a0dSDavid Yang int res; 7129c472a0dSDavid Yang 7139c472a0dSDavid Yang ctrl = YT921X_FDB_IO1_STATUS_STATIC; 7149c472a0dSDavid Yang res = yt921x_fdb_in01(priv, addr, vid, ctrl); 7159c472a0dSDavid Yang if (res) 7169c472a0dSDavid Yang return res; 7179c472a0dSDavid Yang 7189c472a0dSDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 7199c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 7209c472a0dSDavid Yang if (res) 7219c472a0dSDavid Yang return res; 7229c472a0dSDavid Yang 7239c472a0dSDavid Yang ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; 7249c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 7259c472a0dSDavid Yang if (res) 7269c472a0dSDavid Yang return res; 7279c472a0dSDavid Yang 7289c472a0dSDavid Yang return yt921x_fdb_wait(priv, &val); 7299c472a0dSDavid Yang } 7309c472a0dSDavid Yang 7319c472a0dSDavid Yang static int 7329c472a0dSDavid Yang yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr, 7339c472a0dSDavid Yang u16 vid, u16 ports_mask) 7349c472a0dSDavid Yang { 7359c472a0dSDavid Yang u16 index; 7369c472a0dSDavid Yang u32 ctrl1; 7379c472a0dSDavid Yang u32 ctrl2; 7389c472a0dSDavid Yang u32 ctrl; 7399c472a0dSDavid Yang u32 val2; 7409c472a0dSDavid Yang u32 val; 7419c472a0dSDavid Yang int res; 7429c472a0dSDavid Yang 7439c472a0dSDavid Yang /* Check for presence */ 7449c472a0dSDavid Yang res = yt921x_fdb_has(priv, addr, vid, &index); 7459c472a0dSDavid Yang if (res) 7469c472a0dSDavid Yang return res; 7479c472a0dSDavid Yang if (index >= YT921X_FDB_NUM) 7489c472a0dSDavid Yang return 0; 7499c472a0dSDavid Yang 7509c472a0dSDavid Yang /* Check if action required */ 7519c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); 7529c472a0dSDavid Yang if (res) 7539c472a0dSDavid Yang return res; 7549c472a0dSDavid Yang 7559c472a0dSDavid Yang ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask); 7569c472a0dSDavid Yang if (ctrl2 == val2) 7579c472a0dSDavid Yang return 0; 7589c472a0dSDavid Yang if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) { 7599c472a0dSDavid Yang ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START; 7609c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 7619c472a0dSDavid Yang if (res) 7629c472a0dSDavid Yang return res; 7639c472a0dSDavid Yang 7649c472a0dSDavid Yang return yt921x_fdb_wait(priv, &val); 7659c472a0dSDavid Yang } 7669c472a0dSDavid Yang 7679c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1); 7689c472a0dSDavid Yang if (res) 7699c472a0dSDavid Yang return res; 7709c472a0dSDavid Yang 7719c472a0dSDavid Yang return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); 7729c472a0dSDavid Yang } 7739c472a0dSDavid Yang 7749c472a0dSDavid Yang static int 7759c472a0dSDavid Yang yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 7769c472a0dSDavid Yang u16 ports_mask) 7779c472a0dSDavid Yang { 7789c472a0dSDavid Yang u16 index; 7799c472a0dSDavid Yang u32 ctrl1; 7809c472a0dSDavid Yang u32 ctrl2; 7819c472a0dSDavid Yang u32 val1; 7829c472a0dSDavid Yang u32 val2; 7839c472a0dSDavid Yang int res; 7849c472a0dSDavid Yang 7859c472a0dSDavid Yang /* Check for presence */ 7869c472a0dSDavid Yang res = yt921x_fdb_has(priv, addr, vid, &index); 7879c472a0dSDavid Yang if (res) 7889c472a0dSDavid Yang return res; 7899c472a0dSDavid Yang if (index >= YT921X_FDB_NUM) 7909c472a0dSDavid Yang return yt921x_fdb_add(priv, addr, vid, ports_mask); 7919c472a0dSDavid Yang 7929c472a0dSDavid Yang /* Check if action required */ 7939c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1); 7949c472a0dSDavid Yang if (res) 7959c472a0dSDavid Yang return res; 7969c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); 7979c472a0dSDavid Yang if (res) 7989c472a0dSDavid Yang return res; 7999c472a0dSDavid Yang 8009c472a0dSDavid Yang ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M; 8019c472a0dSDavid Yang ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC; 8029c472a0dSDavid Yang ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask); 8039c472a0dSDavid Yang if (ctrl1 == val1 && ctrl2 == val2) 8049c472a0dSDavid Yang return 0; 8059c472a0dSDavid Yang 8069c472a0dSDavid Yang return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); 8079c472a0dSDavid Yang } 8089c472a0dSDavid Yang 8099c472a0dSDavid Yang static int 8109c472a0dSDavid Yang yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port, 8119c472a0dSDavid Yang dsa_fdb_dump_cb_t *cb, void *data) 8129c472a0dSDavid Yang { 813cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 8149c472a0dSDavid Yang int res; 8159c472a0dSDavid Yang 8169c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8179c472a0dSDavid Yang /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" 8189c472a0dSDavid Yang * only wants to see unicast 8199c472a0dSDavid Yang */ 8209c472a0dSDavid Yang res = yt921x_fdb_dump(priv, BIT(port), cb, data); 8219c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8229c472a0dSDavid Yang 8239c472a0dSDavid Yang return res; 8249c472a0dSDavid Yang } 8259c472a0dSDavid Yang 8269c472a0dSDavid Yang static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port) 8279c472a0dSDavid Yang { 828cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 829cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 8309c472a0dSDavid Yang int res; 8319c472a0dSDavid Yang 8329c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8339c472a0dSDavid Yang res = yt921x_fdb_flush_port(priv, port, false); 8349c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8359c472a0dSDavid Yang 8369c472a0dSDavid Yang if (res) 8379c472a0dSDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for", 8389c472a0dSDavid Yang port, res); 8399c472a0dSDavid Yang } 8409c472a0dSDavid Yang 8419c472a0dSDavid Yang static int 8429c472a0dSDavid Yang yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) 8439c472a0dSDavid Yang { 844cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 8459c472a0dSDavid Yang u32 ctrl; 8469c472a0dSDavid Yang int res; 8479c472a0dSDavid Yang 8489c472a0dSDavid Yang /* AGEING reg is set in 5s step */ 8499c472a0dSDavid Yang ctrl = clamp(msecs / 5000, 1, U16_MAX); 8509c472a0dSDavid Yang 8519c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8529c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_AGEING, ctrl); 8539c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8549c472a0dSDavid Yang 8559c472a0dSDavid Yang return res; 8569c472a0dSDavid Yang } 8579c472a0dSDavid Yang 8589c472a0dSDavid Yang static int 8599c472a0dSDavid Yang yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port, 8609c472a0dSDavid Yang const unsigned char *addr, u16 vid, struct dsa_db db) 8619c472a0dSDavid Yang { 862cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 8639c472a0dSDavid Yang int res; 8649c472a0dSDavid Yang 8659c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8669c472a0dSDavid Yang res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); 8679c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8689c472a0dSDavid Yang 8699c472a0dSDavid Yang return res; 8709c472a0dSDavid Yang } 8719c472a0dSDavid Yang 8729c472a0dSDavid Yang static int 8739c472a0dSDavid Yang yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port, 8749c472a0dSDavid Yang const unsigned char *addr, u16 vid, struct dsa_db db) 8759c472a0dSDavid Yang { 876cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 8779c472a0dSDavid Yang int res; 8789c472a0dSDavid Yang 8799c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8809c472a0dSDavid Yang res = yt921x_fdb_join(priv, addr, vid, BIT(port)); 8819c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8829c472a0dSDavid Yang 8839c472a0dSDavid Yang return res; 8849c472a0dSDavid Yang } 8859c472a0dSDavid Yang 8869c472a0dSDavid Yang static int 8879c472a0dSDavid Yang yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port, 8889c472a0dSDavid Yang const struct switchdev_obj_port_mdb *mdb, 8899c472a0dSDavid Yang struct dsa_db db) 8909c472a0dSDavid Yang { 891cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 8929c472a0dSDavid Yang const unsigned char *addr = mdb->addr; 8939c472a0dSDavid Yang u16 vid = mdb->vid; 8949c472a0dSDavid Yang int res; 8959c472a0dSDavid Yang 8969c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 8979c472a0dSDavid Yang res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); 8989c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 8999c472a0dSDavid Yang 9009c472a0dSDavid Yang return res; 9019c472a0dSDavid Yang } 9029c472a0dSDavid Yang 9039c472a0dSDavid Yang static int 9049c472a0dSDavid Yang yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port, 9059c472a0dSDavid Yang const struct switchdev_obj_port_mdb *mdb, 9069c472a0dSDavid Yang struct dsa_db db) 9079c472a0dSDavid Yang { 908cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 9099c472a0dSDavid Yang const unsigned char *addr = mdb->addr; 9109c472a0dSDavid Yang u16 vid = mdb->vid; 9119c472a0dSDavid Yang int res; 9129c472a0dSDavid Yang 9139c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 9149c472a0dSDavid Yang res = yt921x_fdb_join(priv, addr, vid, BIT(port)); 9159c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 9169c472a0dSDavid Yang 9179c472a0dSDavid Yang return res; 9189c472a0dSDavid Yang } 9199c472a0dSDavid Yang 9209c472a0dSDavid Yang static int 9219c472a0dSDavid Yang yt921x_vlan_aware_set(struct yt921x_priv *priv, int port, bool vlan_aware) 9229c472a0dSDavid Yang { 9239c472a0dSDavid Yang u32 ctrl; 9249c472a0dSDavid Yang 9259c472a0dSDavid Yang /* Abuse SVLAN for PCP parsing without polluting the FDB - it just works 9269c472a0dSDavid Yang * despite YT921X_VLAN_CTRL_SVLAN_EN never being set 9279c472a0dSDavid Yang */ 9289c472a0dSDavid Yang if (!vlan_aware) 9299c472a0dSDavid Yang ctrl = YT921X_PORT_IGR_TPIDn_STAG(0); 9309c472a0dSDavid Yang else 9319c472a0dSDavid Yang ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0); 9329c472a0dSDavid Yang return yt921x_reg_write(priv, YT921X_PORTn_IGR_TPID(port), ctrl); 9339c472a0dSDavid Yang } 9349c472a0dSDavid Yang 9359c472a0dSDavid Yang static int 9369c472a0dSDavid Yang yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid) 9379c472a0dSDavid Yang { 9389c472a0dSDavid Yang u32 mask; 9399c472a0dSDavid Yang u32 ctrl; 9409c472a0dSDavid Yang 9419c472a0dSDavid Yang mask = YT921X_PORT_VLAN_CTRL_CVID_M; 9429c472a0dSDavid Yang ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid); 9439c472a0dSDavid Yang return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port), 9449c472a0dSDavid Yang mask, ctrl); 9459c472a0dSDavid Yang } 9469c472a0dSDavid Yang 9479c472a0dSDavid Yang static int 9489c472a0dSDavid Yang yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering) 9499c472a0dSDavid Yang { 9509c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 9519c472a0dSDavid Yang struct net_device *bdev; 9529c472a0dSDavid Yang u16 pvid; 9539c472a0dSDavid Yang u32 mask; 9549c472a0dSDavid Yang u32 ctrl; 9559c472a0dSDavid Yang int res; 9569c472a0dSDavid Yang 9579c472a0dSDavid Yang bdev = dsa_port_bridge_dev_get(dp); 9589c472a0dSDavid Yang 9599c472a0dSDavid Yang if (!bdev || !vlan_filtering) 9609c472a0dSDavid Yang pvid = YT921X_VID_UNWARE; 9619c472a0dSDavid Yang else 9629c472a0dSDavid Yang br_vlan_get_pvid(bdev, &pvid); 9639c472a0dSDavid Yang res = yt921x_port_set_pvid(priv, port, pvid); 9649c472a0dSDavid Yang if (res) 9659c472a0dSDavid Yang return res; 9669c472a0dSDavid Yang 9679c472a0dSDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED | 9689c472a0dSDavid Yang YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 9699c472a0dSDavid Yang ctrl = 0; 9709c472a0dSDavid Yang /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */ 9719c472a0dSDavid Yang if (vlan_filtering && !pvid) 9729c472a0dSDavid Yang ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 9739c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), 9749c472a0dSDavid Yang mask, ctrl); 9759c472a0dSDavid Yang if (res) 9769c472a0dSDavid Yang return res; 9779c472a0dSDavid Yang 9789c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER, 9799c472a0dSDavid Yang YT921X_VLAN_IGR_FILTER_PORTn(port), 9809c472a0dSDavid Yang vlan_filtering); 9819c472a0dSDavid Yang if (res) 9829c472a0dSDavid Yang return res; 9839c472a0dSDavid Yang 9849c472a0dSDavid Yang res = yt921x_vlan_aware_set(priv, port, vlan_filtering); 9859c472a0dSDavid Yang if (res) 9869c472a0dSDavid Yang return res; 9879c472a0dSDavid Yang 9889c472a0dSDavid Yang return 0; 9899c472a0dSDavid Yang } 9909c472a0dSDavid Yang 9919c472a0dSDavid Yang static int yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) 9929c472a0dSDavid Yang { 9939c472a0dSDavid Yang u32 masks[2]; 9949c472a0dSDavid Yang 9959c472a0dSDavid Yang masks[0] = YT921X_VLAN_CTRLa_PORTn(port); 9969c472a0dSDavid Yang masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); 9979c472a0dSDavid Yang 9989c472a0dSDavid Yang return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), masks); 9999c472a0dSDavid Yang } 10009c472a0dSDavid Yang 10019c472a0dSDavid Yang static int 10029c472a0dSDavid Yang yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) 10039c472a0dSDavid Yang { 10049c472a0dSDavid Yang u32 masks[2]; 10059c472a0dSDavid Yang u32 ctrls[2]; 10069c472a0dSDavid Yang 10079c472a0dSDavid Yang masks[0] = YT921X_VLAN_CTRLa_PORTn(port) | 10089c472a0dSDavid Yang YT921X_VLAN_CTRLa_PORTS(priv->cpu_ports_mask); 10099c472a0dSDavid Yang ctrls[0] = masks[0]; 10109c472a0dSDavid Yang 10119c472a0dSDavid Yang masks[1] = YT921X_VLAN_CTRLb_UNTAG_PORTn(port); 10129c472a0dSDavid Yang ctrls[1] = untagged ? masks[1] : 0; 10139c472a0dSDavid Yang 10149c472a0dSDavid Yang return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), 10159c472a0dSDavid Yang masks, ctrls); 10169c472a0dSDavid Yang } 10179c472a0dSDavid Yang 10189c472a0dSDavid Yang static int 10199c472a0dSDavid Yang yt921x_pvid_clear(struct yt921x_priv *priv, int port) 10209c472a0dSDavid Yang { 10219c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 10229c472a0dSDavid Yang bool vlan_filtering; 10239c472a0dSDavid Yang u32 mask; 10249c472a0dSDavid Yang int res; 10259c472a0dSDavid Yang 10269c472a0dSDavid Yang vlan_filtering = dsa_port_is_vlan_filtering(dp); 10279c472a0dSDavid Yang 10289c472a0dSDavid Yang res = yt921x_port_set_pvid(priv, port, 10299c472a0dSDavid Yang vlan_filtering ? 0 : YT921X_VID_UNWARE); 10309c472a0dSDavid Yang if (res) 10319c472a0dSDavid Yang return res; 10329c472a0dSDavid Yang 10339c472a0dSDavid Yang if (vlan_filtering) { 10349c472a0dSDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 10359c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), 10369c472a0dSDavid Yang mask); 10379c472a0dSDavid Yang if (res) 10389c472a0dSDavid Yang return res; 10399c472a0dSDavid Yang } 10409c472a0dSDavid Yang 10419c472a0dSDavid Yang return 0; 10429c472a0dSDavid Yang } 10439c472a0dSDavid Yang 10449c472a0dSDavid Yang static int 10459c472a0dSDavid Yang yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid) 10469c472a0dSDavid Yang { 10479c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 10489c472a0dSDavid Yang bool vlan_filtering; 10499c472a0dSDavid Yang u32 mask; 10509c472a0dSDavid Yang int res; 10519c472a0dSDavid Yang 10529c472a0dSDavid Yang vlan_filtering = dsa_port_is_vlan_filtering(dp); 10539c472a0dSDavid Yang 10549c472a0dSDavid Yang if (vlan_filtering) { 10559c472a0dSDavid Yang res = yt921x_port_set_pvid(priv, port, vid); 10569c472a0dSDavid Yang if (res) 10579c472a0dSDavid Yang return res; 10589c472a0dSDavid Yang } 10599c472a0dSDavid Yang 10609c472a0dSDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 10619c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask); 10629c472a0dSDavid Yang if (res) 10639c472a0dSDavid Yang return res; 10649c472a0dSDavid Yang 10659c472a0dSDavid Yang return 0; 10669c472a0dSDavid Yang } 10679c472a0dSDavid Yang 10689c472a0dSDavid Yang static int 10699c472a0dSDavid Yang yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port, 10709c472a0dSDavid Yang bool vlan_filtering, 10719c472a0dSDavid Yang struct netlink_ext_ack *extack) 10729c472a0dSDavid Yang { 1073cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 10749c472a0dSDavid Yang int res; 10759c472a0dSDavid Yang 10769c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 10779c472a0dSDavid Yang return 0; 10789c472a0dSDavid Yang 10799c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 10809c472a0dSDavid Yang res = yt921x_vlan_filtering(priv, port, vlan_filtering); 10819c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 10829c472a0dSDavid Yang 10839c472a0dSDavid Yang return res; 10849c472a0dSDavid Yang } 10859c472a0dSDavid Yang 10869c472a0dSDavid Yang static int 10879c472a0dSDavid Yang yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port, 10889c472a0dSDavid Yang const struct switchdev_obj_port_vlan *vlan) 10899c472a0dSDavid Yang { 1090cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 10919c472a0dSDavid Yang u16 vid = vlan->vid; 10929c472a0dSDavid Yang u16 pvid; 10939c472a0dSDavid Yang int res; 10949c472a0dSDavid Yang 10959c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 10969c472a0dSDavid Yang return 0; 10979c472a0dSDavid Yang 10989c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 10999c472a0dSDavid Yang do { 11009c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 11019c472a0dSDavid Yang struct net_device *bdev; 11029c472a0dSDavid Yang 11039c472a0dSDavid Yang res = yt921x_vlan_del(priv, port, vid); 11049c472a0dSDavid Yang if (res) 11059c472a0dSDavid Yang break; 11069c472a0dSDavid Yang 11079c472a0dSDavid Yang bdev = dsa_port_bridge_dev_get(dp); 11089c472a0dSDavid Yang if (bdev) { 11099c472a0dSDavid Yang br_vlan_get_pvid(bdev, &pvid); 11109c472a0dSDavid Yang if (pvid == vid) 11119c472a0dSDavid Yang res = yt921x_pvid_clear(priv, port); 11129c472a0dSDavid Yang } 11139c472a0dSDavid Yang } while (0); 11149c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 11159c472a0dSDavid Yang 11169c472a0dSDavid Yang return res; 11179c472a0dSDavid Yang } 11189c472a0dSDavid Yang 11199c472a0dSDavid Yang static int 11209c472a0dSDavid Yang yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port, 11219c472a0dSDavid Yang const struct switchdev_obj_port_vlan *vlan, 11229c472a0dSDavid Yang struct netlink_ext_ack *extack) 11239c472a0dSDavid Yang { 1124cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 11259c472a0dSDavid Yang u16 vid = vlan->vid; 11269c472a0dSDavid Yang u16 pvid; 11279c472a0dSDavid Yang int res; 11289c472a0dSDavid Yang 11299c472a0dSDavid Yang /* CPU port is supposed to be a member of every VLAN; see 11309c472a0dSDavid Yang * yt921x_vlan_add() and yt921x_port_setup() 11319c472a0dSDavid Yang */ 11329c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 11339c472a0dSDavid Yang return 0; 11349c472a0dSDavid Yang 11359c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 11369c472a0dSDavid Yang do { 11379c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 11389c472a0dSDavid Yang struct net_device *bdev; 11399c472a0dSDavid Yang 11409c472a0dSDavid Yang res = yt921x_vlan_add(priv, port, vid, 11419c472a0dSDavid Yang vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 11429c472a0dSDavid Yang if (res) 11439c472a0dSDavid Yang break; 11449c472a0dSDavid Yang 11459c472a0dSDavid Yang bdev = dsa_port_bridge_dev_get(dp); 11469c472a0dSDavid Yang if (bdev) { 11479c472a0dSDavid Yang if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { 11489c472a0dSDavid Yang res = yt921x_pvid_set(priv, port, vid); 11499c472a0dSDavid Yang } else { 11509c472a0dSDavid Yang br_vlan_get_pvid(bdev, &pvid); 11519c472a0dSDavid Yang if (pvid == vid) 11529c472a0dSDavid Yang res = yt921x_pvid_clear(priv, port); 11539c472a0dSDavid Yang } 11549c472a0dSDavid Yang } 11559c472a0dSDavid Yang } while (0); 11569c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 11579c472a0dSDavid Yang 11589c472a0dSDavid Yang return res; 11599c472a0dSDavid Yang } 11609c472a0dSDavid Yang 11619c472a0dSDavid Yang static int yt921x_userport_standalone(struct yt921x_priv *priv, int port) 11629c472a0dSDavid Yang { 11639c472a0dSDavid Yang u32 mask; 11649c472a0dSDavid Yang u32 ctrl; 11659c472a0dSDavid Yang int res; 11669c472a0dSDavid Yang 11679c472a0dSDavid Yang ctrl = ~priv->cpu_ports_mask; 11689c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl); 11699c472a0dSDavid Yang if (res) 11709c472a0dSDavid Yang return res; 11719c472a0dSDavid Yang 11729c472a0dSDavid Yang /* Turn off FDB learning to prevent FDB pollution */ 11739c472a0dSDavid Yang mask = YT921X_PORT_LEARN_DIS; 11749c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask); 11759c472a0dSDavid Yang if (res) 11769c472a0dSDavid Yang return res; 11779c472a0dSDavid Yang 11789c472a0dSDavid Yang /* Turn off VLAN awareness */ 11799c472a0dSDavid Yang res = yt921x_vlan_aware_set(priv, port, false); 11809c472a0dSDavid Yang if (res) 11819c472a0dSDavid Yang return res; 11829c472a0dSDavid Yang 11839c472a0dSDavid Yang /* Unrelated since learning is off and all packets are trapped; 11849c472a0dSDavid Yang * set it anyway 11859c472a0dSDavid Yang */ 11869c472a0dSDavid Yang res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE); 11879c472a0dSDavid Yang if (res) 11889c472a0dSDavid Yang return res; 11899c472a0dSDavid Yang 11909c472a0dSDavid Yang return 0; 11919c472a0dSDavid Yang } 11929c472a0dSDavid Yang 11939c472a0dSDavid Yang static int yt921x_userport_bridge(struct yt921x_priv *priv, int port) 11949c472a0dSDavid Yang { 11959c472a0dSDavid Yang u32 mask; 11969c472a0dSDavid Yang int res; 11979c472a0dSDavid Yang 11989c472a0dSDavid Yang mask = YT921X_PORT_LEARN_DIS; 11999c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask); 12009c472a0dSDavid Yang if (res) 12019c472a0dSDavid Yang return res; 12029c472a0dSDavid Yang 12039c472a0dSDavid Yang return 0; 12049c472a0dSDavid Yang } 12059c472a0dSDavid Yang 12069c472a0dSDavid Yang static int yt921x_isolate(struct yt921x_priv *priv, int port) 12079c472a0dSDavid Yang { 12089c472a0dSDavid Yang u32 mask; 12099c472a0dSDavid Yang int res; 12109c472a0dSDavid Yang 12119c472a0dSDavid Yang mask = BIT(port); 12129c472a0dSDavid Yang for (int i = 0; i < YT921X_PORT_NUM; i++) { 12139c472a0dSDavid Yang if ((BIT(i) & priv->cpu_ports_mask) || i == port) 12149c472a0dSDavid Yang continue; 12159c472a0dSDavid Yang 12169c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i), 12179c472a0dSDavid Yang mask); 12189c472a0dSDavid Yang if (res) 12199c472a0dSDavid Yang return res; 12209c472a0dSDavid Yang } 12219c472a0dSDavid Yang 12229c472a0dSDavid Yang return 0; 12239c472a0dSDavid Yang } 12249c472a0dSDavid Yang 12259c472a0dSDavid Yang /* Make sure to include the CPU port in ports_mask, or your bridge will 12269c472a0dSDavid Yang * not have it. 12279c472a0dSDavid Yang */ 12289c472a0dSDavid Yang static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask) 12299c472a0dSDavid Yang { 12309c472a0dSDavid Yang unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask; 12319c472a0dSDavid Yang u32 isolated_mask; 12329c472a0dSDavid Yang u32 ctrl; 12339c472a0dSDavid Yang int port; 12349c472a0dSDavid Yang int res; 12359c472a0dSDavid Yang 12369c472a0dSDavid Yang isolated_mask = 0; 12379c472a0dSDavid Yang for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { 12389c472a0dSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 12399c472a0dSDavid Yang 12409c472a0dSDavid Yang if (pp->isolated) 12419c472a0dSDavid Yang isolated_mask |= BIT(port); 12429c472a0dSDavid Yang } 12439c472a0dSDavid Yang 12449c472a0dSDavid Yang /* Block from non-cpu bridge ports ... */ 12459c472a0dSDavid Yang for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { 12469c472a0dSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 12479c472a0dSDavid Yang 12489c472a0dSDavid Yang /* to non-bridge ports */ 12499c472a0dSDavid Yang ctrl = ~ports_mask; 12509c472a0dSDavid Yang /* to isolated ports when isolated */ 12519c472a0dSDavid Yang if (pp->isolated) 12529c472a0dSDavid Yang ctrl |= isolated_mask; 12539c472a0dSDavid Yang /* to itself when non-hairpin */ 12549c472a0dSDavid Yang if (!pp->hairpin) 12559c472a0dSDavid Yang ctrl |= BIT(port); 12569c472a0dSDavid Yang else 12579c472a0dSDavid Yang ctrl &= ~BIT(port); 12589c472a0dSDavid Yang 12599c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), 12609c472a0dSDavid Yang ctrl); 12619c472a0dSDavid Yang if (res) 12629c472a0dSDavid Yang return res; 12639c472a0dSDavid Yang } 12649c472a0dSDavid Yang 12659c472a0dSDavid Yang return 0; 12669c472a0dSDavid Yang } 12679c472a0dSDavid Yang 12689c472a0dSDavid Yang static int yt921x_bridge_leave(struct yt921x_priv *priv, int port) 12699c472a0dSDavid Yang { 12709c472a0dSDavid Yang int res; 12719c472a0dSDavid Yang 12729c472a0dSDavid Yang res = yt921x_userport_standalone(priv, port); 12739c472a0dSDavid Yang if (res) 12749c472a0dSDavid Yang return res; 12759c472a0dSDavid Yang 12769c472a0dSDavid Yang res = yt921x_isolate(priv, port); 12779c472a0dSDavid Yang if (res) 12789c472a0dSDavid Yang return res; 12799c472a0dSDavid Yang 12809c472a0dSDavid Yang return 0; 12819c472a0dSDavid Yang } 12829c472a0dSDavid Yang 12839c472a0dSDavid Yang static int 12849c472a0dSDavid Yang yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask) 12859c472a0dSDavid Yang { 12869c472a0dSDavid Yang int res; 12879c472a0dSDavid Yang 12889c472a0dSDavid Yang res = yt921x_userport_bridge(priv, port); 12899c472a0dSDavid Yang if (res) 12909c472a0dSDavid Yang return res; 12919c472a0dSDavid Yang 12929c472a0dSDavid Yang res = yt921x_bridge(priv, ports_mask); 12939c472a0dSDavid Yang if (res) 12949c472a0dSDavid Yang return res; 12959c472a0dSDavid Yang 12969c472a0dSDavid Yang return 0; 12979c472a0dSDavid Yang } 12989c472a0dSDavid Yang 12999c472a0dSDavid Yang static int 13009c472a0dSDavid Yang yt921x_bridge_flags(struct yt921x_priv *priv, int port, 13019c472a0dSDavid Yang struct switchdev_brport_flags flags) 13029c472a0dSDavid Yang { 13039c472a0dSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 13049c472a0dSDavid Yang bool do_flush; 13059c472a0dSDavid Yang u32 mask; 13069c472a0dSDavid Yang int res; 13079c472a0dSDavid Yang 13089c472a0dSDavid Yang if (flags.mask & BR_LEARNING) { 13099c472a0dSDavid Yang bool learning = flags.val & BR_LEARNING; 13109c472a0dSDavid Yang 13119c472a0dSDavid Yang mask = YT921X_PORT_LEARN_DIS; 13129c472a0dSDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port), 13139c472a0dSDavid Yang mask, !learning); 13149c472a0dSDavid Yang if (res) 13159c472a0dSDavid Yang return res; 13169c472a0dSDavid Yang } 13179c472a0dSDavid Yang 13189c472a0dSDavid Yang /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP 13199c472a0dSDavid Yang * is set 13209c472a0dSDavid Yang */ 13219c472a0dSDavid Yang 13229c472a0dSDavid Yang /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */ 13239c472a0dSDavid Yang 13249c472a0dSDavid Yang do_flush = false; 13259c472a0dSDavid Yang if (flags.mask & BR_HAIRPIN_MODE) { 13269c472a0dSDavid Yang pp->hairpin = flags.val & BR_HAIRPIN_MODE; 13279c472a0dSDavid Yang do_flush = true; 13289c472a0dSDavid Yang } 13299c472a0dSDavid Yang if (flags.mask & BR_ISOLATED) { 13309c472a0dSDavid Yang pp->isolated = flags.val & BR_ISOLATED; 13319c472a0dSDavid Yang do_flush = true; 13329c472a0dSDavid Yang } 13339c472a0dSDavid Yang if (do_flush) { 13349c472a0dSDavid Yang struct dsa_switch *ds = &priv->ds; 13359c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 13369c472a0dSDavid Yang struct net_device *bdev; 13379c472a0dSDavid Yang 13389c472a0dSDavid Yang bdev = dsa_port_bridge_dev_get(dp); 13399c472a0dSDavid Yang if (bdev) { 13409c472a0dSDavid Yang u32 ports_mask; 13419c472a0dSDavid Yang 13429c472a0dSDavid Yang ports_mask = dsa_bridge_ports(ds, bdev); 13439c472a0dSDavid Yang ports_mask |= priv->cpu_ports_mask; 13449c472a0dSDavid Yang res = yt921x_bridge(priv, ports_mask); 13459c472a0dSDavid Yang if (res) 13469c472a0dSDavid Yang return res; 13479c472a0dSDavid Yang } 13489c472a0dSDavid Yang } 13499c472a0dSDavid Yang 13509c472a0dSDavid Yang return 0; 13519c472a0dSDavid Yang } 13529c472a0dSDavid Yang 13539c472a0dSDavid Yang static int 13549c472a0dSDavid Yang yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port, 13559c472a0dSDavid Yang struct switchdev_brport_flags flags, 13569c472a0dSDavid Yang struct netlink_ext_ack *extack) 13579c472a0dSDavid Yang { 13589c472a0dSDavid Yang if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD | 13599c472a0dSDavid Yang BR_MCAST_FLOOD | BR_ISOLATED)) 13609c472a0dSDavid Yang return -EINVAL; 13619c472a0dSDavid Yang return 0; 13629c472a0dSDavid Yang } 13639c472a0dSDavid Yang 13649c472a0dSDavid Yang static int 13659c472a0dSDavid Yang yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port, 13669c472a0dSDavid Yang struct switchdev_brport_flags flags, 13679c472a0dSDavid Yang struct netlink_ext_ack *extack) 13689c472a0dSDavid Yang { 1369cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 13709c472a0dSDavid Yang int res; 13719c472a0dSDavid Yang 13729c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 13739c472a0dSDavid Yang return 0; 13749c472a0dSDavid Yang 13759c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 13769c472a0dSDavid Yang res = yt921x_bridge_flags(priv, port, flags); 13779c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 13789c472a0dSDavid Yang 13799c472a0dSDavid Yang return res; 13809c472a0dSDavid Yang } 13819c472a0dSDavid Yang 13829c472a0dSDavid Yang static void 13839c472a0dSDavid Yang yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port, 13849c472a0dSDavid Yang struct dsa_bridge bridge) 13859c472a0dSDavid Yang { 1386cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 1387cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 13889c472a0dSDavid Yang int res; 13899c472a0dSDavid Yang 13909c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 13919c472a0dSDavid Yang return; 13929c472a0dSDavid Yang 13939c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 13949c472a0dSDavid Yang res = yt921x_bridge_leave(priv, port); 13959c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 13969c472a0dSDavid Yang 13979c472a0dSDavid Yang if (res) 13989c472a0dSDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", 13999c472a0dSDavid Yang port, res); 14009c472a0dSDavid Yang } 14019c472a0dSDavid Yang 14029c472a0dSDavid Yang static int 14039c472a0dSDavid Yang yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port, 14049c472a0dSDavid Yang struct dsa_bridge bridge, bool *tx_fwd_offload, 14059c472a0dSDavid Yang struct netlink_ext_ack *extack) 14069c472a0dSDavid Yang { 1407cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 14089c472a0dSDavid Yang u16 ports_mask; 14099c472a0dSDavid Yang int res; 14109c472a0dSDavid Yang 14119c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) 14129c472a0dSDavid Yang return 0; 14139c472a0dSDavid Yang 14149c472a0dSDavid Yang ports_mask = dsa_bridge_ports(ds, bridge.dev); 14159c472a0dSDavid Yang ports_mask |= priv->cpu_ports_mask; 14169c472a0dSDavid Yang 14179c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 14189c472a0dSDavid Yang res = yt921x_bridge_join(priv, port, ports_mask); 14199c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 14209c472a0dSDavid Yang 14219c472a0dSDavid Yang return res; 14229c472a0dSDavid Yang } 14239c472a0dSDavid Yang 14249c472a0dSDavid Yang static int 14259c472a0dSDavid Yang yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port, 14269c472a0dSDavid Yang const struct switchdev_mst_state *st) 14279c472a0dSDavid Yang { 1428cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 14299c472a0dSDavid Yang u32 mask; 14309c472a0dSDavid Yang u32 ctrl; 14319c472a0dSDavid Yang int res; 14329c472a0dSDavid Yang 14339c472a0dSDavid Yang mask = YT921X_STP_PORTn_M(port); 14349c472a0dSDavid Yang switch (st->state) { 14359c472a0dSDavid Yang case BR_STATE_DISABLED: 14369c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_DISABLED(port); 14379c472a0dSDavid Yang break; 14389c472a0dSDavid Yang case BR_STATE_LISTENING: 14399c472a0dSDavid Yang case BR_STATE_LEARNING: 14409c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 14419c472a0dSDavid Yang break; 14429c472a0dSDavid Yang case BR_STATE_FORWARDING: 14439c472a0dSDavid Yang default: 14449c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_FORWARD(port); 14459c472a0dSDavid Yang break; 14469c472a0dSDavid Yang case BR_STATE_BLOCKING: 14479c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_BLOCKING(port); 14489c472a0dSDavid Yang break; 14499c472a0dSDavid Yang } 14509c472a0dSDavid Yang 14519c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 14529c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl); 14539c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 14549c472a0dSDavid Yang 14559c472a0dSDavid Yang return res; 14569c472a0dSDavid Yang } 14579c472a0dSDavid Yang 14589c472a0dSDavid Yang static int 14599c472a0dSDavid Yang yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, 14609c472a0dSDavid Yang const struct switchdev_vlan_msti *msti) 14619c472a0dSDavid Yang { 1462cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 14639c472a0dSDavid Yang u32 masks[2]; 14649c472a0dSDavid Yang u32 ctrls[2]; 14659c472a0dSDavid Yang int res; 14669c472a0dSDavid Yang 14679c472a0dSDavid Yang if (!msti->vid) 14689c472a0dSDavid Yang return -EINVAL; 14699c472a0dSDavid Yang if (!msti->msti || msti->msti >= YT921X_MSTI_NUM) 14709c472a0dSDavid Yang return -EINVAL; 14719c472a0dSDavid Yang 14729c472a0dSDavid Yang masks[0] = 0; 14739c472a0dSDavid Yang ctrls[0] = 0; 14749c472a0dSDavid Yang masks[1] = YT921X_VLAN_CTRLb_STP_ID_M; 14759c472a0dSDavid Yang ctrls[1] = YT921X_VLAN_CTRLb_STP_ID(msti->msti); 14769c472a0dSDavid Yang 14779c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 14789c472a0dSDavid Yang res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid), 14799c472a0dSDavid Yang masks, ctrls); 14809c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 14819c472a0dSDavid Yang 14829c472a0dSDavid Yang return res; 14839c472a0dSDavid Yang } 14849c472a0dSDavid Yang 14859c472a0dSDavid Yang static void 14869c472a0dSDavid Yang yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) 14879c472a0dSDavid Yang { 1488cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 1489cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 14909c472a0dSDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 14919c472a0dSDavid Yang bool learning; 14929c472a0dSDavid Yang u32 mask; 14939c472a0dSDavid Yang u32 ctrl; 14949c472a0dSDavid Yang int res; 14959c472a0dSDavid Yang 14969c472a0dSDavid Yang mask = YT921X_STP_PORTn_M(port); 14979c472a0dSDavid Yang learning = false; 14989c472a0dSDavid Yang switch (state) { 14999c472a0dSDavid Yang case BR_STATE_DISABLED: 15009c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_DISABLED(port); 15019c472a0dSDavid Yang break; 15029c472a0dSDavid Yang case BR_STATE_LISTENING: 15039c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 15049c472a0dSDavid Yang break; 15059c472a0dSDavid Yang case BR_STATE_LEARNING: 15069c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 15079c472a0dSDavid Yang learning = dp->learning; 15089c472a0dSDavid Yang break; 15099c472a0dSDavid Yang case BR_STATE_FORWARDING: 15109c472a0dSDavid Yang default: 15119c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_FORWARD(port); 15129c472a0dSDavid Yang learning = dp->learning; 15139c472a0dSDavid Yang break; 15149c472a0dSDavid Yang case BR_STATE_BLOCKING: 15159c472a0dSDavid Yang ctrl = YT921X_STP_PORTn_BLOCKING(port); 15169c472a0dSDavid Yang break; 15179c472a0dSDavid Yang } 15189c472a0dSDavid Yang 15199c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 15209c472a0dSDavid Yang do { 15219c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl); 15229c472a0dSDavid Yang if (res) 15239c472a0dSDavid Yang break; 15249c472a0dSDavid Yang 15259c472a0dSDavid Yang mask = YT921X_PORT_LEARN_DIS; 15269c472a0dSDavid Yang ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0; 15279c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), 15289c472a0dSDavid Yang mask, ctrl); 15299c472a0dSDavid Yang } while (0); 15309c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 15319c472a0dSDavid Yang 15329c472a0dSDavid Yang if (res) 15339c472a0dSDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for", 15349c472a0dSDavid Yang port, res); 15359c472a0dSDavid Yang } 15369c472a0dSDavid Yang 15379c472a0dSDavid Yang static int __maybe_unused 15389c472a0dSDavid Yang yt921x_dsa_port_get_default_prio(struct dsa_switch *ds, int port) 15399c472a0dSDavid Yang { 1540cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 15419c472a0dSDavid Yang u32 val; 15429c472a0dSDavid Yang int res; 15439c472a0dSDavid Yang 15449c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 15459c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_PORTn_QOS(port), &val); 15469c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 15479c472a0dSDavid Yang 15489c472a0dSDavid Yang if (res) 15499c472a0dSDavid Yang return res; 15509c472a0dSDavid Yang 15519c472a0dSDavid Yang return FIELD_GET(YT921X_PORT_QOS_PRIO_M, val); 15529c472a0dSDavid Yang } 15539c472a0dSDavid Yang 15549c472a0dSDavid Yang static int __maybe_unused 15559c472a0dSDavid Yang yt921x_dsa_port_set_default_prio(struct dsa_switch *ds, int port, u8 prio) 15569c472a0dSDavid Yang { 1557cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 15589c472a0dSDavid Yang u32 mask; 15599c472a0dSDavid Yang u32 ctrl; 15609c472a0dSDavid Yang int res; 15619c472a0dSDavid Yang 15629c472a0dSDavid Yang if (prio >= YT921X_PRIO_NUM) 15639c472a0dSDavid Yang return -EINVAL; 15649c472a0dSDavid Yang 15659c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 15669c472a0dSDavid Yang mask = YT921X_PORT_QOS_PRIO_M | YT921X_PORT_QOS_PRIO_EN; 15679c472a0dSDavid Yang ctrl = YT921X_PORT_QOS_PRIO(prio) | YT921X_PORT_QOS_PRIO_EN; 15689c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_QOS(port), mask, ctrl); 15699c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 15709c472a0dSDavid Yang 15719c472a0dSDavid Yang return res; 15729c472a0dSDavid Yang } 15739c472a0dSDavid Yang 15749c472a0dSDavid Yang static int __maybe_unused appprios_cmp(const void *a, const void *b) 15759c472a0dSDavid Yang { 15769c472a0dSDavid Yang return ((const u8 *)b)[1] - ((const u8 *)a)[1]; 15779c472a0dSDavid Yang } 15789c472a0dSDavid Yang 15799c472a0dSDavid Yang static int __maybe_unused 15809c472a0dSDavid Yang yt921x_dsa_port_get_apptrust(struct dsa_switch *ds, int port, u8 *sel, 15819c472a0dSDavid Yang int *nselp) 15829c472a0dSDavid Yang { 1583cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 15849c472a0dSDavid Yang u8 appprios[2][2] = {}; 15859c472a0dSDavid Yang int nsel; 15869c472a0dSDavid Yang u32 val; 15879c472a0dSDavid Yang int res; 15889c472a0dSDavid Yang 15899c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 15909c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_PORTn_PRIO_ORD(port), &val); 15919c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 15929c472a0dSDavid Yang 15939c472a0dSDavid Yang if (res) 15949c472a0dSDavid Yang return res; 15959c472a0dSDavid Yang 15969c472a0dSDavid Yang appprios[0][0] = IEEE_8021QAZ_APP_SEL_DSCP; 15979c472a0dSDavid Yang appprios[0][1] = (val >> (3 * YT921X_APP_SEL_DSCP)) & 7; 15989c472a0dSDavid Yang appprios[1][0] = DCB_APP_SEL_PCP; 15999c472a0dSDavid Yang appprios[1][1] = (val >> (3 * YT921X_APP_SEL_CVLAN_PCP)) & 7; 16009c472a0dSDavid Yang sort(appprios, ARRAY_SIZE(appprios), sizeof(appprios[0]), appprios_cmp, 16019c472a0dSDavid Yang NULL); 16029c472a0dSDavid Yang 16039c472a0dSDavid Yang nsel = 0; 16049c472a0dSDavid Yang for (int i = 0; i < ARRAY_SIZE(appprios) && appprios[i][1]; i++) { 16059c472a0dSDavid Yang sel[nsel] = appprios[i][0]; 16069c472a0dSDavid Yang nsel++; 16079c472a0dSDavid Yang } 16089c472a0dSDavid Yang *nselp = nsel; 16099c472a0dSDavid Yang 16109c472a0dSDavid Yang return 0; 16119c472a0dSDavid Yang } 16129c472a0dSDavid Yang 16139c472a0dSDavid Yang static int __maybe_unused 16149c472a0dSDavid Yang yt921x_dsa_port_set_apptrust(struct dsa_switch *ds, int port, const u8 *sel, 16159c472a0dSDavid Yang int nsel) 16169c472a0dSDavid Yang { 1617cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 1618cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 16199c472a0dSDavid Yang u32 ctrl; 16209c472a0dSDavid Yang int res; 16219c472a0dSDavid Yang 16229c472a0dSDavid Yang if (nsel > YT921X_APP_SEL_NUM) 16239c472a0dSDavid Yang return -EINVAL; 16249c472a0dSDavid Yang 16259c472a0dSDavid Yang ctrl = 0; 16269c472a0dSDavid Yang for (int i = 0; i < nsel; i++) { 16279c472a0dSDavid Yang switch (sel[i]) { 16289c472a0dSDavid Yang case IEEE_8021QAZ_APP_SEL_DSCP: 16299c472a0dSDavid Yang ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_DSCP, 16309c472a0dSDavid Yang 7 - i); 16319c472a0dSDavid Yang break; 16329c472a0dSDavid Yang case DCB_APP_SEL_PCP: 16339c472a0dSDavid Yang ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_CVLAN_PCP, 16349c472a0dSDavid Yang 7 - i); 16359c472a0dSDavid Yang ctrl |= YT921X_PORT_PRIO_ORD_APPm(YT921X_APP_SEL_SVLAN_PCP, 16369c472a0dSDavid Yang 7 - i); 16379c472a0dSDavid Yang break; 16389c472a0dSDavid Yang default: 16399c472a0dSDavid Yang dev_err(dev, 16409c472a0dSDavid Yang "Invalid apptrust selector (at %d-th). Supported: dscp, pcp\n", 16419c472a0dSDavid Yang i + 1); 16429c472a0dSDavid Yang return -EOPNOTSUPP; 16439c472a0dSDavid Yang } 16449c472a0dSDavid Yang } 16459c472a0dSDavid Yang 16469c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 16479c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), ctrl); 16489c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 16499c472a0dSDavid Yang 16509c472a0dSDavid Yang return res; 16519c472a0dSDavid Yang } 16529c472a0dSDavid Yang 16539c472a0dSDavid Yang static int yt921x_port_down(struct yt921x_priv *priv, int port) 16549c472a0dSDavid Yang { 165507b60e1bSDavid Yang const struct yt921x_info *info = priv->info; 16569c472a0dSDavid Yang u32 mask; 16579c472a0dSDavid Yang int res; 16589c472a0dSDavid Yang 1659be5cdff0SDavid Yang mask = YT921X_PORT_CTRL_LINK_AN | YT921X_PORT_RX_MAC_EN | 1660be5cdff0SDavid Yang YT921X_PORT_TX_MAC_EN; 16619c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); 16629c472a0dSDavid Yang if (res) 16639c472a0dSDavid Yang return res; 16649c472a0dSDavid Yang 166507b60e1bSDavid Yang if (BIT(port) & info->serdes_mask) { 16669c472a0dSDavid Yang mask = YT921X_SERDES_LINK; 16679c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); 16689c472a0dSDavid Yang if (res) 16699c472a0dSDavid Yang return res; 167007b60e1bSDavid Yang } 167107b60e1bSDavid Yang if (BIT(port) & info->xmii_mask) { 16729c472a0dSDavid Yang mask = YT921X_XMII_LINK; 16739c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); 16749c472a0dSDavid Yang if (res) 16759c472a0dSDavid Yang return res; 16769c472a0dSDavid Yang } 16779c472a0dSDavid Yang 16789c472a0dSDavid Yang return 0; 16799c472a0dSDavid Yang } 16809c472a0dSDavid Yang 16819c472a0dSDavid Yang static int 16829c472a0dSDavid Yang yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, 16839c472a0dSDavid Yang phy_interface_t interface, int speed, int duplex, 16849c472a0dSDavid Yang bool tx_pause, bool rx_pause) 16859c472a0dSDavid Yang { 168607b60e1bSDavid Yang const struct yt921x_info *info = priv->info; 168707b60e1bSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 16885423f892SDavid Yang int ps = ethtool_speed_to_yt921x(speed); 16899c472a0dSDavid Yang u32 mask; 16909c472a0dSDavid Yang u32 ctrl; 16919c472a0dSDavid Yang int res; 16929c472a0dSDavid Yang 16935423f892SDavid Yang if (ps == YT921X_SPEED_INVALID) 16949c472a0dSDavid Yang return -EINVAL; 16955423f892SDavid Yang 16964b8fbb87SDavid Yang mask = YT921X_PORT_SPEED_M | YT921X_PORT_TX_MAC_EN | 16974b8fbb87SDavid Yang YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_PAUSE | 16984b8fbb87SDavid Yang YT921X_PORT_RX_PAUSE | YT921X_PORT_DUPLEX_FULL | 16994b8fbb87SDavid Yang YT921X_PORT_CTRL_LINK_AN; 17005423f892SDavid Yang ctrl = YT921X_PORT_SPEED(ps); 17019c472a0dSDavid Yang if (duplex == DUPLEX_FULL) 17029c472a0dSDavid Yang ctrl |= YT921X_PORT_DUPLEX_FULL; 17039c472a0dSDavid Yang if (tx_pause) 17049c472a0dSDavid Yang ctrl |= YT921X_PORT_TX_PAUSE; 17059c472a0dSDavid Yang if (rx_pause) 17069c472a0dSDavid Yang ctrl |= YT921X_PORT_RX_PAUSE; 17079c472a0dSDavid Yang ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; 17084b8fbb87SDavid Yang if (pp->serdes && pp->inband) 17094b8fbb87SDavid Yang ctrl |= YT921X_PORT_CTRL_LINK_AN; 17104b8fbb87SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_CTRL(port), mask, ctrl); 17119c472a0dSDavid Yang if (res) 17129c472a0dSDavid Yang return res; 17139c472a0dSDavid Yang 171407b60e1bSDavid Yang if (!(BIT(port) & (info->serdes_mask | info->xmii_mask))) 171507b60e1bSDavid Yang return 0; 171607b60e1bSDavid Yang 171707b60e1bSDavid Yang if (pp->serdes) { 17189c472a0dSDavid Yang mask = YT921X_SERDES_SPEED_M; 17195423f892SDavid Yang ctrl = YT921X_SERDES_SPEED(ps); 17209c472a0dSDavid Yang mask |= YT921X_SERDES_DUPLEX_FULL; 17219c472a0dSDavid Yang if (duplex == DUPLEX_FULL) 17229c472a0dSDavid Yang ctrl |= YT921X_SERDES_DUPLEX_FULL; 17239c472a0dSDavid Yang mask |= YT921X_SERDES_TX_PAUSE; 17249c472a0dSDavid Yang if (tx_pause) 17259c472a0dSDavid Yang ctrl |= YT921X_SERDES_TX_PAUSE; 17269c472a0dSDavid Yang mask |= YT921X_SERDES_RX_PAUSE; 17279c472a0dSDavid Yang if (rx_pause) 17289c472a0dSDavid Yang ctrl |= YT921X_SERDES_RX_PAUSE; 17299c472a0dSDavid Yang mask |= YT921X_SERDES_LINK; 17304b8fbb87SDavid Yang if (!pp->inband) 17319c472a0dSDavid Yang ctrl |= YT921X_SERDES_LINK; 17329c472a0dSDavid Yang res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), 17339c472a0dSDavid Yang mask, ctrl); 17349c472a0dSDavid Yang if (res) 17359c472a0dSDavid Yang return res; 173607b60e1bSDavid Yang } else { 17379c472a0dSDavid Yang mask = YT921X_XMII_LINK; 17389c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); 17399c472a0dSDavid Yang if (res) 17409c472a0dSDavid Yang return res; 17419c472a0dSDavid Yang 17425423f892SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED(ps); 17439c472a0dSDavid Yang if (duplex == DUPLEX_FULL) 17449c472a0dSDavid Yang ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL; 17459c472a0dSDavid Yang ctrl |= YT921X_MDIO_POLLING_LINK; 17469c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl); 17479c472a0dSDavid Yang if (res) 17489c472a0dSDavid Yang return res; 17499c472a0dSDavid Yang } 17509c472a0dSDavid Yang 17519c472a0dSDavid Yang return 0; 17529c472a0dSDavid Yang } 17539c472a0dSDavid Yang 17549c472a0dSDavid Yang static int 17559c472a0dSDavid Yang yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, 17569c472a0dSDavid Yang phy_interface_t interface) 17579c472a0dSDavid Yang { 1758cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 175907b60e1bSDavid Yang const struct yt921x_info *info = priv->info; 176007b60e1bSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 17619c472a0dSDavid Yang u32 mask; 17629c472a0dSDavid Yang int res; 17639c472a0dSDavid Yang 176407b60e1bSDavid Yang if (BIT(port) & info->internal_mask) { 176507b60e1bSDavid Yang if (interface != PHY_INTERFACE_MODE_INTERNAL) 176607b60e1bSDavid Yang goto err; 17679c472a0dSDavid Yang return 0; 17689c472a0dSDavid Yang } 17699c472a0dSDavid Yang 17709c472a0dSDavid Yang switch (interface) { 17719c472a0dSDavid Yang /* SERDES */ 17729c472a0dSDavid Yang case PHY_INTERFACE_MODE_SGMII: 17739c472a0dSDavid Yang case PHY_INTERFACE_MODE_100BASEX: 17749c472a0dSDavid Yang case PHY_INTERFACE_MODE_1000BASEX: 17759c472a0dSDavid Yang case PHY_INTERFACE_MODE_2500BASEX: 177607b60e1bSDavid Yang if (!(BIT(port) & info->serdes_mask)) 177707b60e1bSDavid Yang goto err; 177807b60e1bSDavid Yang 17799c472a0dSDavid Yang mask = YT921X_SERDES_CTRL_PORTn(port); 17809c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); 17819c472a0dSDavid Yang if (res) 17829c472a0dSDavid Yang return res; 17839c472a0dSDavid Yang 17849c472a0dSDavid Yang mask = YT921X_XMII_CTRL_PORTn(port); 17859c472a0dSDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask); 17869c472a0dSDavid Yang if (res) 17879c472a0dSDavid Yang return res; 17889c472a0dSDavid Yang 178907b60e1bSDavid Yang /* The order is quite arbitrary - we can't return to a safe 179007b60e1bSDavid Yang * state on IO errors. 179107b60e1bSDavid Yang */ 179207b60e1bSDavid Yang pp->serdes = true; 179307b60e1bSDavid Yang 17949c472a0dSDavid Yang break; 17959c472a0dSDavid Yang /* add XMII support here */ 17969c472a0dSDavid Yang default: 179707b60e1bSDavid Yang goto err; 17989c472a0dSDavid Yang } 17999c472a0dSDavid Yang 18009c472a0dSDavid Yang return 0; 180107b60e1bSDavid Yang 180207b60e1bSDavid Yang err: 180307b60e1bSDavid Yang dev_err(dev, "Wrong mode %d on port %d\n", interface, port); 180407b60e1bSDavid Yang return -EINVAL; 18059c472a0dSDavid Yang } 18069c472a0dSDavid Yang 18074b8fbb87SDavid Yang static struct phylink_pcs * 18084b8fbb87SDavid Yang yt921x_phylink_mac_select_pcs(struct phylink_config *config, 18094b8fbb87SDavid Yang phy_interface_t interface) 18104b8fbb87SDavid Yang { 18114b8fbb87SDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 1812cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); 18134b8fbb87SDavid Yang const struct yt921x_info *info = priv->info; 18144b8fbb87SDavid Yang int port = dp->index; 18154b8fbb87SDavid Yang 18164b8fbb87SDavid Yang if (!(BIT(port) & info->serdes_mask)) 18174b8fbb87SDavid Yang return NULL; 18184b8fbb87SDavid Yang 18194b8fbb87SDavid Yang switch (interface) { 18204b8fbb87SDavid Yang case PHY_INTERFACE_MODE_SGMII: 18214b8fbb87SDavid Yang case PHY_INTERFACE_MODE_100BASEX: 18224b8fbb87SDavid Yang case PHY_INTERFACE_MODE_1000BASEX: 18234b8fbb87SDavid Yang case PHY_INTERFACE_MODE_2500BASEX: 18244b8fbb87SDavid Yang return &priv->ports[port].pcs; 18254b8fbb87SDavid Yang default: 18264b8fbb87SDavid Yang return NULL; 18274b8fbb87SDavid Yang } 18284b8fbb87SDavid Yang } 18294b8fbb87SDavid Yang 18309c472a0dSDavid Yang static void 18319c472a0dSDavid Yang yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, 18329c472a0dSDavid Yang phy_interface_t interface) 18339c472a0dSDavid Yang { 18349c472a0dSDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 1835cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); 1836a219b5eaSDavid Yang struct yt921x_mib *pm; 18379c472a0dSDavid Yang int port = dp->index; 18389c472a0dSDavid Yang int res; 18399c472a0dSDavid Yang 18409c472a0dSDavid Yang /* No need to sync; port control block is hold until device remove */ 1841a219b5eaSDavid Yang pm = priv->ports[port].mib; 1842a219b5eaSDavid Yang if (pm) 1843a219b5eaSDavid Yang cancel_delayed_work(&pm->work); 18449c472a0dSDavid Yang 18459c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 18469c472a0dSDavid Yang res = yt921x_port_down(priv, port); 18479c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 18489c472a0dSDavid Yang 18499c472a0dSDavid Yang if (res) 18509c472a0dSDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", 18519c472a0dSDavid Yang port, res); 18529c472a0dSDavid Yang } 18539c472a0dSDavid Yang 18549c472a0dSDavid Yang static void 18559c472a0dSDavid Yang yt921x_phylink_mac_link_up(struct phylink_config *config, 18569c472a0dSDavid Yang struct phy_device *phydev, unsigned int mode, 18579c472a0dSDavid Yang phy_interface_t interface, int speed, int duplex, 18589c472a0dSDavid Yang bool tx_pause, bool rx_pause) 18599c472a0dSDavid Yang { 18609c472a0dSDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 1861cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); 1862a219b5eaSDavid Yang struct yt921x_mib *pm; 18639c472a0dSDavid Yang int port = dp->index; 18649c472a0dSDavid Yang int res; 18659c472a0dSDavid Yang 18669c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 18679c472a0dSDavid Yang res = yt921x_port_up(priv, port, mode, interface, speed, duplex, 18689c472a0dSDavid Yang tx_pause, rx_pause); 18699c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 18709c472a0dSDavid Yang 18719c472a0dSDavid Yang if (res) 18729c472a0dSDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", 18739c472a0dSDavid Yang port, res); 18749c472a0dSDavid Yang 1875a219b5eaSDavid Yang pm = priv->ports[port].mib; 1876a219b5eaSDavid Yang if (pm) 1877a219b5eaSDavid Yang schedule_delayed_work(&pm->work, 0); 18789c472a0dSDavid Yang } 18799c472a0dSDavid Yang 18809c472a0dSDavid Yang static void 18819c472a0dSDavid Yang yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode, 18829c472a0dSDavid Yang const struct phylink_link_state *state) 18839c472a0dSDavid Yang { 18849c472a0dSDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 1885cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(dp->ds); 18869c472a0dSDavid Yang int port = dp->index; 18879c472a0dSDavid Yang int res; 18889c472a0dSDavid Yang 18899c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 18909c472a0dSDavid Yang res = yt921x_port_config(priv, port, mode, state->interface); 18919c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 18929c472a0dSDavid Yang 18939c472a0dSDavid Yang if (res) 18949c472a0dSDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", 18959c472a0dSDavid Yang port, res); 18969c472a0dSDavid Yang } 18979c472a0dSDavid Yang 18989c472a0dSDavid Yang static void 18999c472a0dSDavid Yang yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, 19009c472a0dSDavid Yang struct phylink_config *config) 19019c472a0dSDavid Yang { 1902cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 19039c472a0dSDavid Yang const struct yt921x_info *info = priv->info; 19049c472a0dSDavid Yang 19059c472a0dSDavid Yang config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 19069c472a0dSDavid Yang MAC_10 | MAC_100 | MAC_1000; 19079c472a0dSDavid Yang 19089c472a0dSDavid Yang if (info->internal_mask & BIT(port)) { 19099c472a0dSDavid Yang /* Port 10 for MCU should probably go here too. But since that 19109c472a0dSDavid Yang * is untested yet, turn it down for the moment by letting it 19119c472a0dSDavid Yang * fall to the default branch. 19129c472a0dSDavid Yang */ 19139c472a0dSDavid Yang __set_bit(PHY_INTERFACE_MODE_INTERNAL, 19149c472a0dSDavid Yang config->supported_interfaces); 1915605597afSDavid Yang return; 1916605597afSDavid Yang } 19179c472a0dSDavid Yang 1918605597afSDavid Yang if (BIT(port) & info->serdes_mask) { 19199c472a0dSDavid Yang __set_bit(PHY_INTERFACE_MODE_SGMII, 19209c472a0dSDavid Yang config->supported_interfaces); 19219c472a0dSDavid Yang /* REVSGMII (SGMII in PHY role) should go here, once 19229c472a0dSDavid Yang * PHY_INTERFACE_MODE_REVSGMII is introduced. 19239c472a0dSDavid Yang */ 19249c472a0dSDavid Yang __set_bit(PHY_INTERFACE_MODE_100BASEX, 19259c472a0dSDavid Yang config->supported_interfaces); 19269c472a0dSDavid Yang __set_bit(PHY_INTERFACE_MODE_1000BASEX, 19279c472a0dSDavid Yang config->supported_interfaces); 19289c472a0dSDavid Yang __set_bit(PHY_INTERFACE_MODE_2500BASEX, 19299c472a0dSDavid Yang config->supported_interfaces); 19309c472a0dSDavid Yang config->mac_capabilities |= MAC_2500FD; 1931605597afSDavid Yang } 1932605597afSDavid Yang if (BIT(port) & info->xmii_mask) { 19339c472a0dSDavid Yang /* Not tested. To add support for XMII: 19349c472a0dSDavid Yang * - Add proper interface modes below 19359c472a0dSDavid Yang * - Handle them in yt921x_port_config() 19369c472a0dSDavid Yang */ 19379c472a0dSDavid Yang } 19389c472a0dSDavid Yang /* no such port: empty supported_interfaces causes phylink to turn it 19399c472a0dSDavid Yang * down 19409c472a0dSDavid Yang */ 19419c472a0dSDavid Yang } 19429c472a0dSDavid Yang 19439c472a0dSDavid Yang static int yt921x_port_setup(struct yt921x_priv *priv, int port) 19449c472a0dSDavid Yang { 19459c472a0dSDavid Yang struct dsa_switch *ds = &priv->ds; 19469c472a0dSDavid Yang u32 ctrl; 19479c472a0dSDavid Yang int res; 19489c472a0dSDavid Yang 19499c472a0dSDavid Yang res = yt921x_userport_standalone(priv, port); 19509c472a0dSDavid Yang if (res) 19519c472a0dSDavid Yang return res; 19529c472a0dSDavid Yang 19539c472a0dSDavid Yang /* Clear prio order (even if DCB is not enabled) to avoid unsolicited 19549c472a0dSDavid Yang * priorities 19559c472a0dSDavid Yang */ 19569c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_PRIO_ORD(port), 0); 19579c472a0dSDavid Yang if (res) 19589c472a0dSDavid Yang return res; 19599c472a0dSDavid Yang 19609c472a0dSDavid Yang if (dsa_is_cpu_port(ds, port)) { 19619c472a0dSDavid Yang /* Egress of CPU port is supposed to be completely controlled 19629c472a0dSDavid Yang * via tagging, so set to oneway isolated (drop all packets 19639c472a0dSDavid Yang * without tag). 19649c472a0dSDavid Yang */ 19659c472a0dSDavid Yang ctrl = ~(u32)0; 19669c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), 19679c472a0dSDavid Yang ctrl); 19689c472a0dSDavid Yang if (res) 19699c472a0dSDavid Yang return res; 19709c472a0dSDavid Yang 19719c472a0dSDavid Yang /* To simplify FDB "isolation" simulation, we also disable 19729c472a0dSDavid Yang * learning on the CPU port, and let software identify packets 19739c472a0dSDavid Yang * towarding CPU (either trapped or a static FDB entry is 19749c472a0dSDavid Yang * matched, no matter which bridge that entry is for), which is 19759c472a0dSDavid Yang * already done by yt921x_userport_standalone(). As a result, 19769c472a0dSDavid Yang * VLAN-awareness becomes unrelated on the CPU port (set to 19779c472a0dSDavid Yang * VLAN-unaware by the way). 19789c472a0dSDavid Yang */ 19799c472a0dSDavid Yang } 19809c472a0dSDavid Yang 19819c472a0dSDavid Yang return 0; 19829c472a0dSDavid Yang } 19839c472a0dSDavid Yang 19849c472a0dSDavid Yang static enum dsa_tag_protocol 19859c472a0dSDavid Yang yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, 19869c472a0dSDavid Yang enum dsa_tag_protocol m) 19879c472a0dSDavid Yang { 19889c472a0dSDavid Yang return DSA_TAG_PROTO_YT921X; 19899c472a0dSDavid Yang } 19909c472a0dSDavid Yang 1991a219b5eaSDavid Yang static void yt921x_dsa_port_teardown(struct dsa_switch *ds, int port) 1992a219b5eaSDavid Yang { 1993a219b5eaSDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 1994a219b5eaSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 1995a219b5eaSDavid Yang struct yt921x_mib *pm = pp->mib; 1996a219b5eaSDavid Yang 1997a219b5eaSDavid Yang /* get_stats64() might not be synchronized with port_teardown(), hold 1998a219b5eaSDavid Yang * the MIB block till the device teardown 1999a219b5eaSDavid Yang */ 2000a219b5eaSDavid Yang if (pm) 2001a219b5eaSDavid Yang disable_delayed_work_sync(&pm->work); 2002a219b5eaSDavid Yang } 2003a219b5eaSDavid Yang 20049c472a0dSDavid Yang static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) 20059c472a0dSDavid Yang { 2006cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 2007a219b5eaSDavid Yang struct yt921x_port *pp = &priv->ports[port]; 2008a219b5eaSDavid Yang const struct yt921x_info *info = priv->info; 2009a219b5eaSDavid Yang struct device *dev = yt921x_priv_to_device(priv); 2010a219b5eaSDavid Yang struct yt921x_mib *pm = pp->mib; 20119c472a0dSDavid Yang int res; 20129c472a0dSDavid Yang 2013a219b5eaSDavid Yang if (!pm && (BIT(port) & yt921x_info_ports_mask(info))) { 2014a219b5eaSDavid Yang pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL); 2015a219b5eaSDavid Yang if (!pm) 2016a219b5eaSDavid Yang return -ENOMEM; 2017a219b5eaSDavid Yang pp->mib = pm; 2018a219b5eaSDavid Yang 2019a219b5eaSDavid Yang pm->port = pp; 2020ed445358SDavid Yang u64_stats_init(&pm->syncp); 2021a219b5eaSDavid Yang INIT_DELAYED_WORK(&pm->work, yt921x_mib_poll); 2022a219b5eaSDavid Yang /* Start disabled, so that the enable below always pairs with 2023a219b5eaSDavid Yang * either this disable or the one from a previous teardown 2024a219b5eaSDavid Yang */ 2025a219b5eaSDavid Yang disable_delayed_work_sync(&pm->work); 2026a219b5eaSDavid Yang } 2027a219b5eaSDavid Yang 20289c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 20299c472a0dSDavid Yang res = yt921x_port_setup(priv, port); 20309c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 2031a219b5eaSDavid Yang if (res) 20329c472a0dSDavid Yang return res; 2033a219b5eaSDavid Yang 2034a219b5eaSDavid Yang if (pm) 2035a219b5eaSDavid Yang enable_delayed_work(&pm->work); 2036a219b5eaSDavid Yang 2037a219b5eaSDavid Yang return 0; 20389c472a0dSDavid Yang } 20399c472a0dSDavid Yang 20409c472a0dSDavid Yang /* Not "port" - DSCP mapping is global */ 20419c472a0dSDavid Yang static int __maybe_unused 20429c472a0dSDavid Yang yt921x_dsa_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) 20439c472a0dSDavid Yang { 2044cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 20459c472a0dSDavid Yang u32 val; 20469c472a0dSDavid Yang int res; 20479c472a0dSDavid Yang 20489c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 20499c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); 20509c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 20519c472a0dSDavid Yang 20529c472a0dSDavid Yang if (res) 20539c472a0dSDavid Yang return res; 20549c472a0dSDavid Yang 20559c472a0dSDavid Yang return FIELD_GET(YT921X_IPM_PRIO_M, val); 20569c472a0dSDavid Yang } 20579c472a0dSDavid Yang 20589c472a0dSDavid Yang static int __maybe_unused 20599c472a0dSDavid Yang yt921x_dsa_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) 20609c472a0dSDavid Yang { 2061cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 20629c472a0dSDavid Yang u32 val; 20639c472a0dSDavid Yang int res; 20649c472a0dSDavid Yang 20659c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 20669c472a0dSDavid Yang /* During a "dcb app replace" command, the new app table entry will be 20679c472a0dSDavid Yang * added first, then the old one will be deleted. But the hardware only 20689c472a0dSDavid Yang * supports one QoS class per DSCP value (duh), so if we blindly delete 20699c472a0dSDavid Yang * the app table entry for this DSCP value, we end up deleting the 20709c472a0dSDavid Yang * entry with the new priority. Avoid that by checking whether user 20719c472a0dSDavid Yang * space wants to delete the priority which is currently configured, or 20729c472a0dSDavid Yang * something else which is no longer current. 20739c472a0dSDavid Yang */ 20749c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_IPM_DSCPn(dscp), &val); 20759c472a0dSDavid Yang if (!res && FIELD_GET(YT921X_IPM_PRIO_M, val) == prio) 20769c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), 20779c472a0dSDavid Yang YT921X_IPM_PRIO(IEEE8021Q_TT_BK)); 20789c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 20799c472a0dSDavid Yang 20809c472a0dSDavid Yang return res; 20819c472a0dSDavid Yang } 20829c472a0dSDavid Yang 20839c472a0dSDavid Yang static int __maybe_unused 20849c472a0dSDavid Yang yt921x_dsa_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, u8 prio) 20859c472a0dSDavid Yang { 2086cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 20879c472a0dSDavid Yang int res; 20889c472a0dSDavid Yang 20899c472a0dSDavid Yang if (prio >= YT921X_PRIO_NUM) 20909c472a0dSDavid Yang return -EINVAL; 20919c472a0dSDavid Yang 20929c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 20939c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), 20949c472a0dSDavid Yang YT921X_IPM_PRIO(prio)); 20959c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 20969c472a0dSDavid Yang 20979c472a0dSDavid Yang return res; 20989c472a0dSDavid Yang } 20999c472a0dSDavid Yang 21009c472a0dSDavid Yang static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp) 21019c472a0dSDavid Yang { 21029c472a0dSDavid Yang u32 val = YT921X_EDATA_DATA_IDLE; 21039c472a0dSDavid Yang int res; 21049c472a0dSDavid Yang 21059c472a0dSDavid Yang res = yt921x_reg_wait(priv, YT921X_EDATA_DATA, 21069c472a0dSDavid Yang YT921X_EDATA_DATA_STATUS_M, &val); 21079c472a0dSDavid Yang if (res) 21089c472a0dSDavid Yang return res; 21099c472a0dSDavid Yang 21109c472a0dSDavid Yang *valp = val; 21119c472a0dSDavid Yang return 0; 21129c472a0dSDavid Yang } 21139c472a0dSDavid Yang 21149c472a0dSDavid Yang static int 21159c472a0dSDavid Yang yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp) 21169c472a0dSDavid Yang { 21179c472a0dSDavid Yang u32 ctrl; 21189c472a0dSDavid Yang u32 val; 21199c472a0dSDavid Yang int res; 21209c472a0dSDavid Yang 21219c472a0dSDavid Yang ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ; 21229c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl); 21239c472a0dSDavid Yang if (res) 21249c472a0dSDavid Yang return res; 21259c472a0dSDavid Yang res = yt921x_edata_wait(priv, &val); 21269c472a0dSDavid Yang if (res) 21279c472a0dSDavid Yang return res; 21289c472a0dSDavid Yang 21299c472a0dSDavid Yang *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val); 21309c472a0dSDavid Yang return 0; 21319c472a0dSDavid Yang } 21329c472a0dSDavid Yang 21339c472a0dSDavid Yang static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp) 21349c472a0dSDavid Yang { 21359c472a0dSDavid Yang u32 val; 21369c472a0dSDavid Yang int res; 21379c472a0dSDavid Yang 21389c472a0dSDavid Yang res = yt921x_edata_wait(priv, &val); 21399c472a0dSDavid Yang if (res) 21409c472a0dSDavid Yang return res; 21419c472a0dSDavid Yang return yt921x_edata_read_cont(priv, addr, valp); 21429c472a0dSDavid Yang } 21439c472a0dSDavid Yang 21449af9a816SDavid Yang static void yt921x_dsa_teardown(struct dsa_switch *ds) 21459af9a816SDavid Yang { 2146e3bfd256SArnd Bergmann #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) 2147cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 21489af9a816SDavid Yang 21499af9a816SDavid Yang yt921x_leds_remove(priv); 21509af9a816SDavid Yang #endif 21519af9a816SDavid Yang } 21529af9a816SDavid Yang 21539c472a0dSDavid Yang static int yt921x_chip_detect(struct yt921x_priv *priv) 21549c472a0dSDavid Yang { 2155cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 21569c472a0dSDavid Yang const struct yt921x_info *info; 21579c472a0dSDavid Yang u8 extmode; 21589c472a0dSDavid Yang u32 chipid; 21599c472a0dSDavid Yang u32 major; 21609c472a0dSDavid Yang u32 mode; 21619c472a0dSDavid Yang u32 val; 21629c472a0dSDavid Yang int res; 21639c472a0dSDavid Yang 21649c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); 21659c472a0dSDavid Yang if (res) 21669c472a0dSDavid Yang return res; 21679c472a0dSDavid Yang 21689c472a0dSDavid Yang major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); 21699c472a0dSDavid Yang 21709c472a0dSDavid Yang for (info = yt921x_infos; info->name; info++) 21719c472a0dSDavid Yang if (info->major == major) 21729c472a0dSDavid Yang break; 21739c472a0dSDavid Yang if (!info->name) { 21749c472a0dSDavid Yang dev_err(dev, "Unexpected chipid 0x%x\n", chipid); 21759c472a0dSDavid Yang return -ENODEV; 21769c472a0dSDavid Yang } 21779c472a0dSDavid Yang 21789c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); 21799c472a0dSDavid Yang if (res) 21809c472a0dSDavid Yang return res; 21819c472a0dSDavid Yang res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode); 21829c472a0dSDavid Yang if (res) 21839c472a0dSDavid Yang return res; 21849c472a0dSDavid Yang 21859c472a0dSDavid Yang for (; info->name; info++) 21869c472a0dSDavid Yang if (info->major == major && info->mode == mode && 21879c472a0dSDavid Yang info->extmode == extmode) 21889c472a0dSDavid Yang break; 21899c472a0dSDavid Yang if (!info->name) { 21909c472a0dSDavid Yang dev_err(dev, 21919c472a0dSDavid Yang "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n", 21929c472a0dSDavid Yang chipid, mode, extmode); 21939c472a0dSDavid Yang return -ENODEV; 21949c472a0dSDavid Yang } 21959c472a0dSDavid Yang 21969c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_SYS_CLK, &val); 21979c472a0dSDavid Yang if (res) 21989c472a0dSDavid Yang return res; 21999c472a0dSDavid Yang switch (FIELD_GET(YT921X_SYS_CLK_SEL_M, val)) { 22009c472a0dSDavid Yang case 0: 22019c472a0dSDavid Yang priv->cycle_ns = info->major == YT9215_MAJOR ? 8 : 6; 22029c472a0dSDavid Yang break; 22039c472a0dSDavid Yang case YT921X_SYS_CLK_143M: 22049c472a0dSDavid Yang priv->cycle_ns = 7; 22059c472a0dSDavid Yang break; 22069c472a0dSDavid Yang default: 22079c472a0dSDavid Yang priv->cycle_ns = 8; 22089c472a0dSDavid Yang } 22099c472a0dSDavid Yang 22109c472a0dSDavid Yang /* Print chipid here since we are interested in lower 16 bits */ 22119c472a0dSDavid Yang dev_info(dev, 22129c472a0dSDavid Yang "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n", 22139c472a0dSDavid Yang info->name, chipid, mode, extmode); 22149c472a0dSDavid Yang 22159c472a0dSDavid Yang priv->info = info; 22169c472a0dSDavid Yang 22179c472a0dSDavid Yang return 0; 22189c472a0dSDavid Yang } 22199c472a0dSDavid Yang 22209c472a0dSDavid Yang static int yt921x_chip_reset(struct yt921x_priv *priv) 22219c472a0dSDavid Yang { 2222cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 22239c472a0dSDavid Yang u16 eth_p_tag; 22249c472a0dSDavid Yang u32 val; 22259c472a0dSDavid Yang int res; 22269c472a0dSDavid Yang 22279c472a0dSDavid Yang res = yt921x_chip_detect(priv); 22289c472a0dSDavid Yang if (res) 22299c472a0dSDavid Yang return res; 22309c472a0dSDavid Yang 22319c472a0dSDavid Yang /* Reset */ 22329c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW); 22339c472a0dSDavid Yang if (res) 22349c472a0dSDavid Yang return res; 22359c472a0dSDavid Yang 22369c472a0dSDavid Yang /* RST_HW is almost same as GPIO hard reset, so we need this delay. */ 22379c472a0dSDavid Yang fsleep(YT921X_RST_DELAY_US); 22389c472a0dSDavid Yang 22399c472a0dSDavid Yang val = 0; 22409c472a0dSDavid Yang res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val); 22419c472a0dSDavid Yang if (res) 22429c472a0dSDavid Yang return res; 22439c472a0dSDavid Yang 22449c472a0dSDavid Yang /* Check for tag EtherType; do it after reset in case you messed it up 22459c472a0dSDavid Yang * before. 22469c472a0dSDavid Yang */ 22479c472a0dSDavid Yang res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val); 22489c472a0dSDavid Yang if (res) 22499c472a0dSDavid Yang return res; 22509c472a0dSDavid Yang eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val); 22519c472a0dSDavid Yang if (eth_p_tag != ETH_P_YT921X) { 22529c472a0dSDavid Yang dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag, 22539c472a0dSDavid Yang ETH_P_YT921X); 22549c472a0dSDavid Yang /* Despite being possible, we choose not to set CPU_TAG_TPID, 22559c472a0dSDavid Yang * since there is no way it can be different unless you have the 22569c472a0dSDavid Yang * wrong chip. 22579c472a0dSDavid Yang */ 22589c472a0dSDavid Yang return -EINVAL; 22599c472a0dSDavid Yang } 22609c472a0dSDavid Yang 22619c472a0dSDavid Yang return 0; 22629c472a0dSDavid Yang } 22639c472a0dSDavid Yang 22649c472a0dSDavid Yang static int yt921x_chip_setup_dsa(struct yt921x_priv *priv) 22659c472a0dSDavid Yang { 22669c472a0dSDavid Yang struct dsa_switch *ds = &priv->ds; 22679c472a0dSDavid Yang unsigned long cpu_ports_mask; 22689c472a0dSDavid Yang u32 ctrls[2]; 22699c472a0dSDavid Yang u32 ctrl; 22709c472a0dSDavid Yang int port; 22719c472a0dSDavid Yang int res; 22729c472a0dSDavid Yang 22739c472a0dSDavid Yang /* Enable DSA */ 22749c472a0dSDavid Yang priv->cpu_ports_mask = dsa_cpu_ports(ds); 22759c472a0dSDavid Yang 22769c472a0dSDavid Yang ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | 22779c472a0dSDavid Yang YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); 22789c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); 22799c472a0dSDavid Yang if (res) 22809c472a0dSDavid Yang return res; 22819c472a0dSDavid Yang 22829c472a0dSDavid Yang /* Setup software switch */ 22839c472a0dSDavid Yang ctrl = YT921X_CPU_COPY_TO_EXT_CPU; 22849c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl); 22859c472a0dSDavid Yang if (res) 22869c472a0dSDavid Yang return res; 22879c472a0dSDavid Yang 22889c472a0dSDavid Yang ctrl = GENMASK(10, 0); 22899c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); 22909c472a0dSDavid Yang if (res) 22919c472a0dSDavid Yang return res; 22929c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl); 22939c472a0dSDavid Yang if (res) 22949c472a0dSDavid Yang return res; 22959c472a0dSDavid Yang 22969c472a0dSDavid Yang /* YT921x does not support native DSA port bridging, so we use port 22979c472a0dSDavid Yang * isolation to emulate it. However, be especially careful that port 22989c472a0dSDavid Yang * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from 22999c472a0dSDavid Yang * another bridge) is matched and the destination port (in another 23009c472a0dSDavid Yang * bridge) is blocked, the packet will be dropped instead of flooding to 23019c472a0dSDavid Yang * the "bridged" ports, thus we need to trap and handle those packets by 23029c472a0dSDavid Yang * software. 23039c472a0dSDavid Yang * 23049c472a0dSDavid Yang * If there is no more than one bridge, we might be able to drop them 23059c472a0dSDavid Yang * directly given some conditions are met, but we trap them in all cases 23069c472a0dSDavid Yang * for now. 23079c472a0dSDavid Yang */ 23089c472a0dSDavid Yang ctrl = 0; 23099c472a0dSDavid Yang for (int i = 0; i < YT921X_PORT_NUM; i++) 23109c472a0dSDavid Yang ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i); 23119c472a0dSDavid Yang /* Except for CPU ports, if any packets are sent via CPU ports without 23129c472a0dSDavid Yang * tag, they should be dropped. 23139c472a0dSDavid Yang */ 23149c472a0dSDavid Yang cpu_ports_mask = priv->cpu_ports_mask; 23159c472a0dSDavid Yang for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) { 23169c472a0dSDavid Yang ctrl &= ~YT921X_ACT_UNK_ACTn_M(port); 23179c472a0dSDavid Yang ctrl |= YT921X_ACT_UNK_ACTn_DROP(port); 23189c472a0dSDavid Yang } 23199c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl); 23209c472a0dSDavid Yang if (res) 23219c472a0dSDavid Yang return res; 23229c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl); 23239c472a0dSDavid Yang if (res) 23249c472a0dSDavid Yang return res; 23259c472a0dSDavid Yang 23269c472a0dSDavid Yang /* Tagged VID 0 should be treated as untagged, which confuses the 23279c472a0dSDavid Yang * hardware a lot 23289c472a0dSDavid Yang */ 23299c472a0dSDavid Yang ctrls[0] = YT921X_VLAN_CTRLa_LEARN_DIS | YT921X_VLAN_CTRLa_PORTS_M; 23309c472a0dSDavid Yang ctrls[1] = 0; 23319c472a0dSDavid Yang res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrls); 23329c472a0dSDavid Yang if (res) 23339c472a0dSDavid Yang return res; 23349c472a0dSDavid Yang 23359c472a0dSDavid Yang return 0; 23369c472a0dSDavid Yang } 23379c472a0dSDavid Yang 23389c472a0dSDavid Yang static int __maybe_unused yt921x_chip_setup_qos(struct yt921x_priv *priv) 23399c472a0dSDavid Yang { 23409c472a0dSDavid Yang u32 ctrl; 23419c472a0dSDavid Yang int res; 23429c472a0dSDavid Yang 23439c472a0dSDavid Yang /* DSCP to internal priorities */ 23449c472a0dSDavid Yang for (u8 dscp = 0; dscp < DSCP_MAX; dscp++) { 23459c472a0dSDavid Yang int prio = ietf_dscp_to_ieee8021q_tt(dscp); 23469c472a0dSDavid Yang 23479c472a0dSDavid Yang if (prio < 0) 23489c472a0dSDavid Yang return prio; 23499c472a0dSDavid Yang 23509c472a0dSDavid Yang res = yt921x_reg_write(priv, YT921X_IPM_DSCPn(dscp), 23519c472a0dSDavid Yang YT921X_IPM_PRIO(prio)); 23529c472a0dSDavid Yang if (res) 23539c472a0dSDavid Yang return res; 23549c472a0dSDavid Yang } 23559c472a0dSDavid Yang 23569c472a0dSDavid Yang /* 802.1Q QoS to internal priorities */ 23579c472a0dSDavid Yang for (u8 pcp = 0; pcp < 8; pcp++) 23589c472a0dSDavid Yang for (u8 dei = 0; dei < 2; dei++) { 23599c472a0dSDavid Yang ctrl = YT921X_IPM_PRIO(pcp); 23609c472a0dSDavid Yang if (dei) 23619c472a0dSDavid Yang /* "Red" almost means drop, so it's not that 23629c472a0dSDavid Yang * useful. Note that tc police does not support 23639c472a0dSDavid Yang * Three-Color very well 23649c472a0dSDavid Yang */ 23659c472a0dSDavid Yang ctrl |= YT921X_IPM_COLOR_YELLOW; 23669c472a0dSDavid Yang 23679c472a0dSDavid Yang for (u8 svlan = 0; svlan < 2; svlan++) { 23689c472a0dSDavid Yang u32 reg = YT921X_IPM_PCPn(svlan, dei, pcp); 23699c472a0dSDavid Yang 23709c472a0dSDavid Yang res = yt921x_reg_write(priv, reg, ctrl); 23719c472a0dSDavid Yang if (res) 23729c472a0dSDavid Yang return res; 23739c472a0dSDavid Yang } 23749c472a0dSDavid Yang } 23759c472a0dSDavid Yang 23769c472a0dSDavid Yang return 0; 23779c472a0dSDavid Yang } 23789c472a0dSDavid Yang 23799c472a0dSDavid Yang static int yt921x_chip_setup(struct yt921x_priv *priv) 23809c472a0dSDavid Yang { 23819c472a0dSDavid Yang u32 ctrl; 23829c472a0dSDavid Yang int res; 23839c472a0dSDavid Yang 23849c472a0dSDavid Yang ctrl = YT921X_FUNC_MIB | YT921X_FUNC_ACL | YT921X_FUNC_METER; 23859c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_FUNC, ctrl); 23869c472a0dSDavid Yang if (res) 23879c472a0dSDavid Yang return res; 23889c472a0dSDavid Yang 23899c472a0dSDavid Yang res = yt921x_chip_setup_dsa(priv); 23909c472a0dSDavid Yang if (res) 23919c472a0dSDavid Yang return res; 23929c472a0dSDavid Yang 23939c472a0dSDavid Yang res = yt921x_chip_setup_tc(priv); 23949c472a0dSDavid Yang if (res) 23959c472a0dSDavid Yang return res; 23969c472a0dSDavid Yang 23979c472a0dSDavid Yang res = yt921x_chip_setup_acl(priv); 23989c472a0dSDavid Yang if (res) 23999c472a0dSDavid Yang return res; 24009c472a0dSDavid Yang 24019c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB) 24029c472a0dSDavid Yang res = yt921x_chip_setup_qos(priv); 24039c472a0dSDavid Yang if (res) 24049c472a0dSDavid Yang return res; 24059c472a0dSDavid Yang #endif 24069c472a0dSDavid Yang 24079c472a0dSDavid Yang /* Miscellaneous */ 24089c472a0dSDavid Yang res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP); 24099c472a0dSDavid Yang if (res) 24109c472a0dSDavid Yang return res; 24119c472a0dSDavid Yang 24124b8fbb87SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_SERDES_CTRL, 24134b8fbb87SDavid Yang YT921X_SERDES_CTRL_TEST); 24144b8fbb87SDavid Yang if (res) 24154b8fbb87SDavid Yang return res; 24164b8fbb87SDavid Yang 24179c472a0dSDavid Yang return 0; 24189c472a0dSDavid Yang } 24199c472a0dSDavid Yang 24209c472a0dSDavid Yang static int yt921x_dsa_setup(struct dsa_switch *ds) 24219c472a0dSDavid Yang { 2422cece0b57SDavid Yang struct yt921x_priv *priv = dsa_to_yt921x_priv(ds); 2423cece0b57SDavid Yang struct device *dev = yt921x_priv_to_device(priv); 24249c472a0dSDavid Yang struct device_node *np = dev->of_node; 24259c472a0dSDavid Yang struct device_node *child; 24264b8fbb87SDavid Yang unsigned long mask; 24274b8fbb87SDavid Yang int port; 24289c472a0dSDavid Yang int res; 24299c472a0dSDavid Yang 24309c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 24319c472a0dSDavid Yang res = yt921x_chip_reset(priv); 24329c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 24339c472a0dSDavid Yang 24349c472a0dSDavid Yang if (res) 24359c472a0dSDavid Yang return res; 24369c472a0dSDavid Yang 24379c472a0dSDavid Yang /* Register the internal mdio bus. Nodes for internal ports should have 24389c472a0dSDavid Yang * proper phy-handle pointing to their PHYs. Not enabling the internal 24399c472a0dSDavid Yang * bus is possible, though pretty wired, if internal ports are not used. 24409c472a0dSDavid Yang */ 24419c472a0dSDavid Yang child = of_get_child_by_name(np, "mdio"); 24429c472a0dSDavid Yang if (child) { 24439c472a0dSDavid Yang res = yt921x_mbus_int_init(priv, child); 24449c472a0dSDavid Yang of_node_put(child); 24459c472a0dSDavid Yang if (res) 24469c472a0dSDavid Yang return res; 24479c472a0dSDavid Yang } 24489c472a0dSDavid Yang 24499c472a0dSDavid Yang /* External mdio bus is optional */ 24509c472a0dSDavid Yang child = of_get_child_by_name(np, "mdio-external"); 24519c472a0dSDavid Yang if (child) { 24529c472a0dSDavid Yang res = yt921x_mbus_ext_init(priv, child); 24539c472a0dSDavid Yang of_node_put(child); 24549c472a0dSDavid Yang if (res) 24559c472a0dSDavid Yang return res; 24569c472a0dSDavid Yang 24579c472a0dSDavid Yang dev_err(dev, "Untested external mdio bus\n"); 24589c472a0dSDavid Yang return -ENODEV; 24599c472a0dSDavid Yang } 24609c472a0dSDavid Yang 24614b8fbb87SDavid Yang mask = priv->info->serdes_mask; 24624b8fbb87SDavid Yang for_each_set_bit(port, &mask, YT921X_PORT_NUM) { 24634b8fbb87SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 24644b8fbb87SDavid Yang 24654b8fbb87SDavid Yang pp->pcs.ops = &yt921x_phylink_pcs_ops; 24664b8fbb87SDavid Yang pp->pcs.poll = true; 24674b8fbb87SDavid Yang 24684b8fbb87SDavid Yang __set_bit(PHY_INTERFACE_MODE_SGMII, 24694b8fbb87SDavid Yang pp->pcs.supported_interfaces); 24704b8fbb87SDavid Yang __set_bit(PHY_INTERFACE_MODE_100BASEX, 24714b8fbb87SDavid Yang pp->pcs.supported_interfaces); 24724b8fbb87SDavid Yang __set_bit(PHY_INTERFACE_MODE_1000BASEX, 24734b8fbb87SDavid Yang pp->pcs.supported_interfaces); 24744b8fbb87SDavid Yang __set_bit(PHY_INTERFACE_MODE_2500BASEX, 24754b8fbb87SDavid Yang pp->pcs.supported_interfaces); 24764b8fbb87SDavid Yang } 24774b8fbb87SDavid Yang 24789c472a0dSDavid Yang mutex_lock(&priv->reg_lock); 24799c472a0dSDavid Yang res = yt921x_chip_setup(priv); 24809c472a0dSDavid Yang mutex_unlock(&priv->reg_lock); 24819c472a0dSDavid Yang 24829c472a0dSDavid Yang if (res) 24839c472a0dSDavid Yang return res; 24849c472a0dSDavid Yang 24859af9a816SDavid Yang #if IS_ENABLED(CONFIG_NET_DSA_YT921X_LEDS) 24869af9a816SDavid Yang res = yt921x_leds_setup(priv); 24879af9a816SDavid Yang if (res) 24889af9a816SDavid Yang dev_warn(dev, "Failed to setup LEDs: %d\n", res); 24899af9a816SDavid Yang #endif 24909af9a816SDavid Yang 24919c472a0dSDavid Yang return 0; 24929c472a0dSDavid Yang } 24939c472a0dSDavid Yang 24949c472a0dSDavid Yang static const struct phylink_mac_ops yt921x_phylink_mac_ops = { 24954b8fbb87SDavid Yang .mac_select_pcs = yt921x_phylink_mac_select_pcs, 24969c472a0dSDavid Yang .mac_link_down = yt921x_phylink_mac_link_down, 24979c472a0dSDavid Yang .mac_link_up = yt921x_phylink_mac_link_up, 24989c472a0dSDavid Yang .mac_config = yt921x_phylink_mac_config, 24999c472a0dSDavid Yang }; 25009c472a0dSDavid Yang 25019c472a0dSDavid Yang static const struct dsa_switch_ops yt921x_dsa_switch_ops = { 25029c472a0dSDavid Yang /* mib */ 25039c472a0dSDavid Yang .get_strings = yt921x_dsa_get_strings, 25049c472a0dSDavid Yang .get_ethtool_stats = yt921x_dsa_get_ethtool_stats, 25059c472a0dSDavid Yang .get_sset_count = yt921x_dsa_get_sset_count, 25069c472a0dSDavid Yang .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats, 25079c472a0dSDavid Yang .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats, 25089c472a0dSDavid Yang .get_rmon_stats = yt921x_dsa_get_rmon_stats, 25099c472a0dSDavid Yang .get_stats64 = yt921x_dsa_get_stats64, 25109c472a0dSDavid Yang .get_pause_stats = yt921x_dsa_get_pause_stats, 25119c472a0dSDavid Yang /* eee */ 25129c472a0dSDavid Yang .support_eee = dsa_supports_eee, 25139c472a0dSDavid Yang .set_mac_eee = yt921x_dsa_set_mac_eee, 25149c472a0dSDavid Yang /* mtu */ 25159c472a0dSDavid Yang .port_change_mtu = yt921x_dsa_port_change_mtu, 25169c472a0dSDavid Yang .port_max_mtu = yt921x_dsa_port_max_mtu, 25179c472a0dSDavid Yang /* rate */ 25189c472a0dSDavid Yang .port_policer_del = yt921x_dsa_port_policer_del, 25199c472a0dSDavid Yang .port_policer_add = yt921x_dsa_port_policer_add, 25209c472a0dSDavid Yang .port_setup_tc = yt921x_dsa_port_setup_tc, 25219c472a0dSDavid Yang /* acl */ 25229c472a0dSDavid Yang .cls_flower_del = yt921x_dsa_cls_flower_del, 25239c472a0dSDavid Yang .cls_flower_add = yt921x_dsa_cls_flower_add, 25249c472a0dSDavid Yang /* hsr */ 25259c472a0dSDavid Yang .port_hsr_leave = dsa_port_simple_hsr_leave, 25269c472a0dSDavid Yang .port_hsr_join = dsa_port_simple_hsr_join, 25279c472a0dSDavid Yang /* mirror */ 25289c472a0dSDavid Yang .port_mirror_del = yt921x_dsa_port_mirror_del, 25299c472a0dSDavid Yang .port_mirror_add = yt921x_dsa_port_mirror_add, 25309c472a0dSDavid Yang /* lag */ 25319c472a0dSDavid Yang .port_lag_leave = yt921x_dsa_port_lag_leave, 25329c472a0dSDavid Yang .port_lag_join = yt921x_dsa_port_lag_join, 25339c472a0dSDavid Yang /* fdb */ 25349c472a0dSDavid Yang .port_fdb_dump = yt921x_dsa_port_fdb_dump, 25359c472a0dSDavid Yang .port_fast_age = yt921x_dsa_port_fast_age, 25369c472a0dSDavid Yang .set_ageing_time = yt921x_dsa_set_ageing_time, 25379c472a0dSDavid Yang .port_fdb_del = yt921x_dsa_port_fdb_del, 25389c472a0dSDavid Yang .port_fdb_add = yt921x_dsa_port_fdb_add, 25399c472a0dSDavid Yang .port_mdb_del = yt921x_dsa_port_mdb_del, 25409c472a0dSDavid Yang .port_mdb_add = yt921x_dsa_port_mdb_add, 25419c472a0dSDavid Yang /* vlan */ 25429c472a0dSDavid Yang .port_vlan_filtering = yt921x_dsa_port_vlan_filtering, 25439c472a0dSDavid Yang .port_vlan_del = yt921x_dsa_port_vlan_del, 25449c472a0dSDavid Yang .port_vlan_add = yt921x_dsa_port_vlan_add, 25459c472a0dSDavid Yang /* bridge */ 25469c472a0dSDavid Yang .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags, 25479c472a0dSDavid Yang .port_bridge_flags = yt921x_dsa_port_bridge_flags, 25489c472a0dSDavid Yang .port_bridge_leave = yt921x_dsa_port_bridge_leave, 25499c472a0dSDavid Yang .port_bridge_join = yt921x_dsa_port_bridge_join, 25509c472a0dSDavid Yang /* mst */ 25519c472a0dSDavid Yang .port_mst_state_set = yt921x_dsa_port_mst_state_set, 25529c472a0dSDavid Yang .vlan_msti_set = yt921x_dsa_vlan_msti_set, 25539c472a0dSDavid Yang .port_stp_state_set = yt921x_dsa_port_stp_state_set, 25549c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB) 25559c472a0dSDavid Yang /* dcb */ 25569c472a0dSDavid Yang .port_get_default_prio = yt921x_dsa_port_get_default_prio, 25579c472a0dSDavid Yang .port_set_default_prio = yt921x_dsa_port_set_default_prio, 25589c472a0dSDavid Yang .port_get_apptrust = yt921x_dsa_port_get_apptrust, 25599c472a0dSDavid Yang .port_set_apptrust = yt921x_dsa_port_set_apptrust, 25609c472a0dSDavid Yang #endif 25619c472a0dSDavid Yang /* port */ 25629c472a0dSDavid Yang .get_tag_protocol = yt921x_dsa_get_tag_protocol, 25639c472a0dSDavid Yang .phylink_get_caps = yt921x_dsa_phylink_get_caps, 2564a219b5eaSDavid Yang .port_teardown = yt921x_dsa_port_teardown, 25659c472a0dSDavid Yang .port_setup = yt921x_dsa_port_setup, 25669c472a0dSDavid Yang #if IS_ENABLED(CONFIG_DCB) 25679c472a0dSDavid Yang /* dscp */ 25689c472a0dSDavid Yang .port_get_dscp_prio = yt921x_dsa_port_get_dscp_prio, 25699c472a0dSDavid Yang .port_del_dscp_prio = yt921x_dsa_port_del_dscp_prio, 25709c472a0dSDavid Yang .port_add_dscp_prio = yt921x_dsa_port_add_dscp_prio, 25719c472a0dSDavid Yang #endif 25729c472a0dSDavid Yang /* chip */ 25739af9a816SDavid Yang .teardown = yt921x_dsa_teardown, 25749c472a0dSDavid Yang .setup = yt921x_dsa_setup, 25759c472a0dSDavid Yang }; 25769c472a0dSDavid Yang 25779c472a0dSDavid Yang static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) 25789c472a0dSDavid Yang { 25799c472a0dSDavid Yang struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); 25809c472a0dSDavid Yang 25819c472a0dSDavid Yang if (!priv) 25829c472a0dSDavid Yang return; 25839c472a0dSDavid Yang 25849c472a0dSDavid Yang dsa_switch_shutdown(&priv->ds); 25859c472a0dSDavid Yang } 25869c472a0dSDavid Yang 25879c472a0dSDavid Yang static void yt921x_mdio_remove(struct mdio_device *mdiodev) 25889c472a0dSDavid Yang { 25899c472a0dSDavid Yang struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); 25909c472a0dSDavid Yang 25919c472a0dSDavid Yang if (!priv) 25929c472a0dSDavid Yang return; 25939c472a0dSDavid Yang 25949c472a0dSDavid Yang dsa_unregister_switch(&priv->ds); 25959c472a0dSDavid Yang 25969c472a0dSDavid Yang for (unsigned int i = 0; i < ARRAY_SIZE(priv->acl_blks); i++) { 25979c472a0dSDavid Yang struct yt921x_acl_blk *aclblk = priv->acl_blks[i]; 25989c472a0dSDavid Yang 25999c472a0dSDavid Yang if (!aclblk) 26009c472a0dSDavid Yang continue; 26019c472a0dSDavid Yang for (unsigned int j = 0; j < ARRAY_SIZE(aclblk->rules); j++) { 26029c472a0dSDavid Yang struct yt921x_acl_rule *aclrule = aclblk->rules[j]; 26039c472a0dSDavid Yang 26049c472a0dSDavid Yang if (!aclrule) 26059c472a0dSDavid Yang continue; 26069c472a0dSDavid Yang kvfree(aclrule); 26079c472a0dSDavid Yang } 26089c472a0dSDavid Yang kvfree(aclblk); 26099c472a0dSDavid Yang } 26109c472a0dSDavid Yang mutex_destroy(&priv->reg_lock); 26119c472a0dSDavid Yang } 26129c472a0dSDavid Yang 26139c472a0dSDavid Yang static int yt921x_mdio_probe(struct mdio_device *mdiodev) 26149c472a0dSDavid Yang { 26159c472a0dSDavid Yang struct device *dev = &mdiodev->dev; 26169c472a0dSDavid Yang struct yt921x_reg_mdio *mdio; 26179c472a0dSDavid Yang struct yt921x_priv *priv; 26189c472a0dSDavid Yang struct dsa_switch *ds; 26199c472a0dSDavid Yang 26209c472a0dSDavid Yang priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 26219c472a0dSDavid Yang if (!priv) 26229c472a0dSDavid Yang return -ENOMEM; 26239c472a0dSDavid Yang 26249c472a0dSDavid Yang mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL); 26259c472a0dSDavid Yang if (!mdio) 26269c472a0dSDavid Yang return -ENOMEM; 26279c472a0dSDavid Yang 26289c472a0dSDavid Yang mdio->bus = mdiodev->bus; 26299c472a0dSDavid Yang mdio->addr = mdiodev->addr; 26309c472a0dSDavid Yang mdio->switchid = 0; 26319c472a0dSDavid Yang 26329c472a0dSDavid Yang mutex_init(&priv->reg_lock); 26339c472a0dSDavid Yang 26349c472a0dSDavid Yang priv->reg_ops = &yt921x_reg_ops_mdio; 26359c472a0dSDavid Yang priv->reg_ctx = mdio; 26369c472a0dSDavid Yang 26379c472a0dSDavid Yang for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) { 26389c472a0dSDavid Yang struct yt921x_port *pp = &priv->ports[i]; 26399c472a0dSDavid Yang 26409c472a0dSDavid Yang pp->index = i; 26419c472a0dSDavid Yang } 26429c472a0dSDavid Yang 26439c472a0dSDavid Yang ds = &priv->ds; 26449c472a0dSDavid Yang ds->dev = dev; 26459c472a0dSDavid Yang ds->assisted_learning_on_cpu_port = true; 26469c472a0dSDavid Yang ds->dscp_prio_mapping_is_global = true; 26479c472a0dSDavid Yang ds->priv = priv; 26489c472a0dSDavid Yang ds->ops = &yt921x_dsa_switch_ops; 26499c472a0dSDavid Yang ds->ageing_time_min = 1 * 5000; 26509c472a0dSDavid Yang ds->ageing_time_max = U16_MAX * 5000; 26519c472a0dSDavid Yang ds->phylink_mac_ops = &yt921x_phylink_mac_ops; 26529c472a0dSDavid Yang ds->num_lag_ids = YT921X_LAG_NUM; 26539c472a0dSDavid Yang ds->num_ports = YT921X_PORT_NUM; 26549c472a0dSDavid Yang 26559c472a0dSDavid Yang mdiodev_set_drvdata(mdiodev, priv); 26569c472a0dSDavid Yang 26579c472a0dSDavid Yang return dsa_register_switch(ds); 26589c472a0dSDavid Yang } 26599c472a0dSDavid Yang 26609c472a0dSDavid Yang static const struct of_device_id yt921x_of_match[] = { 26619c472a0dSDavid Yang { .compatible = "motorcomm,yt9215" }, 26629c472a0dSDavid Yang {} 26639c472a0dSDavid Yang }; 26649c472a0dSDavid Yang MODULE_DEVICE_TABLE(of, yt921x_of_match); 26659c472a0dSDavid Yang 26669c472a0dSDavid Yang static struct mdio_driver yt921x_mdio_driver = { 26679c472a0dSDavid Yang .probe = yt921x_mdio_probe, 26689c472a0dSDavid Yang .remove = yt921x_mdio_remove, 26699c472a0dSDavid Yang .shutdown = yt921x_mdio_shutdown, 26709c472a0dSDavid Yang .mdiodrv.driver = { 26719c472a0dSDavid Yang .name = YT921X_NAME, 26729c472a0dSDavid Yang .of_match_table = yt921x_of_match, 26739c472a0dSDavid Yang }, 26749c472a0dSDavid Yang }; 26759c472a0dSDavid Yang 26769c472a0dSDavid Yang mdio_module_driver(yt921x_mdio_driver); 26779c472a0dSDavid Yang 26789c472a0dSDavid Yang MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>"); 26799c472a0dSDavid Yang MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); 26809c472a0dSDavid Yang MODULE_LICENSE("GPL"); 2681