1186623f4SDavid Yang // SPDX-License-Identifier: GPL-2.0-or-later 2186623f4SDavid Yang /* 3186623f4SDavid Yang * Driver for Motorcomm YT921x Switch 4186623f4SDavid Yang * 5186623f4SDavid Yang * Should work on YT9213/YT9214/YT9215/YT9218, but only tested on YT9215+SGMII, 6186623f4SDavid Yang * be sure to do your own checks before porting to another chip. 7186623f4SDavid Yang * 8186623f4SDavid Yang * Copyright (c) 2025 David Yang 9186623f4SDavid Yang */ 10186623f4SDavid Yang 11186623f4SDavid Yang #include <linux/etherdevice.h> 12186623f4SDavid Yang #include <linux/if_bridge.h> 13186623f4SDavid Yang #include <linux/if_hsr.h> 14186623f4SDavid Yang #include <linux/if_vlan.h> 15186623f4SDavid Yang #include <linux/iopoll.h> 16186623f4SDavid Yang #include <linux/mdio.h> 17186623f4SDavid Yang #include <linux/module.h> 18186623f4SDavid Yang #include <linux/of.h> 19186623f4SDavid Yang #include <linux/of_mdio.h> 20186623f4SDavid Yang #include <linux/of_net.h> 21186623f4SDavid Yang 22186623f4SDavid Yang #include <net/dsa.h> 23186623f4SDavid Yang 24186623f4SDavid Yang #include "yt921x.h" 25186623f4SDavid Yang 26186623f4SDavid Yang struct yt921x_mib_desc { 27186623f4SDavid Yang unsigned int size; 28186623f4SDavid Yang unsigned int offset; 29186623f4SDavid Yang const char *name; 30186623f4SDavid Yang }; 31186623f4SDavid Yang 32186623f4SDavid Yang #define MIB_DESC(_size, _offset, _name) \ 33186623f4SDavid Yang {_size, _offset, _name} 34186623f4SDavid Yang 35186623f4SDavid Yang /* Must agree with yt921x_mib 36186623f4SDavid Yang * 37186623f4SDavid Yang * Unstructured fields (name != NULL) will appear in get_ethtool_stats(), 38186623f4SDavid Yang * structured go to their *_stats() methods, but we need their sizes and offsets 39186623f4SDavid Yang * to perform 32bit MIB overflow wraparound. 40186623f4SDavid Yang */ 41186623f4SDavid Yang static const struct yt921x_mib_desc yt921x_mib_descs[] = { 42fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_BROADCAST, NULL), 43fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PAUSE, NULL), 44fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_MULTICAST, NULL), 45fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_CRC_ERR, NULL), 46186623f4SDavid Yang 47fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_ALIGN_ERR, NULL), 48fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_UNDERSIZE_ERR, NULL), 49fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_FRAG_ERR, NULL), 50fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_64, NULL), 51186623f4SDavid Yang 52fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_65_TO_127, NULL), 53fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_128_TO_255, NULL), 54fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_256_TO_511, NULL), 55fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_512_TO_1023, NULL), 56186623f4SDavid Yang 57fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1024_TO_1518, NULL), 58fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_PKT_SZ_1519_TO_MAX, NULL), 59fbce7b36SDavid Yang MIB_DESC(2, YT921X_MIB_DATA_RX_GOOD_BYTES, NULL), 60186623f4SDavid Yang 61fbce7b36SDavid Yang MIB_DESC(2, YT921X_MIB_DATA_RX_BAD_BYTES, "RxBadBytes"), 62fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_OVERSIZE_ERR, NULL), 63186623f4SDavid Yang 64fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_DROPPED, NULL), 65fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_BROADCAST, NULL), 66fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PAUSE, NULL), 67fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_MULTICAST, NULL), 68186623f4SDavid Yang 69fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_UNDERSIZE_ERR, NULL), 70fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_64, NULL), 71fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_65_TO_127, NULL), 72fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_128_TO_255, NULL), 73186623f4SDavid Yang 74fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_256_TO_511, NULL), 75fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_512_TO_1023, NULL), 76fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1024_TO_1518, NULL), 77fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT_SZ_1519_TO_MAX, NULL), 78186623f4SDavid Yang 79fbce7b36SDavid Yang MIB_DESC(2, YT921X_MIB_DATA_TX_GOOD_BYTES, NULL), 80fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_COLLISION, NULL), 81186623f4SDavid Yang 82fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_EXCESSIVE_COLLISION, NULL), 83fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_MULTIPLE_COLLISION, NULL), 84fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_SINGLE_COLLISION, NULL), 85fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_PKT, NULL), 86186623f4SDavid Yang 87fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_DEFERRED, NULL), 88fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_LATE_COLLISION, NULL), 89fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_RX_OAM, "RxOAM"), 90fbce7b36SDavid Yang MIB_DESC(1, YT921X_MIB_DATA_TX_OAM, "TxOAM"), 91186623f4SDavid Yang }; 92186623f4SDavid Yang 93186623f4SDavid Yang struct yt921x_info { 94186623f4SDavid Yang const char *name; 95186623f4SDavid Yang u16 major; 96186623f4SDavid Yang /* Unknown, seems to be plain enumeration */ 97186623f4SDavid Yang u8 mode; 98186623f4SDavid Yang u8 extmode; 99186623f4SDavid Yang /* Ports with integral GbE PHYs, not including MCU Port 10 */ 100186623f4SDavid Yang u16 internal_mask; 101186623f4SDavid Yang /* TODO: see comments in yt921x_dsa_phylink_get_caps() */ 102186623f4SDavid Yang u16 external_mask; 103186623f4SDavid Yang }; 104186623f4SDavid Yang 105186623f4SDavid Yang #define YT921X_PORT_MASK_INTn(port) BIT(port) 106186623f4SDavid Yang #define YT921X_PORT_MASK_INT0_n(n) GENMASK((n) - 1, 0) 107186623f4SDavid Yang #define YT921X_PORT_MASK_EXT0 BIT(8) 108186623f4SDavid Yang #define YT921X_PORT_MASK_EXT1 BIT(9) 109186623f4SDavid Yang 110186623f4SDavid Yang static const struct yt921x_info yt921x_infos[] = { 111186623f4SDavid Yang { 112186623f4SDavid Yang "YT9215SC", YT9215_MAJOR, 1, 0, 113186623f4SDavid Yang YT921X_PORT_MASK_INT0_n(5), 114186623f4SDavid Yang YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, 115186623f4SDavid Yang }, 116186623f4SDavid Yang { 117186623f4SDavid Yang "YT9215S", YT9215_MAJOR, 2, 0, 118186623f4SDavid Yang YT921X_PORT_MASK_INT0_n(5), 119186623f4SDavid Yang YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, 120186623f4SDavid Yang }, 121186623f4SDavid Yang { 122186623f4SDavid Yang "YT9215RB", YT9215_MAJOR, 3, 0, 123186623f4SDavid Yang YT921X_PORT_MASK_INT0_n(5), 124186623f4SDavid Yang YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, 125186623f4SDavid Yang }, 126186623f4SDavid Yang { 127186623f4SDavid Yang "YT9214NB", YT9215_MAJOR, 3, 2, 128186623f4SDavid Yang YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), 129186623f4SDavid Yang YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, 130186623f4SDavid Yang }, 131186623f4SDavid Yang { 132186623f4SDavid Yang "YT9213NB", YT9215_MAJOR, 3, 3, 133186623f4SDavid Yang YT921X_PORT_MASK_INTn(1) | YT921X_PORT_MASK_INTn(3), 134186623f4SDavid Yang YT921X_PORT_MASK_EXT1, 135186623f4SDavid Yang }, 136186623f4SDavid Yang { 137186623f4SDavid Yang "YT9218N", YT9218_MAJOR, 0, 0, 138186623f4SDavid Yang YT921X_PORT_MASK_INT0_n(8), 139186623f4SDavid Yang 0, 140186623f4SDavid Yang }, 141186623f4SDavid Yang { 142186623f4SDavid Yang "YT9218MB", YT9218_MAJOR, 1, 0, 143186623f4SDavid Yang YT921X_PORT_MASK_INT0_n(8), 144186623f4SDavid Yang YT921X_PORT_MASK_EXT0 | YT921X_PORT_MASK_EXT1, 145186623f4SDavid Yang }, 146186623f4SDavid Yang {} 147186623f4SDavid Yang }; 148186623f4SDavid Yang 149186623f4SDavid Yang #define YT921X_NAME "yt921x" 150186623f4SDavid Yang 151186623f4SDavid Yang #define YT921X_VID_UNWARE 4095 152186623f4SDavid Yang 153186623f4SDavid Yang #define YT921X_POLL_SLEEP_US 10000 154186623f4SDavid Yang #define YT921X_POLL_TIMEOUT_US 100000 155186623f4SDavid Yang 156186623f4SDavid Yang /* The interval should be small enough to avoid overflow of 32bit MIBs. 157186623f4SDavid Yang * 158186623f4SDavid Yang * Until we can read MIBs from stats64 call directly (i.e. sleep 159186623f4SDavid Yang * there), we have to poll stats more frequently then it is actually needed. 160186623f4SDavid Yang * For overflow protection, normally, 100 sec interval should have been OK. 161186623f4SDavid Yang */ 162186623f4SDavid Yang #define YT921X_STATS_INTERVAL_JIFFIES (3 * HZ) 163186623f4SDavid Yang 164186623f4SDavid Yang struct yt921x_reg_mdio { 165186623f4SDavid Yang struct mii_bus *bus; 166186623f4SDavid Yang int addr; 167186623f4SDavid Yang /* SWITCH_ID_1 / SWITCH_ID_0 of the device 168186623f4SDavid Yang * 169186623f4SDavid Yang * This is a way to multiplex multiple devices on the same MII phyaddr 170186623f4SDavid Yang * and should be configurable in DT. However, MDIO core simply doesn't 171186623f4SDavid Yang * allow multiple devices over one reg addr, so this is a fixed value 172186623f4SDavid Yang * for now until a solution is found. 173186623f4SDavid Yang * 174186623f4SDavid Yang * Keep this because we need switchid to form MII regaddrs anyway. 175186623f4SDavid Yang */ 176186623f4SDavid Yang unsigned char switchid; 177186623f4SDavid Yang }; 178186623f4SDavid Yang 179186623f4SDavid Yang /* TODO: SPI/I2C */ 180186623f4SDavid Yang 181186623f4SDavid Yang #define to_yt921x_priv(_ds) container_of_const(_ds, struct yt921x_priv, ds) 182186623f4SDavid Yang #define to_device(priv) ((priv)->ds.dev) 183186623f4SDavid Yang 184186623f4SDavid Yang static int yt921x_reg_read(struct yt921x_priv *priv, u32 reg, u32 *valp) 185186623f4SDavid Yang { 186186623f4SDavid Yang WARN_ON(!mutex_is_locked(&priv->reg_lock)); 187186623f4SDavid Yang 188186623f4SDavid Yang return priv->reg_ops->read(priv->reg_ctx, reg, valp); 189186623f4SDavid Yang } 190186623f4SDavid Yang 191186623f4SDavid Yang static int yt921x_reg_write(struct yt921x_priv *priv, u32 reg, u32 val) 192186623f4SDavid Yang { 193186623f4SDavid Yang WARN_ON(!mutex_is_locked(&priv->reg_lock)); 194186623f4SDavid Yang 195186623f4SDavid Yang return priv->reg_ops->write(priv->reg_ctx, reg, val); 196186623f4SDavid Yang } 197186623f4SDavid Yang 198186623f4SDavid Yang static int 199186623f4SDavid Yang yt921x_reg_wait(struct yt921x_priv *priv, u32 reg, u32 mask, u32 *valp) 200186623f4SDavid Yang { 201186623f4SDavid Yang u32 val; 202186623f4SDavid Yang int res; 203186623f4SDavid Yang int ret; 204186623f4SDavid Yang 205186623f4SDavid Yang ret = read_poll_timeout(yt921x_reg_read, res, 206186623f4SDavid Yang res || (val & mask) == *valp, 207186623f4SDavid Yang YT921X_POLL_SLEEP_US, YT921X_POLL_TIMEOUT_US, 208186623f4SDavid Yang false, priv, reg, &val); 209186623f4SDavid Yang if (ret) 210186623f4SDavid Yang return ret; 211186623f4SDavid Yang if (res) 212186623f4SDavid Yang return res; 213186623f4SDavid Yang 214186623f4SDavid Yang *valp = val; 215186623f4SDavid Yang return 0; 216186623f4SDavid Yang } 217186623f4SDavid Yang 218186623f4SDavid Yang static int 219186623f4SDavid Yang yt921x_reg_update_bits(struct yt921x_priv *priv, u32 reg, u32 mask, u32 val) 220186623f4SDavid Yang { 221186623f4SDavid Yang int res; 222186623f4SDavid Yang u32 v; 223186623f4SDavid Yang u32 u; 224186623f4SDavid Yang 225186623f4SDavid Yang res = yt921x_reg_read(priv, reg, &v); 226186623f4SDavid Yang if (res) 227186623f4SDavid Yang return res; 228186623f4SDavid Yang 229186623f4SDavid Yang u = v; 230186623f4SDavid Yang u &= ~mask; 231186623f4SDavid Yang u |= val; 232186623f4SDavid Yang if (u == v) 233186623f4SDavid Yang return 0; 234186623f4SDavid Yang 235186623f4SDavid Yang return yt921x_reg_write(priv, reg, u); 236186623f4SDavid Yang } 237186623f4SDavid Yang 238186623f4SDavid Yang static int yt921x_reg_set_bits(struct yt921x_priv *priv, u32 reg, u32 mask) 239186623f4SDavid Yang { 240186623f4SDavid Yang return yt921x_reg_update_bits(priv, reg, 0, mask); 241186623f4SDavid Yang } 242186623f4SDavid Yang 243186623f4SDavid Yang static int yt921x_reg_clear_bits(struct yt921x_priv *priv, u32 reg, u32 mask) 244186623f4SDavid Yang { 245186623f4SDavid Yang return yt921x_reg_update_bits(priv, reg, mask, 0); 246186623f4SDavid Yang } 247186623f4SDavid Yang 248186623f4SDavid Yang static int 249186623f4SDavid Yang yt921x_reg_toggle_bits(struct yt921x_priv *priv, u32 reg, u32 mask, bool set) 250186623f4SDavid Yang { 251186623f4SDavid Yang return yt921x_reg_update_bits(priv, reg, mask, !set ? 0 : mask); 252186623f4SDavid Yang } 253186623f4SDavid Yang 254186623f4SDavid Yang /* Some registers, like VLANn_CTRL, should always be written in 64-bit, even if 255186623f4SDavid Yang * you are to write only the lower / upper 32 bits. 256186623f4SDavid Yang * 257186623f4SDavid Yang * There is no such restriction for reading, but we still provide 64-bit read 258186623f4SDavid Yang * wrappers so that we always handle u64 values. 259186623f4SDavid Yang */ 260186623f4SDavid Yang 261186623f4SDavid Yang static int yt921x_reg64_read(struct yt921x_priv *priv, u32 reg, u64 *valp) 262186623f4SDavid Yang { 263186623f4SDavid Yang u32 lo; 264186623f4SDavid Yang u32 hi; 265186623f4SDavid Yang int res; 266186623f4SDavid Yang 267186623f4SDavid Yang res = yt921x_reg_read(priv, reg, &lo); 268186623f4SDavid Yang if (res) 269186623f4SDavid Yang return res; 270186623f4SDavid Yang res = yt921x_reg_read(priv, reg + 4, &hi); 271186623f4SDavid Yang if (res) 272186623f4SDavid Yang return res; 273186623f4SDavid Yang 274186623f4SDavid Yang *valp = ((u64)hi << 32) | lo; 275186623f4SDavid Yang return 0; 276186623f4SDavid Yang } 277186623f4SDavid Yang 278186623f4SDavid Yang static int yt921x_reg64_write(struct yt921x_priv *priv, u32 reg, u64 val) 279186623f4SDavid Yang { 280186623f4SDavid Yang int res; 281186623f4SDavid Yang 282186623f4SDavid Yang res = yt921x_reg_write(priv, reg, (u32)val); 283186623f4SDavid Yang if (res) 284186623f4SDavid Yang return res; 285186623f4SDavid Yang return yt921x_reg_write(priv, reg + 4, (u32)(val >> 32)); 286186623f4SDavid Yang } 287186623f4SDavid Yang 288186623f4SDavid Yang static int 289186623f4SDavid Yang yt921x_reg64_update_bits(struct yt921x_priv *priv, u32 reg, u64 mask, u64 val) 290186623f4SDavid Yang { 291186623f4SDavid Yang int res; 292186623f4SDavid Yang u64 v; 293186623f4SDavid Yang u64 u; 294186623f4SDavid Yang 295186623f4SDavid Yang res = yt921x_reg64_read(priv, reg, &v); 296186623f4SDavid Yang if (res) 297186623f4SDavid Yang return res; 298186623f4SDavid Yang 299186623f4SDavid Yang u = v; 300186623f4SDavid Yang u &= ~mask; 301186623f4SDavid Yang u |= val; 302186623f4SDavid Yang if (u == v) 303186623f4SDavid Yang return 0; 304186623f4SDavid Yang 305186623f4SDavid Yang return yt921x_reg64_write(priv, reg, u); 306186623f4SDavid Yang } 307186623f4SDavid Yang 308186623f4SDavid Yang static int yt921x_reg64_clear_bits(struct yt921x_priv *priv, u32 reg, u64 mask) 309186623f4SDavid Yang { 310186623f4SDavid Yang return yt921x_reg64_update_bits(priv, reg, mask, 0); 311186623f4SDavid Yang } 312186623f4SDavid Yang 313186623f4SDavid Yang static int yt921x_reg_mdio_read(void *context, u32 reg, u32 *valp) 314186623f4SDavid Yang { 315186623f4SDavid Yang struct yt921x_reg_mdio *mdio = context; 316186623f4SDavid Yang struct mii_bus *bus = mdio->bus; 317186623f4SDavid Yang int addr = mdio->addr; 318186623f4SDavid Yang u32 reg_addr; 319186623f4SDavid Yang u32 reg_data; 320186623f4SDavid Yang u32 val; 321186623f4SDavid Yang int res; 322186623f4SDavid Yang 323186623f4SDavid Yang /* Hold the mdio bus lock to avoid (un)locking for 4 times */ 324186623f4SDavid Yang mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); 325186623f4SDavid Yang 326186623f4SDavid Yang reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | 327186623f4SDavid Yang YT921X_SMI_READ; 328186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); 329186623f4SDavid Yang if (res) 330186623f4SDavid Yang goto end; 331186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); 332186623f4SDavid Yang if (res) 333186623f4SDavid Yang goto end; 334186623f4SDavid Yang 335186623f4SDavid Yang reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | 336186623f4SDavid Yang YT921X_SMI_READ; 337186623f4SDavid Yang res = __mdiobus_read(bus, addr, reg_data); 338186623f4SDavid Yang if (res < 0) 339186623f4SDavid Yang goto end; 340186623f4SDavid Yang val = (u16)res; 341186623f4SDavid Yang res = __mdiobus_read(bus, addr, reg_data); 342186623f4SDavid Yang if (res < 0) 343186623f4SDavid Yang goto end; 344186623f4SDavid Yang val = (val << 16) | (u16)res; 345186623f4SDavid Yang 346186623f4SDavid Yang *valp = val; 347186623f4SDavid Yang res = 0; 348186623f4SDavid Yang 349186623f4SDavid Yang end: 350186623f4SDavid Yang mutex_unlock(&bus->mdio_lock); 351186623f4SDavid Yang return res; 352186623f4SDavid Yang } 353186623f4SDavid Yang 354186623f4SDavid Yang static int yt921x_reg_mdio_write(void *context, u32 reg, u32 val) 355186623f4SDavid Yang { 356186623f4SDavid Yang struct yt921x_reg_mdio *mdio = context; 357186623f4SDavid Yang struct mii_bus *bus = mdio->bus; 358186623f4SDavid Yang int addr = mdio->addr; 359186623f4SDavid Yang u32 reg_addr; 360186623f4SDavid Yang u32 reg_data; 361186623f4SDavid Yang int res; 362186623f4SDavid Yang 363186623f4SDavid Yang mutex_lock_nested(&bus->mdio_lock, MDIO_MUTEX_NESTED); 364186623f4SDavid Yang 365186623f4SDavid Yang reg_addr = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_ADDR | 366186623f4SDavid Yang YT921X_SMI_WRITE; 367186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)(reg >> 16)); 368186623f4SDavid Yang if (res) 369186623f4SDavid Yang goto end; 370186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_addr, (u16)reg); 371186623f4SDavid Yang if (res) 372186623f4SDavid Yang goto end; 373186623f4SDavid Yang 374186623f4SDavid Yang reg_data = YT921X_SMI_SWITCHID(mdio->switchid) | YT921X_SMI_DATA | 375186623f4SDavid Yang YT921X_SMI_WRITE; 376186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_data, (u16)(val >> 16)); 377186623f4SDavid Yang if (res) 378186623f4SDavid Yang goto end; 379186623f4SDavid Yang res = __mdiobus_write(bus, addr, reg_data, (u16)val); 380186623f4SDavid Yang if (res) 381186623f4SDavid Yang goto end; 382186623f4SDavid Yang 383186623f4SDavid Yang res = 0; 384186623f4SDavid Yang 385186623f4SDavid Yang end: 386186623f4SDavid Yang mutex_unlock(&bus->mdio_lock); 387186623f4SDavid Yang return res; 388186623f4SDavid Yang } 389186623f4SDavid Yang 390186623f4SDavid Yang static const struct yt921x_reg_ops yt921x_reg_ops_mdio = { 391186623f4SDavid Yang .read = yt921x_reg_mdio_read, 392186623f4SDavid Yang .write = yt921x_reg_mdio_write, 393186623f4SDavid Yang }; 394186623f4SDavid Yang 395186623f4SDavid Yang /* TODO: SPI/I2C */ 396186623f4SDavid Yang 397186623f4SDavid Yang static int yt921x_intif_wait(struct yt921x_priv *priv) 398186623f4SDavid Yang { 399186623f4SDavid Yang u32 val = 0; 400186623f4SDavid Yang 401186623f4SDavid Yang return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START, 402186623f4SDavid Yang &val); 403186623f4SDavid Yang } 404186623f4SDavid Yang 405186623f4SDavid Yang static int 406186623f4SDavid Yang yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) 407186623f4SDavid Yang { 408186623f4SDavid Yang struct device *dev = to_device(priv); 409186623f4SDavid Yang u32 mask; 410186623f4SDavid Yang u32 ctrl; 411186623f4SDavid Yang u32 val; 412186623f4SDavid Yang int res; 413186623f4SDavid Yang 414186623f4SDavid Yang res = yt921x_intif_wait(priv); 415186623f4SDavid Yang if (res) 416186623f4SDavid Yang return res; 417186623f4SDavid Yang 418186623f4SDavid Yang mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | 419186623f4SDavid Yang YT921X_MBUS_CTRL_OP_M; 420186623f4SDavid Yang ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | 421186623f4SDavid Yang YT921X_MBUS_CTRL_READ; 422186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); 423186623f4SDavid Yang if (res) 424186623f4SDavid Yang return res; 425186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); 426186623f4SDavid Yang if (res) 427186623f4SDavid Yang return res; 428186623f4SDavid Yang 429186623f4SDavid Yang res = yt921x_intif_wait(priv); 430186623f4SDavid Yang if (res) 431186623f4SDavid Yang return res; 432186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val); 433186623f4SDavid Yang if (res) 434186623f4SDavid Yang return res; 435186623f4SDavid Yang 436186623f4SDavid Yang if ((u16)val != val) 437186623f4SDavid Yang dev_info(dev, 438186623f4SDavid Yang "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", 439186623f4SDavid Yang __func__, port, reg, val); 440186623f4SDavid Yang *valp = (u16)val; 441186623f4SDavid Yang return 0; 442186623f4SDavid Yang } 443186623f4SDavid Yang 444186623f4SDavid Yang static int 445186623f4SDavid Yang yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val) 446186623f4SDavid Yang { 447186623f4SDavid Yang u32 mask; 448186623f4SDavid Yang u32 ctrl; 449186623f4SDavid Yang int res; 450186623f4SDavid Yang 451186623f4SDavid Yang res = yt921x_intif_wait(priv); 452186623f4SDavid Yang if (res) 453186623f4SDavid Yang return res; 454186623f4SDavid Yang 455186623f4SDavid Yang mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | 456186623f4SDavid Yang YT921X_MBUS_CTRL_OP_M; 457186623f4SDavid Yang ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | 458186623f4SDavid Yang YT921X_MBUS_CTRL_WRITE; 459186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl); 460186623f4SDavid Yang if (res) 461186623f4SDavid Yang return res; 462186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val); 463186623f4SDavid Yang if (res) 464186623f4SDavid Yang return res; 465186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START); 466186623f4SDavid Yang if (res) 467186623f4SDavid Yang return res; 468186623f4SDavid Yang 469186623f4SDavid Yang return yt921x_intif_wait(priv); 470186623f4SDavid Yang } 471186623f4SDavid Yang 472186623f4SDavid Yang static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg) 473186623f4SDavid Yang { 474186623f4SDavid Yang struct yt921x_priv *priv = mbus->priv; 475186623f4SDavid Yang u16 val; 476186623f4SDavid Yang int res; 477186623f4SDavid Yang 478186623f4SDavid Yang if (port >= YT921X_PORT_NUM) 479186623f4SDavid Yang return U16_MAX; 480186623f4SDavid Yang 481186623f4SDavid Yang mutex_lock(&priv->reg_lock); 482186623f4SDavid Yang res = yt921x_intif_read(priv, port, reg, &val); 483186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 484186623f4SDavid Yang 485186623f4SDavid Yang if (res) 486186623f4SDavid Yang return res; 487186623f4SDavid Yang return val; 488186623f4SDavid Yang } 489186623f4SDavid Yang 490186623f4SDavid Yang static int 491186623f4SDavid Yang yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data) 492186623f4SDavid Yang { 493186623f4SDavid Yang struct yt921x_priv *priv = mbus->priv; 494186623f4SDavid Yang int res; 495186623f4SDavid Yang 496186623f4SDavid Yang if (port >= YT921X_PORT_NUM) 497186623f4SDavid Yang return -ENODEV; 498186623f4SDavid Yang 499186623f4SDavid Yang mutex_lock(&priv->reg_lock); 500186623f4SDavid Yang res = yt921x_intif_write(priv, port, reg, data); 501186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 502186623f4SDavid Yang 503186623f4SDavid Yang return res; 504186623f4SDavid Yang } 505186623f4SDavid Yang 506186623f4SDavid Yang static int 507186623f4SDavid Yang yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp) 508186623f4SDavid Yang { 509186623f4SDavid Yang struct device *dev = to_device(priv); 510186623f4SDavid Yang struct mii_bus *mbus; 511186623f4SDavid Yang int res; 512186623f4SDavid Yang 513186623f4SDavid Yang mbus = devm_mdiobus_alloc(dev); 514186623f4SDavid Yang if (!mbus) 515186623f4SDavid Yang return -ENOMEM; 516186623f4SDavid Yang 517186623f4SDavid Yang mbus->name = "YT921x internal MDIO bus"; 518186623f4SDavid Yang snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev)); 519186623f4SDavid Yang mbus->priv = priv; 520186623f4SDavid Yang mbus->read = yt921x_mbus_int_read; 521186623f4SDavid Yang mbus->write = yt921x_mbus_int_write; 522186623f4SDavid Yang mbus->parent = dev; 523186623f4SDavid Yang mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0); 524186623f4SDavid Yang 525186623f4SDavid Yang res = devm_of_mdiobus_register(dev, mbus, mnp); 526186623f4SDavid Yang if (res) 527186623f4SDavid Yang return res; 528186623f4SDavid Yang 529186623f4SDavid Yang priv->mbus_int = mbus; 530186623f4SDavid Yang 531186623f4SDavid Yang return 0; 532186623f4SDavid Yang } 533186623f4SDavid Yang 534186623f4SDavid Yang static int yt921x_extif_wait(struct yt921x_priv *priv) 535186623f4SDavid Yang { 536186623f4SDavid Yang u32 val = 0; 537186623f4SDavid Yang 538186623f4SDavid Yang return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START, 539186623f4SDavid Yang &val); 540186623f4SDavid Yang } 541186623f4SDavid Yang 542186623f4SDavid Yang static int 543186623f4SDavid Yang yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp) 544186623f4SDavid Yang { 545186623f4SDavid Yang struct device *dev = to_device(priv); 546186623f4SDavid Yang u32 mask; 547186623f4SDavid Yang u32 ctrl; 548186623f4SDavid Yang u32 val; 549186623f4SDavid Yang int res; 550186623f4SDavid Yang 551186623f4SDavid Yang res = yt921x_extif_wait(priv); 552186623f4SDavid Yang if (res) 553186623f4SDavid Yang return res; 554186623f4SDavid Yang 555186623f4SDavid Yang mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | 556186623f4SDavid Yang YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; 557186623f4SDavid Yang ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | 558186623f4SDavid Yang YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ; 559186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); 560186623f4SDavid Yang if (res) 561186623f4SDavid Yang return res; 562186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); 563186623f4SDavid Yang if (res) 564186623f4SDavid Yang return res; 565186623f4SDavid Yang 566186623f4SDavid Yang res = yt921x_extif_wait(priv); 567186623f4SDavid Yang if (res) 568186623f4SDavid Yang return res; 569186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val); 570186623f4SDavid Yang if (res) 571186623f4SDavid Yang return res; 572186623f4SDavid Yang 573186623f4SDavid Yang if ((u16)val != val) 574186623f4SDavid Yang dev_info(dev, 575186623f4SDavid Yang "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n", 576186623f4SDavid Yang __func__, port, reg, val); 577186623f4SDavid Yang *valp = (u16)val; 578186623f4SDavid Yang return 0; 579186623f4SDavid Yang } 580186623f4SDavid Yang 581186623f4SDavid Yang static int 582186623f4SDavid Yang yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val) 583186623f4SDavid Yang { 584186623f4SDavid Yang u32 mask; 585186623f4SDavid Yang u32 ctrl; 586186623f4SDavid Yang int res; 587186623f4SDavid Yang 588186623f4SDavid Yang res = yt921x_extif_wait(priv); 589186623f4SDavid Yang if (res) 590186623f4SDavid Yang return res; 591186623f4SDavid Yang 592186623f4SDavid Yang mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M | 593186623f4SDavid Yang YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M; 594186623f4SDavid Yang ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) | 595186623f4SDavid Yang YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE; 596186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl); 597186623f4SDavid Yang if (res) 598186623f4SDavid Yang return res; 599186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val); 600186623f4SDavid Yang if (res) 601186623f4SDavid Yang return res; 602186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START); 603186623f4SDavid Yang if (res) 604186623f4SDavid Yang return res; 605186623f4SDavid Yang 606186623f4SDavid Yang return yt921x_extif_wait(priv); 607186623f4SDavid Yang } 608186623f4SDavid Yang 609186623f4SDavid Yang static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg) 610186623f4SDavid Yang { 611186623f4SDavid Yang struct yt921x_priv *priv = mbus->priv; 612186623f4SDavid Yang u16 val; 613186623f4SDavid Yang int res; 614186623f4SDavid Yang 615186623f4SDavid Yang mutex_lock(&priv->reg_lock); 616186623f4SDavid Yang res = yt921x_extif_read(priv, port, reg, &val); 617186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 618186623f4SDavid Yang 619186623f4SDavid Yang if (res) 620186623f4SDavid Yang return res; 621186623f4SDavid Yang return val; 622186623f4SDavid Yang } 623186623f4SDavid Yang 624186623f4SDavid Yang static int 625186623f4SDavid Yang yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data) 626186623f4SDavid Yang { 627186623f4SDavid Yang struct yt921x_priv *priv = mbus->priv; 628186623f4SDavid Yang int res; 629186623f4SDavid Yang 630186623f4SDavid Yang mutex_lock(&priv->reg_lock); 631186623f4SDavid Yang res = yt921x_extif_write(priv, port, reg, data); 632186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 633186623f4SDavid Yang 634186623f4SDavid Yang return res; 635186623f4SDavid Yang } 636186623f4SDavid Yang 637186623f4SDavid Yang static int 638186623f4SDavid Yang yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp) 639186623f4SDavid Yang { 640186623f4SDavid Yang struct device *dev = to_device(priv); 641186623f4SDavid Yang struct mii_bus *mbus; 642186623f4SDavid Yang int res; 643186623f4SDavid Yang 644186623f4SDavid Yang mbus = devm_mdiobus_alloc(dev); 645186623f4SDavid Yang if (!mbus) 646186623f4SDavid Yang return -ENOMEM; 647186623f4SDavid Yang 648186623f4SDavid Yang mbus->name = "YT921x external MDIO bus"; 649186623f4SDavid Yang snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev)); 650186623f4SDavid Yang mbus->priv = priv; 651186623f4SDavid Yang /* TODO: c45? */ 652186623f4SDavid Yang mbus->read = yt921x_mbus_ext_read; 653186623f4SDavid Yang mbus->write = yt921x_mbus_ext_write; 654186623f4SDavid Yang mbus->parent = dev; 655186623f4SDavid Yang 656186623f4SDavid Yang res = devm_of_mdiobus_register(dev, mbus, mnp); 657186623f4SDavid Yang if (res) 658186623f4SDavid Yang return res; 659186623f4SDavid Yang 660186623f4SDavid Yang priv->mbus_ext = mbus; 661186623f4SDavid Yang 662186623f4SDavid Yang return 0; 663186623f4SDavid Yang } 664186623f4SDavid Yang 665186623f4SDavid Yang /* Read and handle overflow of 32bit MIBs. MIB buffer must be zeroed before. */ 666186623f4SDavid Yang static int yt921x_read_mib(struct yt921x_priv *priv, int port) 667186623f4SDavid Yang { 668186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 669186623f4SDavid Yang struct device *dev = to_device(priv); 670186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 671186623f4SDavid Yang int res = 0; 672186623f4SDavid Yang 673186623f4SDavid Yang /* Reading of yt921x_port::mib is not protected by a lock and it's vain 674186623f4SDavid Yang * to keep its consistency, since we have to read registers one by one 675186623f4SDavid Yang * and there is no way to make a snapshot of MIB stats. 676186623f4SDavid Yang * 677186623f4SDavid Yang * Writing (by this function only) is and should be protected by 678186623f4SDavid Yang * reg_lock. 679186623f4SDavid Yang */ 680186623f4SDavid Yang 681186623f4SDavid Yang for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { 682186623f4SDavid Yang const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; 683186623f4SDavid Yang u32 reg = YT921X_MIBn_DATA0(port) + desc->offset; 684186623f4SDavid Yang u64 *valp = &((u64 *)mib)[i]; 685186623f4SDavid Yang u64 val = *valp; 686186623f4SDavid Yang u32 val0; 687186623f4SDavid Yang u32 val1; 688186623f4SDavid Yang 689186623f4SDavid Yang res = yt921x_reg_read(priv, reg, &val0); 690186623f4SDavid Yang if (res) 691186623f4SDavid Yang break; 692186623f4SDavid Yang 693186623f4SDavid Yang if (desc->size <= 1) { 694186623f4SDavid Yang if (val < (u32)val) 695186623f4SDavid Yang /* overflow */ 696186623f4SDavid Yang val += (u64)U32_MAX + 1; 697186623f4SDavid Yang val &= ~U32_MAX; 698186623f4SDavid Yang val |= val0; 699186623f4SDavid Yang } else { 700186623f4SDavid Yang res = yt921x_reg_read(priv, reg + 4, &val1); 701186623f4SDavid Yang if (res) 702186623f4SDavid Yang break; 703510026a3SDavid Yang val = ((u64)val1 << 32) | val0; 704186623f4SDavid Yang } 705186623f4SDavid Yang 706186623f4SDavid Yang WRITE_ONCE(*valp, val); 707186623f4SDavid Yang } 708186623f4SDavid Yang 709186623f4SDavid Yang pp->rx_frames = mib->rx_64byte + mib->rx_65_127byte + 710186623f4SDavid Yang mib->rx_128_255byte + mib->rx_256_511byte + 711186623f4SDavid Yang mib->rx_512_1023byte + mib->rx_1024_1518byte + 712186623f4SDavid Yang mib->rx_jumbo; 713186623f4SDavid Yang pp->tx_frames = mib->tx_64byte + mib->tx_65_127byte + 714186623f4SDavid Yang mib->tx_128_255byte + mib->tx_256_511byte + 715186623f4SDavid Yang mib->tx_512_1023byte + mib->tx_1024_1518byte + 716186623f4SDavid Yang mib->tx_jumbo; 717186623f4SDavid Yang 718186623f4SDavid Yang if (res) 719186623f4SDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "read stats for", 720186623f4SDavid Yang port, res); 721186623f4SDavid Yang return res; 722186623f4SDavid Yang } 723186623f4SDavid Yang 724186623f4SDavid Yang static void yt921x_poll_mib(struct work_struct *work) 725186623f4SDavid Yang { 726186623f4SDavid Yang struct yt921x_port *pp = container_of_const(work, struct yt921x_port, 727186623f4SDavid Yang mib_read.work); 728186623f4SDavid Yang struct yt921x_priv *priv = (void *)(pp - pp->index) - 729186623f4SDavid Yang offsetof(struct yt921x_priv, ports); 730186623f4SDavid Yang unsigned long delay = YT921X_STATS_INTERVAL_JIFFIES; 731186623f4SDavid Yang int port = pp->index; 732186623f4SDavid Yang int res; 733186623f4SDavid Yang 734186623f4SDavid Yang mutex_lock(&priv->reg_lock); 735186623f4SDavid Yang res = yt921x_read_mib(priv, port); 736186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 737186623f4SDavid Yang if (res) 738186623f4SDavid Yang delay *= 4; 739186623f4SDavid Yang 740186623f4SDavid Yang schedule_delayed_work(&pp->mib_read, delay); 741186623f4SDavid Yang } 742186623f4SDavid Yang 743186623f4SDavid Yang static void 744186623f4SDavid Yang yt921x_dsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, 745186623f4SDavid Yang uint8_t *data) 746186623f4SDavid Yang { 747186623f4SDavid Yang if (stringset != ETH_SS_STATS) 748186623f4SDavid Yang return; 749186623f4SDavid Yang 750186623f4SDavid Yang for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { 751186623f4SDavid Yang const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; 752186623f4SDavid Yang 753186623f4SDavid Yang if (desc->name) 754186623f4SDavid Yang ethtool_puts(&data, desc->name); 755186623f4SDavid Yang } 756186623f4SDavid Yang } 757186623f4SDavid Yang 758186623f4SDavid Yang static void 759186623f4SDavid Yang yt921x_dsa_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *data) 760186623f4SDavid Yang { 761186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 762186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 763186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 764186623f4SDavid Yang size_t j; 765186623f4SDavid Yang 766186623f4SDavid Yang mutex_lock(&priv->reg_lock); 767186623f4SDavid Yang yt921x_read_mib(priv, port); 768186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 769186623f4SDavid Yang 770186623f4SDavid Yang j = 0; 771186623f4SDavid Yang for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { 772186623f4SDavid Yang const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; 773186623f4SDavid Yang 774186623f4SDavid Yang if (!desc->name) 775186623f4SDavid Yang continue; 776186623f4SDavid Yang 777186623f4SDavid Yang data[j] = ((u64 *)mib)[i]; 778186623f4SDavid Yang j++; 779186623f4SDavid Yang } 780186623f4SDavid Yang } 781186623f4SDavid Yang 782186623f4SDavid Yang static int yt921x_dsa_get_sset_count(struct dsa_switch *ds, int port, int sset) 783186623f4SDavid Yang { 784186623f4SDavid Yang int cnt = 0; 785186623f4SDavid Yang 786186623f4SDavid Yang if (sset != ETH_SS_STATS) 787186623f4SDavid Yang return 0; 788186623f4SDavid Yang 789186623f4SDavid Yang for (size_t i = 0; i < ARRAY_SIZE(yt921x_mib_descs); i++) { 790186623f4SDavid Yang const struct yt921x_mib_desc *desc = &yt921x_mib_descs[i]; 791186623f4SDavid Yang 792186623f4SDavid Yang if (desc->name) 793186623f4SDavid Yang cnt++; 794186623f4SDavid Yang } 795186623f4SDavid Yang 796186623f4SDavid Yang return cnt; 797186623f4SDavid Yang } 798186623f4SDavid Yang 799186623f4SDavid Yang static void 800186623f4SDavid Yang yt921x_dsa_get_eth_mac_stats(struct dsa_switch *ds, int port, 801186623f4SDavid Yang struct ethtool_eth_mac_stats *mac_stats) 802186623f4SDavid Yang { 803186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 804186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 805186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 806186623f4SDavid Yang 807186623f4SDavid Yang mutex_lock(&priv->reg_lock); 808186623f4SDavid Yang yt921x_read_mib(priv, port); 809186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 810186623f4SDavid Yang 811186623f4SDavid Yang mac_stats->FramesTransmittedOK = pp->tx_frames; 812186623f4SDavid Yang mac_stats->SingleCollisionFrames = mib->tx_single_collisions; 813186623f4SDavid Yang mac_stats->MultipleCollisionFrames = mib->tx_multiple_collisions; 814186623f4SDavid Yang mac_stats->FramesReceivedOK = pp->rx_frames; 815186623f4SDavid Yang mac_stats->FrameCheckSequenceErrors = mib->rx_crc_errors; 816186623f4SDavid Yang mac_stats->AlignmentErrors = mib->rx_alignment_errors; 817186623f4SDavid Yang mac_stats->OctetsTransmittedOK = mib->tx_good_bytes; 818186623f4SDavid Yang mac_stats->FramesWithDeferredXmissions = mib->tx_deferred; 819186623f4SDavid Yang mac_stats->LateCollisions = mib->tx_late_collisions; 820186623f4SDavid Yang mac_stats->FramesAbortedDueToXSColls = mib->tx_aborted_errors; 821186623f4SDavid Yang /* mac_stats->FramesLostDueToIntMACXmitError */ 822186623f4SDavid Yang /* mac_stats->CarrierSenseErrors */ 823186623f4SDavid Yang mac_stats->OctetsReceivedOK = mib->rx_good_bytes; 824186623f4SDavid Yang /* mac_stats->FramesLostDueToIntMACRcvError */ 825186623f4SDavid Yang mac_stats->MulticastFramesXmittedOK = mib->tx_multicast; 826186623f4SDavid Yang mac_stats->BroadcastFramesXmittedOK = mib->tx_broadcast; 827186623f4SDavid Yang /* mac_stats->FramesWithExcessiveDeferral */ 828186623f4SDavid Yang mac_stats->MulticastFramesReceivedOK = mib->rx_multicast; 829186623f4SDavid Yang mac_stats->BroadcastFramesReceivedOK = mib->rx_broadcast; 830186623f4SDavid Yang /* mac_stats->InRangeLengthErrors */ 831186623f4SDavid Yang /* mac_stats->OutOfRangeLengthField */ 832186623f4SDavid Yang mac_stats->FrameTooLongErrors = mib->rx_oversize_errors; 833186623f4SDavid Yang } 834186623f4SDavid Yang 835186623f4SDavid Yang static void 836186623f4SDavid Yang yt921x_dsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port, 837186623f4SDavid Yang struct ethtool_eth_ctrl_stats *ctrl_stats) 838186623f4SDavid Yang { 839186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 840186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 841186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 842186623f4SDavid Yang 843186623f4SDavid Yang mutex_lock(&priv->reg_lock); 844186623f4SDavid Yang yt921x_read_mib(priv, port); 845186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 846186623f4SDavid Yang 847186623f4SDavid Yang ctrl_stats->MACControlFramesTransmitted = mib->tx_pause; 848186623f4SDavid Yang ctrl_stats->MACControlFramesReceived = mib->rx_pause; 849186623f4SDavid Yang /* ctrl_stats->UnsupportedOpcodesReceived */ 850186623f4SDavid Yang } 851186623f4SDavid Yang 852186623f4SDavid Yang static const struct ethtool_rmon_hist_range yt921x_rmon_ranges[] = { 853186623f4SDavid Yang { 0, 64 }, 854186623f4SDavid Yang { 65, 127 }, 855186623f4SDavid Yang { 128, 255 }, 856186623f4SDavid Yang { 256, 511 }, 857186623f4SDavid Yang { 512, 1023 }, 858186623f4SDavid Yang { 1024, 1518 }, 859186623f4SDavid Yang { 1519, YT921X_FRAME_SIZE_MAX }, 860186623f4SDavid Yang {} 861186623f4SDavid Yang }; 862186623f4SDavid Yang 863186623f4SDavid Yang static void 864186623f4SDavid Yang yt921x_dsa_get_rmon_stats(struct dsa_switch *ds, int port, 865186623f4SDavid Yang struct ethtool_rmon_stats *rmon_stats, 866186623f4SDavid Yang const struct ethtool_rmon_hist_range **ranges) 867186623f4SDavid Yang { 868186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 869186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 870186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 871186623f4SDavid Yang 872186623f4SDavid Yang mutex_lock(&priv->reg_lock); 873186623f4SDavid Yang yt921x_read_mib(priv, port); 874186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 875186623f4SDavid Yang 876186623f4SDavid Yang *ranges = yt921x_rmon_ranges; 877186623f4SDavid Yang 878186623f4SDavid Yang rmon_stats->undersize_pkts = mib->rx_undersize_errors; 879186623f4SDavid Yang rmon_stats->oversize_pkts = mib->rx_oversize_errors; 880186623f4SDavid Yang rmon_stats->fragments = mib->rx_alignment_errors; 881186623f4SDavid Yang /* rmon_stats->jabbers */ 882186623f4SDavid Yang 883186623f4SDavid Yang rmon_stats->hist[0] = mib->rx_64byte; 884186623f4SDavid Yang rmon_stats->hist[1] = mib->rx_65_127byte; 885186623f4SDavid Yang rmon_stats->hist[2] = mib->rx_128_255byte; 886186623f4SDavid Yang rmon_stats->hist[3] = mib->rx_256_511byte; 887186623f4SDavid Yang rmon_stats->hist[4] = mib->rx_512_1023byte; 888186623f4SDavid Yang rmon_stats->hist[5] = mib->rx_1024_1518byte; 889186623f4SDavid Yang rmon_stats->hist[6] = mib->rx_jumbo; 890186623f4SDavid Yang 891186623f4SDavid Yang rmon_stats->hist_tx[0] = mib->tx_64byte; 892186623f4SDavid Yang rmon_stats->hist_tx[1] = mib->tx_65_127byte; 893186623f4SDavid Yang rmon_stats->hist_tx[2] = mib->tx_128_255byte; 894186623f4SDavid Yang rmon_stats->hist_tx[3] = mib->tx_256_511byte; 895186623f4SDavid Yang rmon_stats->hist_tx[4] = mib->tx_512_1023byte; 896186623f4SDavid Yang rmon_stats->hist_tx[5] = mib->tx_1024_1518byte; 897186623f4SDavid Yang rmon_stats->hist_tx[6] = mib->tx_jumbo; 898186623f4SDavid Yang } 899186623f4SDavid Yang 900186623f4SDavid Yang static void 901186623f4SDavid Yang yt921x_dsa_get_stats64(struct dsa_switch *ds, int port, 902186623f4SDavid Yang struct rtnl_link_stats64 *stats) 903186623f4SDavid Yang { 904186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 905186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 906186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 907186623f4SDavid Yang 908186623f4SDavid Yang stats->rx_length_errors = mib->rx_undersize_errors + 909186623f4SDavid Yang mib->rx_fragment_errors; 910186623f4SDavid Yang stats->rx_over_errors = mib->rx_oversize_errors; 911186623f4SDavid Yang stats->rx_crc_errors = mib->rx_crc_errors; 912186623f4SDavid Yang stats->rx_frame_errors = mib->rx_alignment_errors; 913186623f4SDavid Yang /* stats->rx_fifo_errors */ 914186623f4SDavid Yang /* stats->rx_missed_errors */ 915186623f4SDavid Yang 916186623f4SDavid Yang stats->tx_aborted_errors = mib->tx_aborted_errors; 917186623f4SDavid Yang /* stats->tx_carrier_errors */ 918186623f4SDavid Yang stats->tx_fifo_errors = mib->tx_undersize_errors; 919186623f4SDavid Yang /* stats->tx_heartbeat_errors */ 920186623f4SDavid Yang stats->tx_window_errors = mib->tx_late_collisions; 921186623f4SDavid Yang 922186623f4SDavid Yang stats->rx_packets = pp->rx_frames; 923186623f4SDavid Yang stats->tx_packets = pp->tx_frames; 924186623f4SDavid Yang stats->rx_bytes = mib->rx_good_bytes - ETH_FCS_LEN * stats->rx_packets; 925186623f4SDavid Yang stats->tx_bytes = mib->tx_good_bytes - ETH_FCS_LEN * stats->tx_packets; 926186623f4SDavid Yang stats->rx_errors = stats->rx_length_errors + stats->rx_over_errors + 927186623f4SDavid Yang stats->rx_crc_errors + stats->rx_frame_errors; 928186623f4SDavid Yang stats->tx_errors = stats->tx_aborted_errors + stats->tx_fifo_errors + 929186623f4SDavid Yang stats->tx_window_errors; 930186623f4SDavid Yang stats->rx_dropped = mib->rx_dropped; 931186623f4SDavid Yang /* stats->tx_dropped */ 932186623f4SDavid Yang stats->multicast = mib->rx_multicast; 933186623f4SDavid Yang stats->collisions = mib->tx_collisions; 934186623f4SDavid Yang } 935186623f4SDavid Yang 936186623f4SDavid Yang static void 937186623f4SDavid Yang yt921x_dsa_get_pause_stats(struct dsa_switch *ds, int port, 938186623f4SDavid Yang struct ethtool_pause_stats *pause_stats) 939186623f4SDavid Yang { 940186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 941186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 942186623f4SDavid Yang struct yt921x_mib *mib = &pp->mib; 943186623f4SDavid Yang 944186623f4SDavid Yang mutex_lock(&priv->reg_lock); 945186623f4SDavid Yang yt921x_read_mib(priv, port); 946186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 947186623f4SDavid Yang 948186623f4SDavid Yang pause_stats->tx_pause_frames = mib->tx_pause; 949186623f4SDavid Yang pause_stats->rx_pause_frames = mib->rx_pause; 950186623f4SDavid Yang } 951186623f4SDavid Yang 952186623f4SDavid Yang static int 953186623f4SDavid Yang yt921x_set_eee(struct yt921x_priv *priv, int port, struct ethtool_keee *e) 954186623f4SDavid Yang { 955186623f4SDavid Yang /* Poor datasheet for EEE operations; don't ask if you are confused */ 956186623f4SDavid Yang 957186623f4SDavid Yang bool enable = e->eee_enabled; 958186623f4SDavid Yang u16 new_mask; 959186623f4SDavid Yang int res; 960186623f4SDavid Yang 961186623f4SDavid Yang /* Enable / disable global EEE */ 962186623f4SDavid Yang new_mask = priv->eee_ports_mask; 963186623f4SDavid Yang new_mask &= ~BIT(port); 964186623f4SDavid Yang new_mask |= !enable ? 0 : BIT(port); 965186623f4SDavid Yang 966186623f4SDavid Yang if (!!new_mask != !!priv->eee_ports_mask) { 967186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_FUNC, 968186623f4SDavid Yang YT921X_PON_STRAP_EEE, !!new_mask); 969186623f4SDavid Yang if (res) 970186623f4SDavid Yang return res; 971186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PON_STRAP_VAL, 972186623f4SDavid Yang YT921X_PON_STRAP_EEE, !!new_mask); 973186623f4SDavid Yang if (res) 974186623f4SDavid Yang return res; 975186623f4SDavid Yang } 976186623f4SDavid Yang 977186623f4SDavid Yang priv->eee_ports_mask = new_mask; 978186623f4SDavid Yang 979186623f4SDavid Yang /* Enable / disable port EEE */ 980186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_EEE_CTRL, 981186623f4SDavid Yang YT921X_EEE_CTRL_ENn(port), enable); 982186623f4SDavid Yang if (res) 983186623f4SDavid Yang return res; 984186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_EEEn_VAL(port), 985186623f4SDavid Yang YT921X_EEE_VAL_DATA, enable); 986186623f4SDavid Yang if (res) 987186623f4SDavid Yang return res; 988186623f4SDavid Yang 989186623f4SDavid Yang return 0; 990186623f4SDavid Yang } 991186623f4SDavid Yang 992186623f4SDavid Yang static int 993186623f4SDavid Yang yt921x_dsa_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_keee *e) 994186623f4SDavid Yang { 995186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 996186623f4SDavid Yang int res; 997186623f4SDavid Yang 998186623f4SDavid Yang mutex_lock(&priv->reg_lock); 999186623f4SDavid Yang res = yt921x_set_eee(priv, port, e); 1000186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1001186623f4SDavid Yang 1002186623f4SDavid Yang return res; 1003186623f4SDavid Yang } 1004186623f4SDavid Yang 1005186623f4SDavid Yang static int 1006186623f4SDavid Yang yt921x_dsa_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 1007186623f4SDavid Yang { 1008186623f4SDavid Yang /* Only serves as packet filter, since the frame size is always set to 1009186623f4SDavid Yang * maximum after reset 1010186623f4SDavid Yang */ 1011186623f4SDavid Yang 1012186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1013186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 1014186623f4SDavid Yang int frame_size; 1015186623f4SDavid Yang int res; 1016186623f4SDavid Yang 1017186623f4SDavid Yang frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN; 1018186623f4SDavid Yang if (dsa_port_is_cpu(dp)) 1019186623f4SDavid Yang frame_size += YT921X_TAG_LEN; 1020186623f4SDavid Yang 1021186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1022186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_MACn_FRAME(port), 1023186623f4SDavid Yang YT921X_MAC_FRAME_SIZE_M, 1024186623f4SDavid Yang YT921X_MAC_FRAME_SIZE(frame_size)); 1025186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1026186623f4SDavid Yang 1027186623f4SDavid Yang return res; 1028186623f4SDavid Yang } 1029186623f4SDavid Yang 1030186623f4SDavid Yang static int yt921x_dsa_port_max_mtu(struct dsa_switch *ds, int port) 1031186623f4SDavid Yang { 1032186623f4SDavid Yang /* Only called for user ports, exclude tag len here */ 1033186623f4SDavid Yang return YT921X_FRAME_SIZE_MAX - ETH_HLEN - ETH_FCS_LEN - YT921X_TAG_LEN; 1034186623f4SDavid Yang } 1035186623f4SDavid Yang 1036186623f4SDavid Yang static int 1037186623f4SDavid Yang yt921x_mirror_del(struct yt921x_priv *priv, int port, bool ingress) 1038186623f4SDavid Yang { 1039186623f4SDavid Yang u32 mask; 1040186623f4SDavid Yang 1041186623f4SDavid Yang if (ingress) 1042186623f4SDavid Yang mask = YT921X_MIRROR_IGR_PORTn(port); 1043186623f4SDavid Yang else 1044186623f4SDavid Yang mask = YT921X_MIRROR_EGR_PORTn(port); 1045186623f4SDavid Yang return yt921x_reg_clear_bits(priv, YT921X_MIRROR, mask); 1046186623f4SDavid Yang } 1047186623f4SDavid Yang 1048186623f4SDavid Yang static int 1049186623f4SDavid Yang yt921x_mirror_add(struct yt921x_priv *priv, int port, bool ingress, 1050186623f4SDavid Yang int to_local_port, struct netlink_ext_ack *extack) 1051186623f4SDavid Yang { 1052186623f4SDavid Yang u32 srcs; 1053186623f4SDavid Yang u32 ctrl; 1054186623f4SDavid Yang u32 val; 1055186623f4SDavid Yang u32 dst; 1056186623f4SDavid Yang int res; 1057186623f4SDavid Yang 1058186623f4SDavid Yang if (ingress) 1059186623f4SDavid Yang srcs = YT921X_MIRROR_IGR_PORTn(port); 1060186623f4SDavid Yang else 1061186623f4SDavid Yang srcs = YT921X_MIRROR_EGR_PORTn(port); 1062186623f4SDavid Yang dst = YT921X_MIRROR_PORT(to_local_port); 1063186623f4SDavid Yang 1064186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_MIRROR, &val); 1065186623f4SDavid Yang if (res) 1066186623f4SDavid Yang return res; 1067186623f4SDavid Yang 1068186623f4SDavid Yang /* other mirror tasks & different dst port -> conflict */ 1069186623f4SDavid Yang if ((val & ~srcs & (YT921X_MIRROR_EGR_PORTS_M | 1070186623f4SDavid Yang YT921X_MIRROR_IGR_PORTS_M)) && 1071186623f4SDavid Yang (val & YT921X_MIRROR_PORT_M) != dst) { 1072186623f4SDavid Yang NL_SET_ERR_MSG_MOD(extack, 1073186623f4SDavid Yang "Sniffer port is already configured, delete existing rules & retry"); 1074186623f4SDavid Yang return -EBUSY; 1075186623f4SDavid Yang } 1076186623f4SDavid Yang 1077186623f4SDavid Yang ctrl = val & ~YT921X_MIRROR_PORT_M; 1078186623f4SDavid Yang ctrl |= srcs; 1079186623f4SDavid Yang ctrl |= dst; 1080186623f4SDavid Yang 1081186623f4SDavid Yang if (ctrl == val) 1082186623f4SDavid Yang return 0; 1083186623f4SDavid Yang 1084186623f4SDavid Yang return yt921x_reg_write(priv, YT921X_MIRROR, ctrl); 1085186623f4SDavid Yang } 1086186623f4SDavid Yang 1087186623f4SDavid Yang static void 1088186623f4SDavid Yang yt921x_dsa_port_mirror_del(struct dsa_switch *ds, int port, 1089186623f4SDavid Yang struct dsa_mall_mirror_tc_entry *mirror) 1090186623f4SDavid Yang { 1091186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1092186623f4SDavid Yang struct device *dev = to_device(priv); 1093186623f4SDavid Yang int res; 1094186623f4SDavid Yang 1095186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1096186623f4SDavid Yang res = yt921x_mirror_del(priv, port, mirror->ingress); 1097186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1098186623f4SDavid Yang 1099186623f4SDavid Yang if (res) 1100186623f4SDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "unmirror", 1101186623f4SDavid Yang port, res); 1102186623f4SDavid Yang } 1103186623f4SDavid Yang 1104186623f4SDavid Yang static int 1105186623f4SDavid Yang yt921x_dsa_port_mirror_add(struct dsa_switch *ds, int port, 1106186623f4SDavid Yang struct dsa_mall_mirror_tc_entry *mirror, 1107186623f4SDavid Yang bool ingress, struct netlink_ext_ack *extack) 1108186623f4SDavid Yang { 1109186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1110186623f4SDavid Yang int res; 1111186623f4SDavid Yang 1112186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1113186623f4SDavid Yang res = yt921x_mirror_add(priv, port, ingress, 1114186623f4SDavid Yang mirror->to_local_port, extack); 1115186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1116186623f4SDavid Yang 1117186623f4SDavid Yang return res; 1118186623f4SDavid Yang } 1119186623f4SDavid Yang 1120186623f4SDavid Yang static int yt921x_fdb_wait(struct yt921x_priv *priv, u32 *valp) 1121186623f4SDavid Yang { 1122186623f4SDavid Yang struct device *dev = to_device(priv); 1123186623f4SDavid Yang u32 val = YT921X_FDB_RESULT_DONE; 1124186623f4SDavid Yang int res; 1125186623f4SDavid Yang 1126186623f4SDavid Yang res = yt921x_reg_wait(priv, YT921X_FDB_RESULT, YT921X_FDB_RESULT_DONE, 1127186623f4SDavid Yang &val); 1128186623f4SDavid Yang if (res) { 112922795871SColin Ian King dev_err(dev, "FDB probably stuck\n"); 1130186623f4SDavid Yang return res; 1131186623f4SDavid Yang } 1132186623f4SDavid Yang 1133186623f4SDavid Yang *valp = val; 1134186623f4SDavid Yang return 0; 1135186623f4SDavid Yang } 1136186623f4SDavid Yang 1137186623f4SDavid Yang static int 1138186623f4SDavid Yang yt921x_fdb_in01(struct yt921x_priv *priv, const unsigned char *addr, 1139186623f4SDavid Yang u16 vid, u32 ctrl1) 1140186623f4SDavid Yang { 1141186623f4SDavid Yang u32 ctrl; 1142186623f4SDavid Yang int res; 1143186623f4SDavid Yang 1144186623f4SDavid Yang ctrl = (addr[0] << 24) | (addr[1] << 16) | (addr[2] << 8) | addr[3]; 1145186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN0, ctrl); 1146186623f4SDavid Yang if (res) 1147186623f4SDavid Yang return res; 1148186623f4SDavid Yang 1149186623f4SDavid Yang ctrl = ctrl1 | YT921X_FDB_IO1_FID(vid) | (addr[4] << 8) | addr[5]; 1150186623f4SDavid Yang return yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); 1151186623f4SDavid Yang } 1152186623f4SDavid Yang 1153186623f4SDavid Yang static int 1154186623f4SDavid Yang yt921x_fdb_has(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 1155186623f4SDavid Yang u16 *indexp) 1156186623f4SDavid Yang { 1157186623f4SDavid Yang u32 ctrl; 1158186623f4SDavid Yang u32 val; 1159186623f4SDavid Yang int res; 1160186623f4SDavid Yang 1161186623f4SDavid Yang res = yt921x_fdb_in01(priv, addr, vid, 0); 1162186623f4SDavid Yang if (res) 1163186623f4SDavid Yang return res; 1164186623f4SDavid Yang 1165186623f4SDavid Yang ctrl = 0; 1166186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 1167186623f4SDavid Yang if (res) 1168186623f4SDavid Yang return res; 1169186623f4SDavid Yang 1170186623f4SDavid Yang ctrl = YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; 1171186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1172186623f4SDavid Yang if (res) 1173186623f4SDavid Yang return res; 1174186623f4SDavid Yang 1175186623f4SDavid Yang res = yt921x_fdb_wait(priv, &val); 1176186623f4SDavid Yang if (res) 1177186623f4SDavid Yang return res; 1178186623f4SDavid Yang if (val & YT921X_FDB_RESULT_NOTFOUND) { 1179186623f4SDavid Yang *indexp = YT921X_FDB_NUM; 1180186623f4SDavid Yang return 0; 1181186623f4SDavid Yang } 1182186623f4SDavid Yang 1183186623f4SDavid Yang *indexp = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); 1184186623f4SDavid Yang return 0; 1185186623f4SDavid Yang } 1186186623f4SDavid Yang 1187186623f4SDavid Yang static int 1188186623f4SDavid Yang yt921x_fdb_read(struct yt921x_priv *priv, unsigned char *addr, u16 *vidp, 1189186623f4SDavid Yang u16 *ports_maskp, u16 *indexp, u8 *statusp) 1190186623f4SDavid Yang { 1191186623f4SDavid Yang struct device *dev = to_device(priv); 1192186623f4SDavid Yang u16 index; 1193186623f4SDavid Yang u32 data0; 1194186623f4SDavid Yang u32 data1; 1195186623f4SDavid Yang u32 data2; 1196186623f4SDavid Yang u32 val; 1197186623f4SDavid Yang int res; 1198186623f4SDavid Yang 1199186623f4SDavid Yang res = yt921x_fdb_wait(priv, &val); 1200186623f4SDavid Yang if (res) 1201186623f4SDavid Yang return res; 1202186623f4SDavid Yang if (val & YT921X_FDB_RESULT_NOTFOUND) { 1203186623f4SDavid Yang *ports_maskp = 0; 1204186623f4SDavid Yang return 0; 1205186623f4SDavid Yang } 1206186623f4SDavid Yang index = FIELD_GET(YT921X_FDB_RESULT_INDEX_M, val); 1207186623f4SDavid Yang 1208186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &data1); 1209186623f4SDavid Yang if (res) 1210186623f4SDavid Yang return res; 1211186623f4SDavid Yang if ((data1 & YT921X_FDB_IO1_STATUS_M) == 1212186623f4SDavid Yang YT921X_FDB_IO1_STATUS_INVALID) { 1213186623f4SDavid Yang *ports_maskp = 0; 1214186623f4SDavid Yang return 0; 1215186623f4SDavid Yang } 1216186623f4SDavid Yang 1217186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT0, &data0); 1218186623f4SDavid Yang if (res) 1219186623f4SDavid Yang return res; 1220186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &data2); 1221186623f4SDavid Yang if (res) 1222186623f4SDavid Yang return res; 1223186623f4SDavid Yang 1224186623f4SDavid Yang addr[0] = data0 >> 24; 1225186623f4SDavid Yang addr[1] = data0 >> 16; 1226186623f4SDavid Yang addr[2] = data0 >> 8; 1227186623f4SDavid Yang addr[3] = data0; 1228186623f4SDavid Yang addr[4] = data1 >> 8; 1229186623f4SDavid Yang addr[5] = data1; 1230186623f4SDavid Yang *vidp = FIELD_GET(YT921X_FDB_IO1_FID_M, data1); 1231186623f4SDavid Yang *indexp = index; 1232186623f4SDavid Yang *ports_maskp = FIELD_GET(YT921X_FDB_IO2_EGR_PORTS_M, data2); 1233186623f4SDavid Yang *statusp = FIELD_GET(YT921X_FDB_IO1_STATUS_M, data1); 1234186623f4SDavid Yang 1235186623f4SDavid Yang dev_dbg(dev, 1236186623f4SDavid Yang "%s: index 0x%x, mac %02x:%02x:%02x:%02x:%02x:%02x, vid %d, ports 0x%x, status %d\n", 1237186623f4SDavid Yang __func__, *indexp, addr[0], addr[1], addr[2], addr[3], 1238186623f4SDavid Yang addr[4], addr[5], *vidp, *ports_maskp, *statusp); 1239186623f4SDavid Yang return 0; 1240186623f4SDavid Yang } 1241186623f4SDavid Yang 1242186623f4SDavid Yang static int 1243186623f4SDavid Yang yt921x_fdb_dump(struct yt921x_priv *priv, u16 ports_mask, 1244186623f4SDavid Yang dsa_fdb_dump_cb_t *cb, void *data) 1245186623f4SDavid Yang { 1246186623f4SDavid Yang unsigned char addr[ETH_ALEN]; 1247186623f4SDavid Yang u8 status; 1248186623f4SDavid Yang u16 pmask; 1249186623f4SDavid Yang u16 index; 1250186623f4SDavid Yang u32 ctrl; 1251186623f4SDavid Yang u16 vid; 1252186623f4SDavid Yang int res; 1253186623f4SDavid Yang 1254186623f4SDavid Yang ctrl = YT921X_FDB_OP_INDEX(0) | YT921X_FDB_OP_MODE_INDEX | 1255186623f4SDavid Yang YT921X_FDB_OP_OP_GET_ONE | YT921X_FDB_OP_START; 1256186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1257186623f4SDavid Yang if (res) 1258186623f4SDavid Yang return res; 1259186623f4SDavid Yang res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, &status); 1260186623f4SDavid Yang if (res) 1261186623f4SDavid Yang return res; 1262186623f4SDavid Yang if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { 1263186623f4SDavid Yang res = cb(addr, vid, 1264186623f4SDavid Yang status == YT921X_FDB_ENTRY_STATUS_STATIC, data); 1265186623f4SDavid Yang if (res) 1266186623f4SDavid Yang return res; 1267186623f4SDavid Yang } 1268186623f4SDavid Yang 1269186623f4SDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 1270186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 1271186623f4SDavid Yang if (res) 1272186623f4SDavid Yang return res; 1273186623f4SDavid Yang 1274186623f4SDavid Yang index = 0; 1275186623f4SDavid Yang do { 1276186623f4SDavid Yang ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | 1277186623f4SDavid Yang YT921X_FDB_OP_NEXT_TYPE_UCAST_PORT | 1278186623f4SDavid Yang YT921X_FDB_OP_OP_GET_NEXT | YT921X_FDB_OP_START; 1279186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1280186623f4SDavid Yang if (res) 1281186623f4SDavid Yang return res; 1282186623f4SDavid Yang 1283186623f4SDavid Yang res = yt921x_fdb_read(priv, addr, &vid, &pmask, &index, 1284186623f4SDavid Yang &status); 1285186623f4SDavid Yang if (res) 1286186623f4SDavid Yang return res; 1287186623f4SDavid Yang if (!pmask) 1288186623f4SDavid Yang break; 1289186623f4SDavid Yang 1290186623f4SDavid Yang if ((pmask & ports_mask) && !is_multicast_ether_addr(addr)) { 1291186623f4SDavid Yang res = cb(addr, vid, 1292186623f4SDavid Yang status == YT921X_FDB_ENTRY_STATUS_STATIC, 1293186623f4SDavid Yang data); 1294186623f4SDavid Yang if (res) 1295186623f4SDavid Yang return res; 1296186623f4SDavid Yang } 1297186623f4SDavid Yang 1298186623f4SDavid Yang /* Never call GET_NEXT with 4095, otherwise it will hang 1299186623f4SDavid Yang * forever until a reset! 1300186623f4SDavid Yang */ 1301186623f4SDavid Yang } while (index < YT921X_FDB_NUM - 1); 1302186623f4SDavid Yang 1303186623f4SDavid Yang return 0; 1304186623f4SDavid Yang } 1305186623f4SDavid Yang 1306186623f4SDavid Yang static int 1307186623f4SDavid Yang yt921x_fdb_flush_raw(struct yt921x_priv *priv, u16 ports_mask, u16 vid, 1308186623f4SDavid Yang bool flush_static) 1309186623f4SDavid Yang { 1310186623f4SDavid Yang u32 ctrl; 1311186623f4SDavid Yang u32 val; 1312186623f4SDavid Yang int res; 1313186623f4SDavid Yang 1314186623f4SDavid Yang if (vid < 4096) { 1315186623f4SDavid Yang ctrl = YT921X_FDB_IO1_FID(vid); 1316186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl); 1317186623f4SDavid Yang if (res) 1318186623f4SDavid Yang return res; 1319186623f4SDavid Yang } 1320186623f4SDavid Yang 1321186623f4SDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 1322186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 1323186623f4SDavid Yang if (res) 1324186623f4SDavid Yang return res; 1325186623f4SDavid Yang 1326186623f4SDavid Yang ctrl = YT921X_FDB_OP_OP_FLUSH | YT921X_FDB_OP_START; 1327186623f4SDavid Yang if (vid >= 4096) 1328186623f4SDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_PORT; 1329186623f4SDavid Yang else 1330186623f4SDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_PORT_VID; 1331186623f4SDavid Yang if (flush_static) 1332186623f4SDavid Yang ctrl |= YT921X_FDB_OP_FLUSH_STATIC; 1333186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1334186623f4SDavid Yang if (res) 1335186623f4SDavid Yang return res; 1336186623f4SDavid Yang 1337186623f4SDavid Yang res = yt921x_fdb_wait(priv, &val); 1338186623f4SDavid Yang if (res) 1339186623f4SDavid Yang return res; 1340186623f4SDavid Yang 1341186623f4SDavid Yang return 0; 1342186623f4SDavid Yang } 1343186623f4SDavid Yang 1344186623f4SDavid Yang static int 1345186623f4SDavid Yang yt921x_fdb_flush_port(struct yt921x_priv *priv, int port, bool flush_static) 1346186623f4SDavid Yang { 1347186623f4SDavid Yang return yt921x_fdb_flush_raw(priv, BIT(port), 4096, flush_static); 1348186623f4SDavid Yang } 1349186623f4SDavid Yang 1350186623f4SDavid Yang static int 1351186623f4SDavid Yang yt921x_fdb_add_index_in12(struct yt921x_priv *priv, u16 index, u16 ctrl1, 1352186623f4SDavid Yang u16 ctrl2) 1353186623f4SDavid Yang { 1354186623f4SDavid Yang u32 ctrl; 1355186623f4SDavid Yang u32 val; 1356186623f4SDavid Yang int res; 1357186623f4SDavid Yang 1358186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN1, ctrl1); 1359186623f4SDavid Yang if (res) 1360186623f4SDavid Yang return res; 1361186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl2); 1362186623f4SDavid Yang if (res) 1363186623f4SDavid Yang return res; 1364186623f4SDavid Yang 1365186623f4SDavid Yang ctrl = YT921X_FDB_OP_INDEX(index) | YT921X_FDB_OP_MODE_INDEX | 1366186623f4SDavid Yang YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; 1367186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1368186623f4SDavid Yang if (res) 1369186623f4SDavid Yang return res; 1370186623f4SDavid Yang 1371186623f4SDavid Yang return yt921x_fdb_wait(priv, &val); 1372186623f4SDavid Yang } 1373186623f4SDavid Yang 1374186623f4SDavid Yang static int 1375186623f4SDavid Yang yt921x_fdb_add(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 1376186623f4SDavid Yang u16 ports_mask) 1377186623f4SDavid Yang { 1378186623f4SDavid Yang u32 ctrl; 1379186623f4SDavid Yang u32 val; 1380186623f4SDavid Yang int res; 1381186623f4SDavid Yang 1382186623f4SDavid Yang ctrl = YT921X_FDB_IO1_STATUS_STATIC; 1383186623f4SDavid Yang res = yt921x_fdb_in01(priv, addr, vid, ctrl); 1384186623f4SDavid Yang if (res) 1385186623f4SDavid Yang return res; 1386186623f4SDavid Yang 1387186623f4SDavid Yang ctrl = YT921X_FDB_IO2_EGR_PORTS(ports_mask); 1388186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_IN2, ctrl); 1389186623f4SDavid Yang if (res) 1390186623f4SDavid Yang return res; 1391186623f4SDavid Yang 1392186623f4SDavid Yang ctrl = YT921X_FDB_OP_OP_ADD | YT921X_FDB_OP_START; 1393186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1394186623f4SDavid Yang if (res) 1395186623f4SDavid Yang return res; 1396186623f4SDavid Yang 1397186623f4SDavid Yang return yt921x_fdb_wait(priv, &val); 1398186623f4SDavid Yang } 1399186623f4SDavid Yang 1400186623f4SDavid Yang static int 1401186623f4SDavid Yang yt921x_fdb_leave(struct yt921x_priv *priv, const unsigned char *addr, 1402186623f4SDavid Yang u16 vid, u16 ports_mask) 1403186623f4SDavid Yang { 1404186623f4SDavid Yang u16 index; 1405186623f4SDavid Yang u32 ctrl1; 1406186623f4SDavid Yang u32 ctrl2; 1407186623f4SDavid Yang u32 ctrl; 1408186623f4SDavid Yang u32 val2; 1409186623f4SDavid Yang u32 val; 1410186623f4SDavid Yang int res; 1411186623f4SDavid Yang 1412186623f4SDavid Yang /* Check for presence */ 1413186623f4SDavid Yang res = yt921x_fdb_has(priv, addr, vid, &index); 1414186623f4SDavid Yang if (res) 1415186623f4SDavid Yang return res; 1416186623f4SDavid Yang if (index >= YT921X_FDB_NUM) 1417186623f4SDavid Yang return 0; 1418186623f4SDavid Yang 1419186623f4SDavid Yang /* Check if action required */ 1420186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); 1421186623f4SDavid Yang if (res) 1422186623f4SDavid Yang return res; 1423186623f4SDavid Yang 1424186623f4SDavid Yang ctrl2 = val2 & ~YT921X_FDB_IO2_EGR_PORTS(ports_mask); 1425186623f4SDavid Yang if (ctrl2 == val2) 1426186623f4SDavid Yang return 0; 1427186623f4SDavid Yang if (!(ctrl2 & YT921X_FDB_IO2_EGR_PORTS_M)) { 1428186623f4SDavid Yang ctrl = YT921X_FDB_OP_OP_DEL | YT921X_FDB_OP_START; 1429186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FDB_OP, ctrl); 1430186623f4SDavid Yang if (res) 1431186623f4SDavid Yang return res; 1432186623f4SDavid Yang 1433186623f4SDavid Yang return yt921x_fdb_wait(priv, &val); 1434186623f4SDavid Yang } 1435186623f4SDavid Yang 1436186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &ctrl1); 1437186623f4SDavid Yang if (res) 1438186623f4SDavid Yang return res; 1439186623f4SDavid Yang 1440186623f4SDavid Yang return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); 1441186623f4SDavid Yang } 1442186623f4SDavid Yang 1443186623f4SDavid Yang static int 1444186623f4SDavid Yang yt921x_fdb_join(struct yt921x_priv *priv, const unsigned char *addr, u16 vid, 1445186623f4SDavid Yang u16 ports_mask) 1446186623f4SDavid Yang { 1447186623f4SDavid Yang u16 index; 1448186623f4SDavid Yang u32 ctrl1; 1449186623f4SDavid Yang u32 ctrl2; 1450186623f4SDavid Yang u32 val1; 1451186623f4SDavid Yang u32 val2; 1452186623f4SDavid Yang int res; 1453186623f4SDavid Yang 1454186623f4SDavid Yang /* Check for presence */ 1455186623f4SDavid Yang res = yt921x_fdb_has(priv, addr, vid, &index); 1456186623f4SDavid Yang if (res) 1457186623f4SDavid Yang return res; 1458186623f4SDavid Yang if (index >= YT921X_FDB_NUM) 1459186623f4SDavid Yang return yt921x_fdb_add(priv, addr, vid, ports_mask); 1460186623f4SDavid Yang 1461186623f4SDavid Yang /* Check if action required */ 1462186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT1, &val1); 1463186623f4SDavid Yang if (res) 1464186623f4SDavid Yang return res; 1465186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_FDB_OUT2, &val2); 1466186623f4SDavid Yang if (res) 1467186623f4SDavid Yang return res; 1468186623f4SDavid Yang 1469186623f4SDavid Yang ctrl1 = val1 & ~YT921X_FDB_IO1_STATUS_M; 1470186623f4SDavid Yang ctrl1 |= YT921X_FDB_IO1_STATUS_STATIC; 1471186623f4SDavid Yang ctrl2 = val2 | YT921X_FDB_IO2_EGR_PORTS(ports_mask); 1472186623f4SDavid Yang if (ctrl1 == val1 && ctrl2 == val2) 1473186623f4SDavid Yang return 0; 1474186623f4SDavid Yang 1475186623f4SDavid Yang return yt921x_fdb_add_index_in12(priv, index, ctrl1, ctrl2); 1476186623f4SDavid Yang } 1477186623f4SDavid Yang 1478186623f4SDavid Yang static int 1479186623f4SDavid Yang yt921x_dsa_port_fdb_dump(struct dsa_switch *ds, int port, 1480186623f4SDavid Yang dsa_fdb_dump_cb_t *cb, void *data) 1481186623f4SDavid Yang { 1482186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1483186623f4SDavid Yang int res; 1484186623f4SDavid Yang 1485186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1486186623f4SDavid Yang /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" 1487186623f4SDavid Yang * only wants to see unicast 1488186623f4SDavid Yang */ 1489186623f4SDavid Yang res = yt921x_fdb_dump(priv, BIT(port), cb, data); 1490186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1491186623f4SDavid Yang 1492186623f4SDavid Yang return res; 1493186623f4SDavid Yang } 1494186623f4SDavid Yang 1495186623f4SDavid Yang static void yt921x_dsa_port_fast_age(struct dsa_switch *ds, int port) 1496186623f4SDavid Yang { 1497186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1498186623f4SDavid Yang struct device *dev = to_device(priv); 1499186623f4SDavid Yang int res; 1500186623f4SDavid Yang 1501186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1502186623f4SDavid Yang res = yt921x_fdb_flush_port(priv, port, false); 1503186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1504186623f4SDavid Yang 1505186623f4SDavid Yang if (res) 1506186623f4SDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "clear FDB for", 1507186623f4SDavid Yang port, res); 1508186623f4SDavid Yang } 1509186623f4SDavid Yang 1510186623f4SDavid Yang static int 1511186623f4SDavid Yang yt921x_dsa_set_ageing_time(struct dsa_switch *ds, unsigned int msecs) 1512186623f4SDavid Yang { 1513186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1514186623f4SDavid Yang u32 ctrl; 1515186623f4SDavid Yang int res; 1516186623f4SDavid Yang 1517186623f4SDavid Yang /* AGEING reg is set in 5s step */ 1518186623f4SDavid Yang ctrl = clamp(msecs / 5000, 1, U16_MAX); 1519186623f4SDavid Yang 1520186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1521186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_AGEING, ctrl); 1522186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1523186623f4SDavid Yang 1524186623f4SDavid Yang return res; 1525186623f4SDavid Yang } 1526186623f4SDavid Yang 1527186623f4SDavid Yang static int 1528186623f4SDavid Yang yt921x_dsa_port_fdb_del(struct dsa_switch *ds, int port, 1529186623f4SDavid Yang const unsigned char *addr, u16 vid, struct dsa_db db) 1530186623f4SDavid Yang { 1531186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1532186623f4SDavid Yang int res; 1533186623f4SDavid Yang 1534186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1535186623f4SDavid Yang res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); 1536186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1537186623f4SDavid Yang 1538186623f4SDavid Yang return res; 1539186623f4SDavid Yang } 1540186623f4SDavid Yang 1541186623f4SDavid Yang static int 1542186623f4SDavid Yang yt921x_dsa_port_fdb_add(struct dsa_switch *ds, int port, 1543186623f4SDavid Yang const unsigned char *addr, u16 vid, struct dsa_db db) 1544186623f4SDavid Yang { 1545186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1546186623f4SDavid Yang int res; 1547186623f4SDavid Yang 1548186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1549186623f4SDavid Yang res = yt921x_fdb_join(priv, addr, vid, BIT(port)); 1550186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1551186623f4SDavid Yang 1552186623f4SDavid Yang return res; 1553186623f4SDavid Yang } 1554186623f4SDavid Yang 1555186623f4SDavid Yang static int 1556186623f4SDavid Yang yt921x_dsa_port_mdb_del(struct dsa_switch *ds, int port, 1557186623f4SDavid Yang const struct switchdev_obj_port_mdb *mdb, 1558186623f4SDavid Yang struct dsa_db db) 1559186623f4SDavid Yang { 1560186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1561186623f4SDavid Yang const unsigned char *addr = mdb->addr; 1562186623f4SDavid Yang u16 vid = mdb->vid; 1563186623f4SDavid Yang int res; 1564186623f4SDavid Yang 1565186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1566186623f4SDavid Yang res = yt921x_fdb_leave(priv, addr, vid, BIT(port)); 1567186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1568186623f4SDavid Yang 1569186623f4SDavid Yang return res; 1570186623f4SDavid Yang } 1571186623f4SDavid Yang 1572186623f4SDavid Yang static int 1573186623f4SDavid Yang yt921x_dsa_port_mdb_add(struct dsa_switch *ds, int port, 1574186623f4SDavid Yang const struct switchdev_obj_port_mdb *mdb, 1575186623f4SDavid Yang struct dsa_db db) 1576186623f4SDavid Yang { 1577186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1578186623f4SDavid Yang const unsigned char *addr = mdb->addr; 1579186623f4SDavid Yang u16 vid = mdb->vid; 1580186623f4SDavid Yang int res; 1581186623f4SDavid Yang 1582186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1583186623f4SDavid Yang res = yt921x_fdb_join(priv, addr, vid, BIT(port)); 1584186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1585186623f4SDavid Yang 1586186623f4SDavid Yang return res; 1587186623f4SDavid Yang } 1588186623f4SDavid Yang 1589186623f4SDavid Yang static int 1590186623f4SDavid Yang yt921x_port_set_pvid(struct yt921x_priv *priv, int port, u16 vid) 1591186623f4SDavid Yang { 1592186623f4SDavid Yang u32 mask; 1593186623f4SDavid Yang u32 ctrl; 1594186623f4SDavid Yang 1595186623f4SDavid Yang mask = YT921X_PORT_VLAN_CTRL_CVID_M; 1596186623f4SDavid Yang ctrl = YT921X_PORT_VLAN_CTRL_CVID(vid); 1597186623f4SDavid Yang return yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL(port), 1598186623f4SDavid Yang mask, ctrl); 1599186623f4SDavid Yang } 1600186623f4SDavid Yang 1601186623f4SDavid Yang static int 1602186623f4SDavid Yang yt921x_vlan_filtering(struct yt921x_priv *priv, int port, bool vlan_filtering) 1603186623f4SDavid Yang { 1604186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 1605186623f4SDavid Yang struct net_device *bdev; 1606186623f4SDavid Yang u16 pvid; 1607186623f4SDavid Yang u32 mask; 1608186623f4SDavid Yang u32 ctrl; 1609186623f4SDavid Yang int res; 1610186623f4SDavid Yang 1611186623f4SDavid Yang bdev = dsa_port_bridge_dev_get(dp); 1612186623f4SDavid Yang 1613186623f4SDavid Yang if (!bdev || !vlan_filtering) 1614186623f4SDavid Yang pvid = YT921X_VID_UNWARE; 1615186623f4SDavid Yang else 1616186623f4SDavid Yang br_vlan_get_pvid(bdev, &pvid); 1617186623f4SDavid Yang res = yt921x_port_set_pvid(priv, port, pvid); 1618186623f4SDavid Yang if (res) 1619186623f4SDavid Yang return res; 1620186623f4SDavid Yang 1621186623f4SDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_TAGGED | 1622186623f4SDavid Yang YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 1623186623f4SDavid Yang ctrl = 0; 1624186623f4SDavid Yang /* Do not drop tagged frames here; let VLAN_IGR_FILTER do it */ 1625186623f4SDavid Yang if (vlan_filtering && !pvid) 1626186623f4SDavid Yang ctrl |= YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 1627186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), 1628186623f4SDavid Yang mask, ctrl); 1629186623f4SDavid Yang if (res) 1630186623f4SDavid Yang return res; 1631186623f4SDavid Yang 1632186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_VLAN_IGR_FILTER, 1633186623f4SDavid Yang YT921X_VLAN_IGR_FILTER_PORTn(port), 1634186623f4SDavid Yang vlan_filtering); 1635186623f4SDavid Yang if (res) 1636186623f4SDavid Yang return res; 1637186623f4SDavid Yang 1638186623f4SDavid Yang /* Turn on / off VLAN awareness */ 1639186623f4SDavid Yang mask = YT921X_PORT_IGR_TPIDn_CTAG_M; 1640186623f4SDavid Yang if (!vlan_filtering) 1641186623f4SDavid Yang ctrl = 0; 1642186623f4SDavid Yang else 1643186623f4SDavid Yang ctrl = YT921X_PORT_IGR_TPIDn_CTAG(0); 1644186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_IGR_TPID(port), 1645186623f4SDavid Yang mask, ctrl); 1646186623f4SDavid Yang if (res) 1647186623f4SDavid Yang return res; 1648186623f4SDavid Yang 1649186623f4SDavid Yang return 0; 1650186623f4SDavid Yang } 1651186623f4SDavid Yang 1652186623f4SDavid Yang static int 1653186623f4SDavid Yang yt921x_vlan_del(struct yt921x_priv *priv, int port, u16 vid) 1654186623f4SDavid Yang { 1655186623f4SDavid Yang u64 mask64; 1656186623f4SDavid Yang 1657186623f4SDavid Yang mask64 = YT921X_VLAN_CTRL_PORTS(port) | 1658186623f4SDavid Yang YT921X_VLAN_CTRL_UNTAG_PORTn(port); 1659186623f4SDavid Yang 1660186623f4SDavid Yang return yt921x_reg64_clear_bits(priv, YT921X_VLANn_CTRL(vid), mask64); 1661186623f4SDavid Yang } 1662186623f4SDavid Yang 1663186623f4SDavid Yang static int 1664186623f4SDavid Yang yt921x_vlan_add(struct yt921x_priv *priv, int port, u16 vid, bool untagged) 1665186623f4SDavid Yang { 1666186623f4SDavid Yang u64 mask64; 1667186623f4SDavid Yang u64 ctrl64; 1668186623f4SDavid Yang 1669186623f4SDavid Yang mask64 = YT921X_VLAN_CTRL_PORTn(port) | 1670186623f4SDavid Yang YT921X_VLAN_CTRL_PORTS(priv->cpu_ports_mask); 1671186623f4SDavid Yang ctrl64 = mask64; 1672186623f4SDavid Yang 1673186623f4SDavid Yang mask64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port); 1674186623f4SDavid Yang if (untagged) 1675186623f4SDavid Yang ctrl64 |= YT921X_VLAN_CTRL_UNTAG_PORTn(port); 1676186623f4SDavid Yang 1677186623f4SDavid Yang return yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(vid), 1678186623f4SDavid Yang mask64, ctrl64); 1679186623f4SDavid Yang } 1680186623f4SDavid Yang 1681186623f4SDavid Yang static int 1682186623f4SDavid Yang yt921x_pvid_clear(struct yt921x_priv *priv, int port) 1683186623f4SDavid Yang { 1684186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 1685186623f4SDavid Yang bool vlan_filtering; 1686186623f4SDavid Yang u32 mask; 1687186623f4SDavid Yang int res; 1688186623f4SDavid Yang 1689186623f4SDavid Yang vlan_filtering = dsa_port_is_vlan_filtering(dp); 1690186623f4SDavid Yang 1691186623f4SDavid Yang res = yt921x_port_set_pvid(priv, port, 1692186623f4SDavid Yang vlan_filtering ? 0 : YT921X_VID_UNWARE); 1693186623f4SDavid Yang if (res) 1694186623f4SDavid Yang return res; 1695186623f4SDavid Yang 1696186623f4SDavid Yang if (vlan_filtering) { 1697186623f4SDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 1698186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), 1699186623f4SDavid Yang mask); 1700186623f4SDavid Yang if (res) 1701186623f4SDavid Yang return res; 1702186623f4SDavid Yang } 1703186623f4SDavid Yang 1704186623f4SDavid Yang return 0; 1705186623f4SDavid Yang } 1706186623f4SDavid Yang 1707186623f4SDavid Yang static int 1708186623f4SDavid Yang yt921x_pvid_set(struct yt921x_priv *priv, int port, u16 vid) 1709186623f4SDavid Yang { 1710186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(&priv->ds, port); 1711186623f4SDavid Yang bool vlan_filtering; 1712186623f4SDavid Yang u32 mask; 1713186623f4SDavid Yang int res; 1714186623f4SDavid Yang 1715186623f4SDavid Yang vlan_filtering = dsa_port_is_vlan_filtering(dp); 1716186623f4SDavid Yang 1717186623f4SDavid Yang if (vlan_filtering) { 1718186623f4SDavid Yang res = yt921x_port_set_pvid(priv, port, vid); 1719186623f4SDavid Yang if (res) 1720186623f4SDavid Yang return res; 1721186623f4SDavid Yang } 1722186623f4SDavid Yang 1723186623f4SDavid Yang mask = YT921X_PORT_VLAN_CTRL1_CVLAN_DROP_UNTAGGED; 1724186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_VLAN_CTRL1(port), mask); 1725186623f4SDavid Yang if (res) 1726186623f4SDavid Yang return res; 1727186623f4SDavid Yang 1728186623f4SDavid Yang return 0; 1729186623f4SDavid Yang } 1730186623f4SDavid Yang 1731186623f4SDavid Yang static int 1732186623f4SDavid Yang yt921x_dsa_port_vlan_filtering(struct dsa_switch *ds, int port, 1733186623f4SDavid Yang bool vlan_filtering, 1734186623f4SDavid Yang struct netlink_ext_ack *extack) 1735186623f4SDavid Yang { 1736186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1737186623f4SDavid Yang int res; 1738186623f4SDavid Yang 1739186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 1740186623f4SDavid Yang return 0; 1741186623f4SDavid Yang 1742186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1743186623f4SDavid Yang res = yt921x_vlan_filtering(priv, port, vlan_filtering); 1744186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1745186623f4SDavid Yang 1746186623f4SDavid Yang return res; 1747186623f4SDavid Yang } 1748186623f4SDavid Yang 1749186623f4SDavid Yang static int 1750186623f4SDavid Yang yt921x_dsa_port_vlan_del(struct dsa_switch *ds, int port, 1751186623f4SDavid Yang const struct switchdev_obj_port_vlan *vlan) 1752186623f4SDavid Yang { 1753186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1754186623f4SDavid Yang u16 vid = vlan->vid; 1755186623f4SDavid Yang u16 pvid; 1756186623f4SDavid Yang int res; 1757186623f4SDavid Yang 1758186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 1759186623f4SDavid Yang return 0; 1760186623f4SDavid Yang 1761186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1762186623f4SDavid Yang do { 1763186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 1764186623f4SDavid Yang struct net_device *bdev; 1765186623f4SDavid Yang 1766186623f4SDavid Yang res = yt921x_vlan_del(priv, port, vid); 1767186623f4SDavid Yang if (res) 1768186623f4SDavid Yang break; 1769186623f4SDavid Yang 1770186623f4SDavid Yang bdev = dsa_port_bridge_dev_get(dp); 1771186623f4SDavid Yang if (bdev) { 1772186623f4SDavid Yang br_vlan_get_pvid(bdev, &pvid); 1773186623f4SDavid Yang if (pvid == vid) 1774186623f4SDavid Yang res = yt921x_pvid_clear(priv, port); 1775186623f4SDavid Yang } 1776186623f4SDavid Yang } while (0); 1777186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1778186623f4SDavid Yang 1779186623f4SDavid Yang return res; 1780186623f4SDavid Yang } 1781186623f4SDavid Yang 1782186623f4SDavid Yang static int 1783186623f4SDavid Yang yt921x_dsa_port_vlan_add(struct dsa_switch *ds, int port, 1784186623f4SDavid Yang const struct switchdev_obj_port_vlan *vlan, 1785186623f4SDavid Yang struct netlink_ext_ack *extack) 1786186623f4SDavid Yang { 1787186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 1788186623f4SDavid Yang u16 vid = vlan->vid; 1789186623f4SDavid Yang u16 pvid; 1790186623f4SDavid Yang int res; 1791186623f4SDavid Yang 1792186623f4SDavid Yang /* CPU port is supposed to be a member of every VLAN; see 1793186623f4SDavid Yang * yt921x_vlan_add() and yt921x_port_setup() 1794186623f4SDavid Yang */ 1795186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 1796186623f4SDavid Yang return 0; 1797186623f4SDavid Yang 1798186623f4SDavid Yang mutex_lock(&priv->reg_lock); 1799186623f4SDavid Yang do { 1800186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 1801186623f4SDavid Yang struct net_device *bdev; 1802186623f4SDavid Yang 1803186623f4SDavid Yang res = yt921x_vlan_add(priv, port, vid, 1804186623f4SDavid Yang vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 1805186623f4SDavid Yang if (res) 1806186623f4SDavid Yang break; 1807186623f4SDavid Yang 1808186623f4SDavid Yang bdev = dsa_port_bridge_dev_get(dp); 1809186623f4SDavid Yang if (bdev) { 1810186623f4SDavid Yang if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { 1811186623f4SDavid Yang res = yt921x_pvid_set(priv, port, vid); 1812186623f4SDavid Yang } else { 1813186623f4SDavid Yang br_vlan_get_pvid(bdev, &pvid); 1814186623f4SDavid Yang if (pvid == vid) 1815186623f4SDavid Yang res = yt921x_pvid_clear(priv, port); 1816186623f4SDavid Yang } 1817186623f4SDavid Yang } 1818186623f4SDavid Yang } while (0); 1819186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 1820186623f4SDavid Yang 1821186623f4SDavid Yang return res; 1822186623f4SDavid Yang } 1823186623f4SDavid Yang 1824186623f4SDavid Yang static int yt921x_userport_standalone(struct yt921x_priv *priv, int port) 1825186623f4SDavid Yang { 1826186623f4SDavid Yang u32 mask; 1827186623f4SDavid Yang u32 ctrl; 1828186623f4SDavid Yang int res; 1829186623f4SDavid Yang 1830186623f4SDavid Yang ctrl = ~priv->cpu_ports_mask; 1831186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), ctrl); 1832186623f4SDavid Yang if (res) 1833186623f4SDavid Yang return res; 1834186623f4SDavid Yang 1835186623f4SDavid Yang /* Turn off FDB learning to prevent FDB pollution */ 1836186623f4SDavid Yang mask = YT921X_PORT_LEARN_DIS; 1837186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_LEARN(port), mask); 1838186623f4SDavid Yang if (res) 1839186623f4SDavid Yang return res; 1840186623f4SDavid Yang 1841186623f4SDavid Yang /* Turn off VLAN awareness */ 1842186623f4SDavid Yang mask = YT921X_PORT_IGR_TPIDn_CTAG_M; 1843186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_IGR_TPID(port), mask); 1844186623f4SDavid Yang if (res) 1845186623f4SDavid Yang return res; 1846186623f4SDavid Yang 1847186623f4SDavid Yang /* Unrelated since learning is off and all packets are trapped; 1848186623f4SDavid Yang * set it anyway 1849186623f4SDavid Yang */ 1850186623f4SDavid Yang res = yt921x_port_set_pvid(priv, port, YT921X_VID_UNWARE); 1851186623f4SDavid Yang if (res) 1852186623f4SDavid Yang return res; 1853186623f4SDavid Yang 1854186623f4SDavid Yang return 0; 1855186623f4SDavid Yang } 1856186623f4SDavid Yang 1857186623f4SDavid Yang static int yt921x_userport_bridge(struct yt921x_priv *priv, int port) 1858186623f4SDavid Yang { 1859186623f4SDavid Yang u32 mask; 1860186623f4SDavid Yang int res; 1861186623f4SDavid Yang 1862186623f4SDavid Yang mask = YT921X_PORT_LEARN_DIS; 1863186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_LEARN(port), mask); 1864186623f4SDavid Yang if (res) 1865186623f4SDavid Yang return res; 1866186623f4SDavid Yang 1867186623f4SDavid Yang return 0; 1868186623f4SDavid Yang } 1869186623f4SDavid Yang 1870186623f4SDavid Yang static int yt921x_isolate(struct yt921x_priv *priv, int port) 1871186623f4SDavid Yang { 1872186623f4SDavid Yang u32 mask; 1873186623f4SDavid Yang int res; 1874186623f4SDavid Yang 1875186623f4SDavid Yang mask = BIT(port); 1876186623f4SDavid Yang for (int i = 0; i < YT921X_PORT_NUM; i++) { 1877186623f4SDavid Yang if ((BIT(i) & priv->cpu_ports_mask) || i == port) 1878186623f4SDavid Yang continue; 1879186623f4SDavid Yang 1880186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_PORTn_ISOLATION(i), 1881186623f4SDavid Yang mask); 1882186623f4SDavid Yang if (res) 1883186623f4SDavid Yang return res; 1884186623f4SDavid Yang } 1885186623f4SDavid Yang 1886186623f4SDavid Yang return 0; 1887186623f4SDavid Yang } 1888186623f4SDavid Yang 1889186623f4SDavid Yang /* Make sure to include the CPU port in ports_mask, or your bridge will 1890186623f4SDavid Yang * not have it. 1891186623f4SDavid Yang */ 1892186623f4SDavid Yang static int yt921x_bridge(struct yt921x_priv *priv, u16 ports_mask) 1893186623f4SDavid Yang { 1894186623f4SDavid Yang unsigned long targets_mask = ports_mask & ~priv->cpu_ports_mask; 1895186623f4SDavid Yang u32 isolated_mask; 1896186623f4SDavid Yang u32 ctrl; 1897186623f4SDavid Yang int port; 1898186623f4SDavid Yang int res; 1899186623f4SDavid Yang 1900186623f4SDavid Yang isolated_mask = 0; 1901186623f4SDavid Yang for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { 1902186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 1903186623f4SDavid Yang 1904186623f4SDavid Yang if (pp->isolated) 1905186623f4SDavid Yang isolated_mask |= BIT(port); 1906186623f4SDavid Yang } 1907186623f4SDavid Yang 1908186623f4SDavid Yang /* Block from non-cpu bridge ports ... */ 1909186623f4SDavid Yang for_each_set_bit(port, &targets_mask, YT921X_PORT_NUM) { 1910186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 1911186623f4SDavid Yang 1912186623f4SDavid Yang /* to non-bridge ports */ 1913186623f4SDavid Yang ctrl = ~ports_mask; 1914186623f4SDavid Yang /* to isolated ports when isolated */ 1915186623f4SDavid Yang if (pp->isolated) 1916186623f4SDavid Yang ctrl |= isolated_mask; 1917186623f4SDavid Yang /* to itself when non-hairpin */ 1918186623f4SDavid Yang if (!pp->hairpin) 1919186623f4SDavid Yang ctrl |= BIT(port); 1920186623f4SDavid Yang else 1921186623f4SDavid Yang ctrl &= ~BIT(port); 1922186623f4SDavid Yang 1923186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), 1924186623f4SDavid Yang ctrl); 1925186623f4SDavid Yang if (res) 1926186623f4SDavid Yang return res; 1927186623f4SDavid Yang } 1928186623f4SDavid Yang 1929186623f4SDavid Yang return 0; 1930186623f4SDavid Yang } 1931186623f4SDavid Yang 1932186623f4SDavid Yang static int yt921x_bridge_leave(struct yt921x_priv *priv, int port) 1933186623f4SDavid Yang { 1934186623f4SDavid Yang int res; 1935186623f4SDavid Yang 1936186623f4SDavid Yang res = yt921x_userport_standalone(priv, port); 1937186623f4SDavid Yang if (res) 1938186623f4SDavid Yang return res; 1939186623f4SDavid Yang 1940186623f4SDavid Yang res = yt921x_isolate(priv, port); 1941186623f4SDavid Yang if (res) 1942186623f4SDavid Yang return res; 1943186623f4SDavid Yang 1944186623f4SDavid Yang return 0; 1945186623f4SDavid Yang } 1946186623f4SDavid Yang 1947186623f4SDavid Yang static int 1948186623f4SDavid Yang yt921x_bridge_join(struct yt921x_priv *priv, int port, u16 ports_mask) 1949186623f4SDavid Yang { 1950186623f4SDavid Yang int res; 1951186623f4SDavid Yang 1952186623f4SDavid Yang res = yt921x_userport_bridge(priv, port); 1953186623f4SDavid Yang if (res) 1954186623f4SDavid Yang return res; 1955186623f4SDavid Yang 1956186623f4SDavid Yang res = yt921x_bridge(priv, ports_mask); 1957186623f4SDavid Yang if (res) 1958186623f4SDavid Yang return res; 1959186623f4SDavid Yang 1960186623f4SDavid Yang return 0; 1961186623f4SDavid Yang } 1962186623f4SDavid Yang 1963186623f4SDavid Yang static u32 1964186623f4SDavid Yang dsa_bridge_ports(struct dsa_switch *ds, const struct net_device *bdev) 1965186623f4SDavid Yang { 1966186623f4SDavid Yang struct dsa_port *dp; 1967186623f4SDavid Yang u32 mask = 0; 1968186623f4SDavid Yang 1969186623f4SDavid Yang dsa_switch_for_each_user_port(dp, ds) 1970186623f4SDavid Yang if (dsa_port_offloads_bridge_dev(dp, bdev)) 1971186623f4SDavid Yang mask |= BIT(dp->index); 1972186623f4SDavid Yang 1973186623f4SDavid Yang return mask; 1974186623f4SDavid Yang } 1975186623f4SDavid Yang 1976186623f4SDavid Yang static int 1977186623f4SDavid Yang yt921x_bridge_flags(struct yt921x_priv *priv, int port, 1978186623f4SDavid Yang struct switchdev_brport_flags flags) 1979186623f4SDavid Yang { 1980186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[port]; 1981186623f4SDavid Yang bool do_flush; 1982186623f4SDavid Yang u32 mask; 1983186623f4SDavid Yang int res; 1984186623f4SDavid Yang 1985186623f4SDavid Yang if (flags.mask & BR_LEARNING) { 1986186623f4SDavid Yang bool learning = flags.val & BR_LEARNING; 1987186623f4SDavid Yang 1988186623f4SDavid Yang mask = YT921X_PORT_LEARN_DIS; 1989186623f4SDavid Yang res = yt921x_reg_toggle_bits(priv, YT921X_PORTn_LEARN(port), 1990186623f4SDavid Yang mask, !learning); 1991186623f4SDavid Yang if (res) 1992186623f4SDavid Yang return res; 1993186623f4SDavid Yang } 1994186623f4SDavid Yang 1995186623f4SDavid Yang /* BR_FLOOD, BR_MCAST_FLOOD: see the comment where ACT_UNK_ACTn_TRAP 1996186623f4SDavid Yang * is set 1997186623f4SDavid Yang */ 1998186623f4SDavid Yang 1999186623f4SDavid Yang /* BR_BCAST_FLOOD: we can filter bcast, but cannot trap them */ 2000186623f4SDavid Yang 2001186623f4SDavid Yang do_flush = false; 2002186623f4SDavid Yang if (flags.mask & BR_HAIRPIN_MODE) { 2003186623f4SDavid Yang pp->hairpin = flags.val & BR_HAIRPIN_MODE; 2004186623f4SDavid Yang do_flush = true; 2005186623f4SDavid Yang } 2006186623f4SDavid Yang if (flags.mask & BR_ISOLATED) { 2007186623f4SDavid Yang pp->isolated = flags.val & BR_ISOLATED; 2008186623f4SDavid Yang do_flush = true; 2009186623f4SDavid Yang } 2010186623f4SDavid Yang if (do_flush) { 2011186623f4SDavid Yang struct dsa_switch *ds = &priv->ds; 2012186623f4SDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 2013186623f4SDavid Yang struct net_device *bdev; 2014186623f4SDavid Yang 2015186623f4SDavid Yang bdev = dsa_port_bridge_dev_get(dp); 2016186623f4SDavid Yang if (bdev) { 2017186623f4SDavid Yang u32 ports_mask; 2018186623f4SDavid Yang 2019186623f4SDavid Yang ports_mask = dsa_bridge_ports(ds, bdev); 2020186623f4SDavid Yang ports_mask |= priv->cpu_ports_mask; 2021186623f4SDavid Yang res = yt921x_bridge(priv, ports_mask); 2022186623f4SDavid Yang if (res) 2023186623f4SDavid Yang return res; 2024186623f4SDavid Yang } 2025186623f4SDavid Yang } 2026186623f4SDavid Yang 2027186623f4SDavid Yang return 0; 2028186623f4SDavid Yang } 2029186623f4SDavid Yang 2030186623f4SDavid Yang static int 2031186623f4SDavid Yang yt921x_dsa_port_pre_bridge_flags(struct dsa_switch *ds, int port, 2032186623f4SDavid Yang struct switchdev_brport_flags flags, 2033186623f4SDavid Yang struct netlink_ext_ack *extack) 2034186623f4SDavid Yang { 2035186623f4SDavid Yang if (flags.mask & ~(BR_HAIRPIN_MODE | BR_LEARNING | BR_FLOOD | 2036186623f4SDavid Yang BR_MCAST_FLOOD | BR_ISOLATED)) 2037186623f4SDavid Yang return -EINVAL; 2038186623f4SDavid Yang return 0; 2039186623f4SDavid Yang } 2040186623f4SDavid Yang 2041186623f4SDavid Yang static int 2042186623f4SDavid Yang yt921x_dsa_port_bridge_flags(struct dsa_switch *ds, int port, 2043186623f4SDavid Yang struct switchdev_brport_flags flags, 2044186623f4SDavid Yang struct netlink_ext_ack *extack) 2045186623f4SDavid Yang { 2046186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2047186623f4SDavid Yang int res; 2048186623f4SDavid Yang 2049186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 2050186623f4SDavid Yang return 0; 2051186623f4SDavid Yang 2052186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2053186623f4SDavid Yang res = yt921x_bridge_flags(priv, port, flags); 2054186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2055186623f4SDavid Yang 2056186623f4SDavid Yang return res; 2057186623f4SDavid Yang } 2058186623f4SDavid Yang 2059186623f4SDavid Yang static void 2060186623f4SDavid Yang yt921x_dsa_port_bridge_leave(struct dsa_switch *ds, int port, 2061186623f4SDavid Yang struct dsa_bridge bridge) 2062186623f4SDavid Yang { 2063186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2064186623f4SDavid Yang struct device *dev = to_device(priv); 2065186623f4SDavid Yang int res; 2066186623f4SDavid Yang 2067186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 2068186623f4SDavid Yang return; 2069186623f4SDavid Yang 2070186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2071186623f4SDavid Yang res = yt921x_bridge_leave(priv, port); 2072186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2073186623f4SDavid Yang 2074186623f4SDavid Yang if (res) 2075186623f4SDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "unbridge", 2076186623f4SDavid Yang port, res); 2077186623f4SDavid Yang } 2078186623f4SDavid Yang 2079186623f4SDavid Yang static int 2080186623f4SDavid Yang yt921x_dsa_port_bridge_join(struct dsa_switch *ds, int port, 2081186623f4SDavid Yang struct dsa_bridge bridge, bool *tx_fwd_offload, 2082186623f4SDavid Yang struct netlink_ext_ack *extack) 2083186623f4SDavid Yang { 2084186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2085186623f4SDavid Yang u16 ports_mask; 2086186623f4SDavid Yang int res; 2087186623f4SDavid Yang 2088186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) 2089186623f4SDavid Yang return 0; 2090186623f4SDavid Yang 2091186623f4SDavid Yang ports_mask = dsa_bridge_ports(ds, bridge.dev); 2092186623f4SDavid Yang ports_mask |= priv->cpu_ports_mask; 2093186623f4SDavid Yang 2094186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2095186623f4SDavid Yang res = yt921x_bridge_join(priv, port, ports_mask); 2096186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2097186623f4SDavid Yang 2098186623f4SDavid Yang return res; 2099186623f4SDavid Yang } 2100186623f4SDavid Yang 2101633b1d01SDavid Yang static int 2102633b1d01SDavid Yang yt921x_dsa_port_mst_state_set(struct dsa_switch *ds, int port, 2103633b1d01SDavid Yang const struct switchdev_mst_state *st) 2104633b1d01SDavid Yang { 2105633b1d01SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2106633b1d01SDavid Yang u32 mask; 2107633b1d01SDavid Yang u32 ctrl; 2108633b1d01SDavid Yang int res; 2109633b1d01SDavid Yang 2110633b1d01SDavid Yang mask = YT921X_STP_PORTn_M(port); 2111633b1d01SDavid Yang switch (st->state) { 2112633b1d01SDavid Yang case BR_STATE_DISABLED: 2113633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_DISABLED(port); 2114633b1d01SDavid Yang break; 2115633b1d01SDavid Yang case BR_STATE_LISTENING: 2116633b1d01SDavid Yang case BR_STATE_LEARNING: 2117633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 2118633b1d01SDavid Yang break; 2119633b1d01SDavid Yang case BR_STATE_FORWARDING: 2120633b1d01SDavid Yang default: 2121633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_FORWARD(port); 2122633b1d01SDavid Yang break; 2123633b1d01SDavid Yang case BR_STATE_BLOCKING: 2124633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_BLOCKING(port); 2125633b1d01SDavid Yang break; 2126633b1d01SDavid Yang } 2127633b1d01SDavid Yang 2128633b1d01SDavid Yang mutex_lock(&priv->reg_lock); 2129633b1d01SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_STPn(st->msti), mask, ctrl); 2130633b1d01SDavid Yang mutex_unlock(&priv->reg_lock); 2131633b1d01SDavid Yang 2132633b1d01SDavid Yang return res; 2133633b1d01SDavid Yang } 2134633b1d01SDavid Yang 2135633b1d01SDavid Yang static int 2136633b1d01SDavid Yang yt921x_dsa_vlan_msti_set(struct dsa_switch *ds, struct dsa_bridge bridge, 2137633b1d01SDavid Yang const struct switchdev_vlan_msti *msti) 2138633b1d01SDavid Yang { 2139633b1d01SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2140633b1d01SDavid Yang u64 mask64; 2141633b1d01SDavid Yang u64 ctrl64; 2142633b1d01SDavid Yang int res; 2143633b1d01SDavid Yang 2144633b1d01SDavid Yang if (!msti->vid) 2145633b1d01SDavid Yang return -EINVAL; 2146633b1d01SDavid Yang if (!msti->msti || msti->msti >= YT921X_MSTI_NUM) 2147633b1d01SDavid Yang return -EINVAL; 2148633b1d01SDavid Yang 2149633b1d01SDavid Yang mask64 = YT921X_VLAN_CTRL_STP_ID_M; 2150633b1d01SDavid Yang ctrl64 = YT921X_VLAN_CTRL_STP_ID(msti->msti); 2151633b1d01SDavid Yang 2152633b1d01SDavid Yang mutex_lock(&priv->reg_lock); 2153633b1d01SDavid Yang res = yt921x_reg64_update_bits(priv, YT921X_VLANn_CTRL(msti->vid), 2154633b1d01SDavid Yang mask64, ctrl64); 2155633b1d01SDavid Yang mutex_unlock(&priv->reg_lock); 2156633b1d01SDavid Yang 2157633b1d01SDavid Yang return res; 2158633b1d01SDavid Yang } 2159633b1d01SDavid Yang 2160633b1d01SDavid Yang static void 2161633b1d01SDavid Yang yt921x_dsa_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) 2162633b1d01SDavid Yang { 2163633b1d01SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2164633b1d01SDavid Yang struct dsa_port *dp = dsa_to_port(ds, port); 2165633b1d01SDavid Yang struct device *dev = to_device(priv); 2166633b1d01SDavid Yang bool learning; 2167633b1d01SDavid Yang u32 mask; 2168633b1d01SDavid Yang u32 ctrl; 2169633b1d01SDavid Yang int res; 2170633b1d01SDavid Yang 2171633b1d01SDavid Yang mask = YT921X_STP_PORTn_M(port); 2172633b1d01SDavid Yang learning = false; 2173633b1d01SDavid Yang switch (state) { 2174633b1d01SDavid Yang case BR_STATE_DISABLED: 2175633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_DISABLED(port); 2176633b1d01SDavid Yang break; 2177633b1d01SDavid Yang case BR_STATE_LISTENING: 2178633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 2179633b1d01SDavid Yang break; 2180633b1d01SDavid Yang case BR_STATE_LEARNING: 2181633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_LEARNING(port); 2182633b1d01SDavid Yang learning = dp->learning; 2183633b1d01SDavid Yang break; 2184633b1d01SDavid Yang case BR_STATE_FORWARDING: 2185633b1d01SDavid Yang default: 2186633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_FORWARD(port); 2187633b1d01SDavid Yang learning = dp->learning; 2188633b1d01SDavid Yang break; 2189633b1d01SDavid Yang case BR_STATE_BLOCKING: 2190633b1d01SDavid Yang ctrl = YT921X_STP_PORTn_BLOCKING(port); 2191633b1d01SDavid Yang break; 2192633b1d01SDavid Yang } 2193633b1d01SDavid Yang 2194633b1d01SDavid Yang mutex_lock(&priv->reg_lock); 2195633b1d01SDavid Yang do { 2196633b1d01SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_STPn(0), mask, ctrl); 2197633b1d01SDavid Yang if (res) 2198633b1d01SDavid Yang break; 2199633b1d01SDavid Yang 2200633b1d01SDavid Yang mask = YT921X_PORT_LEARN_DIS; 2201633b1d01SDavid Yang ctrl = !learning ? YT921X_PORT_LEARN_DIS : 0; 2202633b1d01SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_PORTn_LEARN(port), 2203633b1d01SDavid Yang mask, ctrl); 2204633b1d01SDavid Yang } while (0); 2205633b1d01SDavid Yang mutex_unlock(&priv->reg_lock); 2206633b1d01SDavid Yang 2207633b1d01SDavid Yang if (res) 2208633b1d01SDavid Yang dev_err(dev, "Failed to %s port %d: %i\n", "set STP state for", 2209633b1d01SDavid Yang port, res); 2210633b1d01SDavid Yang } 2211633b1d01SDavid Yang 2212186623f4SDavid Yang static int yt921x_port_down(struct yt921x_priv *priv, int port) 2213186623f4SDavid Yang { 2214186623f4SDavid Yang u32 mask; 2215186623f4SDavid Yang int res; 2216186623f4SDavid Yang 2217186623f4SDavid Yang mask = YT921X_PORT_LINK | YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; 2218186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_PORTn_CTRL(port), mask); 2219186623f4SDavid Yang if (res) 2220186623f4SDavid Yang return res; 2221186623f4SDavid Yang 2222186623f4SDavid Yang if (yt921x_port_is_external(port)) { 2223186623f4SDavid Yang mask = YT921X_SERDES_LINK; 2224186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_SERDESn(port), mask); 2225186623f4SDavid Yang if (res) 2226186623f4SDavid Yang return res; 2227186623f4SDavid Yang 2228186623f4SDavid Yang mask = YT921X_XMII_LINK; 2229186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_XMIIn(port), mask); 2230186623f4SDavid Yang if (res) 2231186623f4SDavid Yang return res; 2232186623f4SDavid Yang } 2233186623f4SDavid Yang 2234186623f4SDavid Yang return 0; 2235186623f4SDavid Yang } 2236186623f4SDavid Yang 2237186623f4SDavid Yang static int 2238186623f4SDavid Yang yt921x_port_up(struct yt921x_priv *priv, int port, unsigned int mode, 2239186623f4SDavid Yang phy_interface_t interface, int speed, int duplex, 2240186623f4SDavid Yang bool tx_pause, bool rx_pause) 2241186623f4SDavid Yang { 2242186623f4SDavid Yang u32 mask; 2243186623f4SDavid Yang u32 ctrl; 2244186623f4SDavid Yang int res; 2245186623f4SDavid Yang 2246186623f4SDavid Yang switch (speed) { 2247186623f4SDavid Yang case SPEED_10: 2248186623f4SDavid Yang ctrl = YT921X_PORT_SPEED_10; 2249186623f4SDavid Yang break; 2250186623f4SDavid Yang case SPEED_100: 2251186623f4SDavid Yang ctrl = YT921X_PORT_SPEED_100; 2252186623f4SDavid Yang break; 2253186623f4SDavid Yang case SPEED_1000: 2254186623f4SDavid Yang ctrl = YT921X_PORT_SPEED_1000; 2255186623f4SDavid Yang break; 2256186623f4SDavid Yang case SPEED_2500: 2257186623f4SDavid Yang ctrl = YT921X_PORT_SPEED_2500; 2258186623f4SDavid Yang break; 2259186623f4SDavid Yang case SPEED_10000: 2260186623f4SDavid Yang ctrl = YT921X_PORT_SPEED_10000; 2261186623f4SDavid Yang break; 2262186623f4SDavid Yang default: 2263186623f4SDavid Yang return -EINVAL; 2264186623f4SDavid Yang } 2265186623f4SDavid Yang if (duplex == DUPLEX_FULL) 2266186623f4SDavid Yang ctrl |= YT921X_PORT_DUPLEX_FULL; 2267186623f4SDavid Yang if (tx_pause) 2268186623f4SDavid Yang ctrl |= YT921X_PORT_TX_PAUSE; 2269186623f4SDavid Yang if (rx_pause) 2270186623f4SDavid Yang ctrl |= YT921X_PORT_RX_PAUSE; 2271186623f4SDavid Yang ctrl |= YT921X_PORT_RX_MAC_EN | YT921X_PORT_TX_MAC_EN; 2272186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_CTRL(port), ctrl); 2273186623f4SDavid Yang if (res) 2274186623f4SDavid Yang return res; 2275186623f4SDavid Yang 2276186623f4SDavid Yang if (yt921x_port_is_external(port)) { 2277186623f4SDavid Yang mask = YT921X_SERDES_SPEED_M; 2278186623f4SDavid Yang switch (speed) { 2279186623f4SDavid Yang case SPEED_10: 2280186623f4SDavid Yang ctrl = YT921X_SERDES_SPEED_10; 2281186623f4SDavid Yang break; 2282186623f4SDavid Yang case SPEED_100: 2283186623f4SDavid Yang ctrl = YT921X_SERDES_SPEED_100; 2284186623f4SDavid Yang break; 2285186623f4SDavid Yang case SPEED_1000: 2286186623f4SDavid Yang ctrl = YT921X_SERDES_SPEED_1000; 2287186623f4SDavid Yang break; 2288186623f4SDavid Yang case SPEED_2500: 2289186623f4SDavid Yang ctrl = YT921X_SERDES_SPEED_2500; 2290186623f4SDavid Yang break; 2291186623f4SDavid Yang case SPEED_10000: 2292186623f4SDavid Yang ctrl = YT921X_SERDES_SPEED_10000; 2293186623f4SDavid Yang break; 2294186623f4SDavid Yang default: 2295186623f4SDavid Yang return -EINVAL; 2296186623f4SDavid Yang } 2297186623f4SDavid Yang mask |= YT921X_SERDES_DUPLEX_FULL; 2298186623f4SDavid Yang if (duplex == DUPLEX_FULL) 2299186623f4SDavid Yang ctrl |= YT921X_SERDES_DUPLEX_FULL; 2300186623f4SDavid Yang mask |= YT921X_SERDES_TX_PAUSE; 2301186623f4SDavid Yang if (tx_pause) 2302186623f4SDavid Yang ctrl |= YT921X_SERDES_TX_PAUSE; 2303186623f4SDavid Yang mask |= YT921X_SERDES_RX_PAUSE; 2304186623f4SDavid Yang if (rx_pause) 2305186623f4SDavid Yang ctrl |= YT921X_SERDES_RX_PAUSE; 2306186623f4SDavid Yang mask |= YT921X_SERDES_LINK; 2307186623f4SDavid Yang ctrl |= YT921X_SERDES_LINK; 2308186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), 2309186623f4SDavid Yang mask, ctrl); 2310186623f4SDavid Yang if (res) 2311186623f4SDavid Yang return res; 2312186623f4SDavid Yang 2313186623f4SDavid Yang mask = YT921X_XMII_LINK; 2314186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_XMIIn(port), mask); 2315186623f4SDavid Yang if (res) 2316186623f4SDavid Yang return res; 2317186623f4SDavid Yang 2318186623f4SDavid Yang switch (speed) { 2319186623f4SDavid Yang case SPEED_10: 2320186623f4SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED_10; 2321186623f4SDavid Yang break; 2322186623f4SDavid Yang case SPEED_100: 2323186623f4SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED_100; 2324186623f4SDavid Yang break; 2325186623f4SDavid Yang case SPEED_1000: 2326186623f4SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED_1000; 2327186623f4SDavid Yang break; 2328186623f4SDavid Yang case SPEED_2500: 2329186623f4SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED_2500; 2330186623f4SDavid Yang break; 2331186623f4SDavid Yang case SPEED_10000: 2332186623f4SDavid Yang ctrl = YT921X_MDIO_POLLING_SPEED_10000; 2333186623f4SDavid Yang break; 2334186623f4SDavid Yang default: 2335186623f4SDavid Yang return -EINVAL; 2336186623f4SDavid Yang } 2337186623f4SDavid Yang if (duplex == DUPLEX_FULL) 2338186623f4SDavid Yang ctrl |= YT921X_MDIO_POLLING_DUPLEX_FULL; 2339186623f4SDavid Yang ctrl |= YT921X_MDIO_POLLING_LINK; 2340186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_MDIO_POLLINGn(port), ctrl); 2341186623f4SDavid Yang if (res) 2342186623f4SDavid Yang return res; 2343186623f4SDavid Yang } 2344186623f4SDavid Yang 2345186623f4SDavid Yang return 0; 2346186623f4SDavid Yang } 2347186623f4SDavid Yang 2348186623f4SDavid Yang static int 2349186623f4SDavid Yang yt921x_port_config(struct yt921x_priv *priv, int port, unsigned int mode, 2350186623f4SDavid Yang phy_interface_t interface) 2351186623f4SDavid Yang { 2352186623f4SDavid Yang struct device *dev = to_device(priv); 2353186623f4SDavid Yang u32 mask; 2354186623f4SDavid Yang u32 ctrl; 2355186623f4SDavid Yang int res; 2356186623f4SDavid Yang 2357186623f4SDavid Yang if (!yt921x_port_is_external(port)) { 2358186623f4SDavid Yang if (interface != PHY_INTERFACE_MODE_INTERNAL) { 2359186623f4SDavid Yang dev_err(dev, "Wrong mode %d on port %d\n", 2360186623f4SDavid Yang interface, port); 2361186623f4SDavid Yang return -EINVAL; 2362186623f4SDavid Yang } 2363186623f4SDavid Yang return 0; 2364186623f4SDavid Yang } 2365186623f4SDavid Yang 2366186623f4SDavid Yang switch (interface) { 2367186623f4SDavid Yang /* SERDES */ 2368186623f4SDavid Yang case PHY_INTERFACE_MODE_SGMII: 2369186623f4SDavid Yang case PHY_INTERFACE_MODE_100BASEX: 2370186623f4SDavid Yang case PHY_INTERFACE_MODE_1000BASEX: 2371186623f4SDavid Yang case PHY_INTERFACE_MODE_2500BASEX: 2372186623f4SDavid Yang mask = YT921X_SERDES_CTRL_PORTn(port); 2373186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_SERDES_CTRL, mask); 2374186623f4SDavid Yang if (res) 2375186623f4SDavid Yang return res; 2376186623f4SDavid Yang 2377186623f4SDavid Yang mask = YT921X_XMII_CTRL_PORTn(port); 2378186623f4SDavid Yang res = yt921x_reg_clear_bits(priv, YT921X_XMII_CTRL, mask); 2379186623f4SDavid Yang if (res) 2380186623f4SDavid Yang return res; 2381186623f4SDavid Yang 2382186623f4SDavid Yang mask = YT921X_SERDES_MODE_M; 2383186623f4SDavid Yang switch (interface) { 2384186623f4SDavid Yang case PHY_INTERFACE_MODE_SGMII: 2385186623f4SDavid Yang ctrl = YT921X_SERDES_MODE_SGMII; 2386186623f4SDavid Yang break; 2387186623f4SDavid Yang case PHY_INTERFACE_MODE_100BASEX: 2388186623f4SDavid Yang ctrl = YT921X_SERDES_MODE_100BASEX; 2389186623f4SDavid Yang break; 2390186623f4SDavid Yang case PHY_INTERFACE_MODE_1000BASEX: 2391186623f4SDavid Yang ctrl = YT921X_SERDES_MODE_1000BASEX; 2392186623f4SDavid Yang break; 2393186623f4SDavid Yang case PHY_INTERFACE_MODE_2500BASEX: 2394186623f4SDavid Yang ctrl = YT921X_SERDES_MODE_2500BASEX; 2395186623f4SDavid Yang break; 2396186623f4SDavid Yang default: 2397186623f4SDavid Yang return -EINVAL; 2398186623f4SDavid Yang } 2399186623f4SDavid Yang res = yt921x_reg_update_bits(priv, YT921X_SERDESn(port), 2400186623f4SDavid Yang mask, ctrl); 2401186623f4SDavid Yang if (res) 2402186623f4SDavid Yang return res; 2403186623f4SDavid Yang 2404186623f4SDavid Yang break; 2405186623f4SDavid Yang /* add XMII support here */ 2406186623f4SDavid Yang default: 2407186623f4SDavid Yang return -EINVAL; 2408186623f4SDavid Yang } 2409186623f4SDavid Yang 2410186623f4SDavid Yang return 0; 2411186623f4SDavid Yang } 2412186623f4SDavid Yang 2413186623f4SDavid Yang static void 2414186623f4SDavid Yang yt921x_phylink_mac_link_down(struct phylink_config *config, unsigned int mode, 2415186623f4SDavid Yang phy_interface_t interface) 2416186623f4SDavid Yang { 2417186623f4SDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 2418186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(dp->ds); 2419186623f4SDavid Yang int port = dp->index; 2420186623f4SDavid Yang int res; 2421186623f4SDavid Yang 2422186623f4SDavid Yang /* No need to sync; port control block is hold until device remove */ 2423186623f4SDavid Yang cancel_delayed_work(&priv->ports[port].mib_read); 2424186623f4SDavid Yang 2425186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2426186623f4SDavid Yang res = yt921x_port_down(priv, port); 2427186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2428186623f4SDavid Yang 2429186623f4SDavid Yang if (res) 2430186623f4SDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring down", 2431186623f4SDavid Yang port, res); 2432186623f4SDavid Yang } 2433186623f4SDavid Yang 2434186623f4SDavid Yang static void 2435186623f4SDavid Yang yt921x_phylink_mac_link_up(struct phylink_config *config, 2436186623f4SDavid Yang struct phy_device *phydev, unsigned int mode, 2437186623f4SDavid Yang phy_interface_t interface, int speed, int duplex, 2438186623f4SDavid Yang bool tx_pause, bool rx_pause) 2439186623f4SDavid Yang { 2440186623f4SDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 2441186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(dp->ds); 2442186623f4SDavid Yang int port = dp->index; 2443186623f4SDavid Yang int res; 2444186623f4SDavid Yang 2445186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2446186623f4SDavid Yang res = yt921x_port_up(priv, port, mode, interface, speed, duplex, 2447186623f4SDavid Yang tx_pause, rx_pause); 2448186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2449186623f4SDavid Yang 2450186623f4SDavid Yang if (res) 2451186623f4SDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "bring up", 2452186623f4SDavid Yang port, res); 2453186623f4SDavid Yang 2454186623f4SDavid Yang schedule_delayed_work(&priv->ports[port].mib_read, 0); 2455186623f4SDavid Yang } 2456186623f4SDavid Yang 2457186623f4SDavid Yang static void 2458186623f4SDavid Yang yt921x_phylink_mac_config(struct phylink_config *config, unsigned int mode, 2459186623f4SDavid Yang const struct phylink_link_state *state) 2460186623f4SDavid Yang { 2461186623f4SDavid Yang struct dsa_port *dp = dsa_phylink_to_port(config); 2462186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(dp->ds); 2463186623f4SDavid Yang int port = dp->index; 2464186623f4SDavid Yang int res; 2465186623f4SDavid Yang 2466186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2467186623f4SDavid Yang res = yt921x_port_config(priv, port, mode, state->interface); 2468186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2469186623f4SDavid Yang 2470186623f4SDavid Yang if (res) 2471186623f4SDavid Yang dev_err(dp->ds->dev, "Failed to %s port %d: %i\n", "config", 2472186623f4SDavid Yang port, res); 2473186623f4SDavid Yang } 2474186623f4SDavid Yang 2475186623f4SDavid Yang static void 2476186623f4SDavid Yang yt921x_dsa_phylink_get_caps(struct dsa_switch *ds, int port, 2477186623f4SDavid Yang struct phylink_config *config) 2478186623f4SDavid Yang { 2479186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2480186623f4SDavid Yang const struct yt921x_info *info = priv->info; 2481186623f4SDavid Yang 2482186623f4SDavid Yang config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 2483186623f4SDavid Yang MAC_10 | MAC_100 | MAC_1000; 2484186623f4SDavid Yang 2485186623f4SDavid Yang if (info->internal_mask & BIT(port)) { 2486186623f4SDavid Yang /* Port 10 for MCU should probably go here too. But since that 2487186623f4SDavid Yang * is untested yet, turn it down for the moment by letting it 2488186623f4SDavid Yang * fall to the default branch. 2489186623f4SDavid Yang */ 2490186623f4SDavid Yang __set_bit(PHY_INTERFACE_MODE_INTERNAL, 2491186623f4SDavid Yang config->supported_interfaces); 2492186623f4SDavid Yang } else if (info->external_mask & BIT(port)) { 2493186623f4SDavid Yang /* TODO: external ports may support SERDES only, XMII only, or 2494186623f4SDavid Yang * SERDES + XMII depending on the chip. However, we can't get 2495186623f4SDavid Yang * the accurate config table due to lack of document, thus 2496186623f4SDavid Yang * we simply declare SERDES + XMII and rely on the correctness 2497186623f4SDavid Yang * of devicetree for now. 2498186623f4SDavid Yang */ 2499186623f4SDavid Yang 2500186623f4SDavid Yang /* SERDES */ 2501186623f4SDavid Yang __set_bit(PHY_INTERFACE_MODE_SGMII, 2502186623f4SDavid Yang config->supported_interfaces); 2503186623f4SDavid Yang /* REVSGMII (SGMII in PHY role) should go here, once 2504186623f4SDavid Yang * PHY_INTERFACE_MODE_REVSGMII is introduced. 2505186623f4SDavid Yang */ 2506186623f4SDavid Yang __set_bit(PHY_INTERFACE_MODE_100BASEX, 2507186623f4SDavid Yang config->supported_interfaces); 2508186623f4SDavid Yang __set_bit(PHY_INTERFACE_MODE_1000BASEX, 2509186623f4SDavid Yang config->supported_interfaces); 2510186623f4SDavid Yang __set_bit(PHY_INTERFACE_MODE_2500BASEX, 2511186623f4SDavid Yang config->supported_interfaces); 2512186623f4SDavid Yang config->mac_capabilities |= MAC_2500FD; 2513186623f4SDavid Yang 2514186623f4SDavid Yang /* XMII */ 2515186623f4SDavid Yang 2516186623f4SDavid Yang /* Not tested. To add support for XMII: 2517186623f4SDavid Yang * - Add proper interface modes below 2518186623f4SDavid Yang * - Handle them in yt921x_port_config() 2519186623f4SDavid Yang */ 2520186623f4SDavid Yang } 2521186623f4SDavid Yang /* no such port: empty supported_interfaces causes phylink to turn it 2522186623f4SDavid Yang * down 2523186623f4SDavid Yang */ 2524186623f4SDavid Yang } 2525186623f4SDavid Yang 2526186623f4SDavid Yang static int yt921x_port_setup(struct yt921x_priv *priv, int port) 2527186623f4SDavid Yang { 2528186623f4SDavid Yang struct dsa_switch *ds = &priv->ds; 2529186623f4SDavid Yang u32 ctrl; 2530186623f4SDavid Yang int res; 2531186623f4SDavid Yang 2532186623f4SDavid Yang res = yt921x_userport_standalone(priv, port); 2533186623f4SDavid Yang if (res) 2534186623f4SDavid Yang return res; 2535186623f4SDavid Yang 2536186623f4SDavid Yang if (dsa_is_cpu_port(ds, port)) { 2537186623f4SDavid Yang /* Egress of CPU port is supposed to be completely controlled 2538186623f4SDavid Yang * via tagging, so set to oneway isolated (drop all packets 2539186623f4SDavid Yang * without tag). 2540186623f4SDavid Yang */ 2541186623f4SDavid Yang ctrl = ~(u32)0; 2542186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_PORTn_ISOLATION(port), 2543186623f4SDavid Yang ctrl); 2544186623f4SDavid Yang if (res) 2545186623f4SDavid Yang return res; 2546186623f4SDavid Yang 2547186623f4SDavid Yang /* To simplify FDB "isolation" simulation, we also disable 2548186623f4SDavid Yang * learning on the CPU port, and let software identify packets 2549186623f4SDavid Yang * towarding CPU (either trapped or a static FDB entry is 2550186623f4SDavid Yang * matched, no matter which bridge that entry is for), which is 2551186623f4SDavid Yang * already done by yt921x_userport_standalone(). As a result, 2552186623f4SDavid Yang * VLAN-awareness becomes unrelated on the CPU port (set to 2553186623f4SDavid Yang * VLAN-unaware by the way). 2554186623f4SDavid Yang */ 2555186623f4SDavid Yang } 2556186623f4SDavid Yang 2557186623f4SDavid Yang return 0; 2558186623f4SDavid Yang } 2559186623f4SDavid Yang 2560186623f4SDavid Yang static enum dsa_tag_protocol 2561186623f4SDavid Yang yt921x_dsa_get_tag_protocol(struct dsa_switch *ds, int port, 2562186623f4SDavid Yang enum dsa_tag_protocol m) 2563186623f4SDavid Yang { 2564186623f4SDavid Yang return DSA_TAG_PROTO_YT921X; 2565186623f4SDavid Yang } 2566186623f4SDavid Yang 2567186623f4SDavid Yang static int yt921x_dsa_port_setup(struct dsa_switch *ds, int port) 2568186623f4SDavid Yang { 2569186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2570186623f4SDavid Yang int res; 2571186623f4SDavid Yang 2572186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2573186623f4SDavid Yang res = yt921x_port_setup(priv, port); 2574186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2575186623f4SDavid Yang 2576186623f4SDavid Yang return res; 2577186623f4SDavid Yang } 2578186623f4SDavid Yang 2579186623f4SDavid Yang static int yt921x_edata_wait(struct yt921x_priv *priv, u32 *valp) 2580186623f4SDavid Yang { 2581186623f4SDavid Yang u32 val = YT921X_EDATA_DATA_IDLE; 2582186623f4SDavid Yang int res; 2583186623f4SDavid Yang 2584186623f4SDavid Yang res = yt921x_reg_wait(priv, YT921X_EDATA_DATA, 2585186623f4SDavid Yang YT921X_EDATA_DATA_STATUS_M, &val); 2586186623f4SDavid Yang if (res) 2587186623f4SDavid Yang return res; 2588186623f4SDavid Yang 2589186623f4SDavid Yang *valp = val; 2590186623f4SDavid Yang return 0; 2591186623f4SDavid Yang } 2592186623f4SDavid Yang 2593186623f4SDavid Yang static int 2594186623f4SDavid Yang yt921x_edata_read_cont(struct yt921x_priv *priv, u8 addr, u8 *valp) 2595186623f4SDavid Yang { 2596186623f4SDavid Yang u32 ctrl; 2597186623f4SDavid Yang u32 val; 2598186623f4SDavid Yang int res; 2599186623f4SDavid Yang 2600186623f4SDavid Yang ctrl = YT921X_EDATA_CTRL_ADDR(addr) | YT921X_EDATA_CTRL_READ; 2601186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_EDATA_CTRL, ctrl); 2602186623f4SDavid Yang if (res) 2603186623f4SDavid Yang return res; 2604186623f4SDavid Yang res = yt921x_edata_wait(priv, &val); 2605186623f4SDavid Yang if (res) 2606186623f4SDavid Yang return res; 2607186623f4SDavid Yang 2608186623f4SDavid Yang *valp = FIELD_GET(YT921X_EDATA_DATA_DATA_M, val); 2609186623f4SDavid Yang return 0; 2610186623f4SDavid Yang } 2611186623f4SDavid Yang 2612186623f4SDavid Yang static int yt921x_edata_read(struct yt921x_priv *priv, u8 addr, u8 *valp) 2613186623f4SDavid Yang { 2614186623f4SDavid Yang u32 val; 2615186623f4SDavid Yang int res; 2616186623f4SDavid Yang 2617186623f4SDavid Yang res = yt921x_edata_wait(priv, &val); 2618186623f4SDavid Yang if (res) 2619186623f4SDavid Yang return res; 2620186623f4SDavid Yang return yt921x_edata_read_cont(priv, addr, valp); 2621186623f4SDavid Yang } 2622186623f4SDavid Yang 2623186623f4SDavid Yang static int yt921x_chip_detect(struct yt921x_priv *priv) 2624186623f4SDavid Yang { 2625186623f4SDavid Yang struct device *dev = to_device(priv); 2626186623f4SDavid Yang const struct yt921x_info *info; 2627186623f4SDavid Yang u8 extmode; 2628186623f4SDavid Yang u32 chipid; 2629186623f4SDavid Yang u32 major; 2630186623f4SDavid Yang u32 mode; 2631186623f4SDavid Yang int res; 2632186623f4SDavid Yang 2633186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_CHIP_ID, &chipid); 2634186623f4SDavid Yang if (res) 2635186623f4SDavid Yang return res; 2636186623f4SDavid Yang 2637186623f4SDavid Yang major = FIELD_GET(YT921X_CHIP_ID_MAJOR, chipid); 2638186623f4SDavid Yang 2639186623f4SDavid Yang for (info = yt921x_infos; info->name; info++) 2640186623f4SDavid Yang if (info->major == major) 2641186623f4SDavid Yang break; 2642186623f4SDavid Yang if (!info->name) { 2643186623f4SDavid Yang dev_err(dev, "Unexpected chipid 0x%x\n", chipid); 2644186623f4SDavid Yang return -ENODEV; 2645186623f4SDavid Yang } 2646186623f4SDavid Yang 2647186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_CHIP_MODE, &mode); 2648186623f4SDavid Yang if (res) 2649186623f4SDavid Yang return res; 2650186623f4SDavid Yang res = yt921x_edata_read(priv, YT921X_EDATA_EXTMODE, &extmode); 2651186623f4SDavid Yang if (res) 2652186623f4SDavid Yang return res; 2653186623f4SDavid Yang 2654186623f4SDavid Yang for (; info->name; info++) 2655186623f4SDavid Yang if (info->major == major && info->mode == mode && 2656186623f4SDavid Yang info->extmode == extmode) 2657186623f4SDavid Yang break; 2658186623f4SDavid Yang if (!info->name) { 2659186623f4SDavid Yang dev_err(dev, 2660186623f4SDavid Yang "Unsupported chipid 0x%x with chipmode 0x%x 0x%x\n", 2661186623f4SDavid Yang chipid, mode, extmode); 2662186623f4SDavid Yang return -ENODEV; 2663186623f4SDavid Yang } 2664186623f4SDavid Yang 2665186623f4SDavid Yang /* Print chipid here since we are interested in lower 16 bits */ 2666186623f4SDavid Yang dev_info(dev, 2667186623f4SDavid Yang "Motorcomm %s ethernet switch, chipid: 0x%x, chipmode: 0x%x 0x%x\n", 2668186623f4SDavid Yang info->name, chipid, mode, extmode); 2669186623f4SDavid Yang 2670186623f4SDavid Yang priv->info = info; 2671186623f4SDavid Yang return 0; 2672186623f4SDavid Yang } 2673186623f4SDavid Yang 2674186623f4SDavid Yang static int yt921x_chip_reset(struct yt921x_priv *priv) 2675186623f4SDavid Yang { 2676186623f4SDavid Yang struct device *dev = to_device(priv); 2677186623f4SDavid Yang u16 eth_p_tag; 2678186623f4SDavid Yang u32 val; 2679186623f4SDavid Yang int res; 2680186623f4SDavid Yang 2681186623f4SDavid Yang res = yt921x_chip_detect(priv); 2682186623f4SDavid Yang if (res) 2683186623f4SDavid Yang return res; 2684186623f4SDavid Yang 2685186623f4SDavid Yang /* Reset */ 2686186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_RST, YT921X_RST_HW); 2687186623f4SDavid Yang if (res) 2688186623f4SDavid Yang return res; 2689186623f4SDavid Yang 2690186623f4SDavid Yang /* RST_HW is almost same as GPIO hard reset, so we need this delay. */ 2691186623f4SDavid Yang fsleep(YT921X_RST_DELAY_US); 2692186623f4SDavid Yang 2693186623f4SDavid Yang val = 0; 2694186623f4SDavid Yang res = yt921x_reg_wait(priv, YT921X_RST, ~0, &val); 2695186623f4SDavid Yang if (res) 2696186623f4SDavid Yang return res; 2697186623f4SDavid Yang 2698186623f4SDavid Yang /* Check for tag EtherType; do it after reset in case you messed it up 2699186623f4SDavid Yang * before. 2700186623f4SDavid Yang */ 2701186623f4SDavid Yang res = yt921x_reg_read(priv, YT921X_CPU_TAG_TPID, &val); 2702186623f4SDavid Yang if (res) 2703186623f4SDavid Yang return res; 2704186623f4SDavid Yang eth_p_tag = FIELD_GET(YT921X_CPU_TAG_TPID_TPID_M, val); 2705186623f4SDavid Yang if (eth_p_tag != ETH_P_YT921X) { 2706186623f4SDavid Yang dev_err(dev, "Tag type 0x%x != 0x%x\n", eth_p_tag, 2707186623f4SDavid Yang ETH_P_YT921X); 2708186623f4SDavid Yang /* Despite being possible, we choose not to set CPU_TAG_TPID, 2709186623f4SDavid Yang * since there is no way it can be different unless you have the 2710186623f4SDavid Yang * wrong chip. 2711186623f4SDavid Yang */ 2712186623f4SDavid Yang return -EINVAL; 2713186623f4SDavid Yang } 2714186623f4SDavid Yang 2715186623f4SDavid Yang return 0; 2716186623f4SDavid Yang } 2717186623f4SDavid Yang 2718186623f4SDavid Yang static int yt921x_chip_setup(struct yt921x_priv *priv) 2719186623f4SDavid Yang { 2720186623f4SDavid Yang struct dsa_switch *ds = &priv->ds; 2721186623f4SDavid Yang unsigned long cpu_ports_mask; 2722186623f4SDavid Yang u64 ctrl64; 2723186623f4SDavid Yang u32 ctrl; 2724186623f4SDavid Yang int port; 2725186623f4SDavid Yang int res; 2726186623f4SDavid Yang 2727186623f4SDavid Yang /* Enable DSA */ 2728186623f4SDavid Yang priv->cpu_ports_mask = dsa_cpu_ports(ds); 2729186623f4SDavid Yang 2730186623f4SDavid Yang ctrl = YT921X_EXT_CPU_PORT_TAG_EN | YT921X_EXT_CPU_PORT_PORT_EN | 2731186623f4SDavid Yang YT921X_EXT_CPU_PORT_PORT(__ffs(priv->cpu_ports_mask)); 2732186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_EXT_CPU_PORT, ctrl); 2733186623f4SDavid Yang if (res) 2734186623f4SDavid Yang return res; 2735186623f4SDavid Yang 2736186623f4SDavid Yang /* Enable and clear MIB */ 2737186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_FUNC, YT921X_FUNC_MIB); 2738186623f4SDavid Yang if (res) 2739186623f4SDavid Yang return res; 2740186623f4SDavid Yang 2741186623f4SDavid Yang ctrl = YT921X_MIB_CTRL_CLEAN | YT921X_MIB_CTRL_ALL_PORT; 2742186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_MIB_CTRL, ctrl); 2743186623f4SDavid Yang if (res) 2744186623f4SDavid Yang return res; 2745186623f4SDavid Yang 2746186623f4SDavid Yang /* Setup software switch */ 2747186623f4SDavid Yang ctrl = YT921X_CPU_COPY_TO_EXT_CPU; 2748186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_CPU_COPY, ctrl); 2749186623f4SDavid Yang if (res) 2750186623f4SDavid Yang return res; 2751186623f4SDavid Yang 2752186623f4SDavid Yang ctrl = GENMASK(10, 0); 2753186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FILTER_UNK_UCAST, ctrl); 2754186623f4SDavid Yang if (res) 2755186623f4SDavid Yang return res; 2756186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_FILTER_UNK_MCAST, ctrl); 2757186623f4SDavid Yang if (res) 2758186623f4SDavid Yang return res; 2759186623f4SDavid Yang 2760186623f4SDavid Yang /* YT921x does not support native DSA port bridging, so we use port 2761186623f4SDavid Yang * isolation to emulate it. However, be especially careful that port 2762186623f4SDavid Yang * isolation takes _after_ FDB lookups, i.e. if an FDB entry (from 2763186623f4SDavid Yang * another bridge) is matched and the destination port (in another 2764186623f4SDavid Yang * bridge) is blocked, the packet will be dropped instead of flooding to 2765186623f4SDavid Yang * the "bridged" ports, thus we need to trap and handle those packets by 2766186623f4SDavid Yang * software. 2767186623f4SDavid Yang * 2768186623f4SDavid Yang * If there is no more than one bridge, we might be able to drop them 2769186623f4SDavid Yang * directly given some conditions are met, but we trap them in all cases 2770186623f4SDavid Yang * for now. 2771186623f4SDavid Yang */ 2772186623f4SDavid Yang ctrl = 0; 2773186623f4SDavid Yang for (int i = 0; i < YT921X_PORT_NUM; i++) 2774186623f4SDavid Yang ctrl |= YT921X_ACT_UNK_ACTn_TRAP(i); 2775186623f4SDavid Yang /* Except for CPU ports, if any packets are sent via CPU ports without 2776186623f4SDavid Yang * tag, they should be dropped. 2777186623f4SDavid Yang */ 2778186623f4SDavid Yang cpu_ports_mask = priv->cpu_ports_mask; 2779186623f4SDavid Yang for_each_set_bit(port, &cpu_ports_mask, YT921X_PORT_NUM) { 2780186623f4SDavid Yang ctrl &= ~YT921X_ACT_UNK_ACTn_M(port); 2781186623f4SDavid Yang ctrl |= YT921X_ACT_UNK_ACTn_DROP(port); 2782186623f4SDavid Yang } 2783186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_ACT_UNK_UCAST, ctrl); 2784186623f4SDavid Yang if (res) 2785186623f4SDavid Yang return res; 2786186623f4SDavid Yang res = yt921x_reg_write(priv, YT921X_ACT_UNK_MCAST, ctrl); 2787186623f4SDavid Yang if (res) 2788186623f4SDavid Yang return res; 2789186623f4SDavid Yang 2790186623f4SDavid Yang /* Tagged VID 0 should be treated as untagged, which confuses the 2791186623f4SDavid Yang * hardware a lot 2792186623f4SDavid Yang */ 2793186623f4SDavid Yang ctrl64 = YT921X_VLAN_CTRL_LEARN_DIS | YT921X_VLAN_CTRL_PORTS_M; 2794186623f4SDavid Yang res = yt921x_reg64_write(priv, YT921X_VLANn_CTRL(0), ctrl64); 2795186623f4SDavid Yang if (res) 2796186623f4SDavid Yang return res; 2797186623f4SDavid Yang 2798186623f4SDavid Yang /* Miscellaneous */ 2799186623f4SDavid Yang res = yt921x_reg_set_bits(priv, YT921X_SENSOR, YT921X_SENSOR_TEMP); 2800186623f4SDavid Yang if (res) 2801186623f4SDavid Yang return res; 2802186623f4SDavid Yang 2803186623f4SDavid Yang return 0; 2804186623f4SDavid Yang } 2805186623f4SDavid Yang 2806186623f4SDavid Yang static int yt921x_dsa_setup(struct dsa_switch *ds) 2807186623f4SDavid Yang { 2808186623f4SDavid Yang struct yt921x_priv *priv = to_yt921x_priv(ds); 2809186623f4SDavid Yang struct device *dev = to_device(priv); 2810186623f4SDavid Yang struct device_node *np = dev->of_node; 2811186623f4SDavid Yang struct device_node *child; 2812186623f4SDavid Yang int res; 2813186623f4SDavid Yang 2814186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2815186623f4SDavid Yang res = yt921x_chip_reset(priv); 2816186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2817186623f4SDavid Yang 2818186623f4SDavid Yang if (res) 2819186623f4SDavid Yang return res; 2820186623f4SDavid Yang 2821186623f4SDavid Yang /* Register the internal mdio bus. Nodes for internal ports should have 2822186623f4SDavid Yang * proper phy-handle pointing to their PHYs. Not enabling the internal 2823186623f4SDavid Yang * bus is possible, though pretty wired, if internal ports are not used. 2824186623f4SDavid Yang */ 2825186623f4SDavid Yang child = of_get_child_by_name(np, "mdio"); 2826186623f4SDavid Yang if (child) { 2827186623f4SDavid Yang res = yt921x_mbus_int_init(priv, child); 2828186623f4SDavid Yang of_node_put(child); 2829186623f4SDavid Yang if (res) 2830186623f4SDavid Yang return res; 2831186623f4SDavid Yang } 2832186623f4SDavid Yang 2833186623f4SDavid Yang /* External mdio bus is optional */ 2834186623f4SDavid Yang child = of_get_child_by_name(np, "mdio-external"); 2835186623f4SDavid Yang if (child) { 2836186623f4SDavid Yang res = yt921x_mbus_ext_init(priv, child); 2837186623f4SDavid Yang of_node_put(child); 2838186623f4SDavid Yang if (res) 2839186623f4SDavid Yang return res; 2840186623f4SDavid Yang 2841186623f4SDavid Yang dev_err(dev, "Untested external mdio bus\n"); 2842186623f4SDavid Yang return -ENODEV; 2843186623f4SDavid Yang } 2844186623f4SDavid Yang 2845186623f4SDavid Yang mutex_lock(&priv->reg_lock); 2846186623f4SDavid Yang res = yt921x_chip_setup(priv); 2847186623f4SDavid Yang mutex_unlock(&priv->reg_lock); 2848186623f4SDavid Yang 2849186623f4SDavid Yang if (res) 2850186623f4SDavid Yang return res; 2851186623f4SDavid Yang 2852186623f4SDavid Yang return 0; 2853186623f4SDavid Yang } 2854186623f4SDavid Yang 2855186623f4SDavid Yang static const struct phylink_mac_ops yt921x_phylink_mac_ops = { 2856186623f4SDavid Yang .mac_link_down = yt921x_phylink_mac_link_down, 2857186623f4SDavid Yang .mac_link_up = yt921x_phylink_mac_link_up, 2858186623f4SDavid Yang .mac_config = yt921x_phylink_mac_config, 2859186623f4SDavid Yang }; 2860186623f4SDavid Yang 2861186623f4SDavid Yang static const struct dsa_switch_ops yt921x_dsa_switch_ops = { 2862186623f4SDavid Yang /* mib */ 2863186623f4SDavid Yang .get_strings = yt921x_dsa_get_strings, 2864186623f4SDavid Yang .get_ethtool_stats = yt921x_dsa_get_ethtool_stats, 2865186623f4SDavid Yang .get_sset_count = yt921x_dsa_get_sset_count, 2866186623f4SDavid Yang .get_eth_mac_stats = yt921x_dsa_get_eth_mac_stats, 2867186623f4SDavid Yang .get_eth_ctrl_stats = yt921x_dsa_get_eth_ctrl_stats, 2868186623f4SDavid Yang .get_rmon_stats = yt921x_dsa_get_rmon_stats, 2869186623f4SDavid Yang .get_stats64 = yt921x_dsa_get_stats64, 2870186623f4SDavid Yang .get_pause_stats = yt921x_dsa_get_pause_stats, 2871186623f4SDavid Yang /* eee */ 2872186623f4SDavid Yang .support_eee = dsa_supports_eee, 2873186623f4SDavid Yang .set_mac_eee = yt921x_dsa_set_mac_eee, 2874186623f4SDavid Yang /* mtu */ 2875186623f4SDavid Yang .port_change_mtu = yt921x_dsa_port_change_mtu, 2876186623f4SDavid Yang .port_max_mtu = yt921x_dsa_port_max_mtu, 2877*42e63b13SVladimir Oltean /* hsr */ 2878*42e63b13SVladimir Oltean .port_hsr_leave = dsa_port_simple_hsr_leave, 2879*42e63b13SVladimir Oltean .port_hsr_join = dsa_port_simple_hsr_join, 2880186623f4SDavid Yang /* mirror */ 2881186623f4SDavid Yang .port_mirror_del = yt921x_dsa_port_mirror_del, 2882186623f4SDavid Yang .port_mirror_add = yt921x_dsa_port_mirror_add, 2883186623f4SDavid Yang /* fdb */ 2884186623f4SDavid Yang .port_fdb_dump = yt921x_dsa_port_fdb_dump, 2885186623f4SDavid Yang .port_fast_age = yt921x_dsa_port_fast_age, 2886186623f4SDavid Yang .set_ageing_time = yt921x_dsa_set_ageing_time, 2887186623f4SDavid Yang .port_fdb_del = yt921x_dsa_port_fdb_del, 2888186623f4SDavid Yang .port_fdb_add = yt921x_dsa_port_fdb_add, 2889186623f4SDavid Yang .port_mdb_del = yt921x_dsa_port_mdb_del, 2890186623f4SDavid Yang .port_mdb_add = yt921x_dsa_port_mdb_add, 2891186623f4SDavid Yang /* vlan */ 2892186623f4SDavid Yang .port_vlan_filtering = yt921x_dsa_port_vlan_filtering, 2893186623f4SDavid Yang .port_vlan_del = yt921x_dsa_port_vlan_del, 2894186623f4SDavid Yang .port_vlan_add = yt921x_dsa_port_vlan_add, 2895186623f4SDavid Yang /* bridge */ 2896186623f4SDavid Yang .port_pre_bridge_flags = yt921x_dsa_port_pre_bridge_flags, 2897186623f4SDavid Yang .port_bridge_flags = yt921x_dsa_port_bridge_flags, 2898186623f4SDavid Yang .port_bridge_leave = yt921x_dsa_port_bridge_leave, 2899186623f4SDavid Yang .port_bridge_join = yt921x_dsa_port_bridge_join, 2900633b1d01SDavid Yang /* mst */ 2901633b1d01SDavid Yang .port_mst_state_set = yt921x_dsa_port_mst_state_set, 2902633b1d01SDavid Yang .vlan_msti_set = yt921x_dsa_vlan_msti_set, 2903633b1d01SDavid Yang .port_stp_state_set = yt921x_dsa_port_stp_state_set, 2904186623f4SDavid Yang /* port */ 2905186623f4SDavid Yang .get_tag_protocol = yt921x_dsa_get_tag_protocol, 2906186623f4SDavid Yang .phylink_get_caps = yt921x_dsa_phylink_get_caps, 2907186623f4SDavid Yang .port_setup = yt921x_dsa_port_setup, 2908186623f4SDavid Yang /* chip */ 2909186623f4SDavid Yang .setup = yt921x_dsa_setup, 2910186623f4SDavid Yang }; 2911186623f4SDavid Yang 2912186623f4SDavid Yang static void yt921x_mdio_shutdown(struct mdio_device *mdiodev) 2913186623f4SDavid Yang { 2914186623f4SDavid Yang struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); 2915186623f4SDavid Yang 2916186623f4SDavid Yang if (!priv) 2917186623f4SDavid Yang return; 2918186623f4SDavid Yang 2919186623f4SDavid Yang dsa_switch_shutdown(&priv->ds); 2920186623f4SDavid Yang } 2921186623f4SDavid Yang 2922186623f4SDavid Yang static void yt921x_mdio_remove(struct mdio_device *mdiodev) 2923186623f4SDavid Yang { 2924186623f4SDavid Yang struct yt921x_priv *priv = mdiodev_get_drvdata(mdiodev); 2925186623f4SDavid Yang 2926186623f4SDavid Yang if (!priv) 2927186623f4SDavid Yang return; 2928186623f4SDavid Yang 2929186623f4SDavid Yang for (size_t i = ARRAY_SIZE(priv->ports); i-- > 0; ) { 2930186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[i]; 2931186623f4SDavid Yang 2932186623f4SDavid Yang disable_delayed_work_sync(&pp->mib_read); 2933186623f4SDavid Yang } 2934186623f4SDavid Yang 2935186623f4SDavid Yang dsa_unregister_switch(&priv->ds); 2936186623f4SDavid Yang 2937186623f4SDavid Yang mutex_destroy(&priv->reg_lock); 2938186623f4SDavid Yang } 2939186623f4SDavid Yang 2940186623f4SDavid Yang static int yt921x_mdio_probe(struct mdio_device *mdiodev) 2941186623f4SDavid Yang { 2942186623f4SDavid Yang struct device *dev = &mdiodev->dev; 2943186623f4SDavid Yang struct yt921x_reg_mdio *mdio; 2944186623f4SDavid Yang struct yt921x_priv *priv; 2945186623f4SDavid Yang struct dsa_switch *ds; 2946186623f4SDavid Yang 2947186623f4SDavid Yang priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 2948186623f4SDavid Yang if (!priv) 2949186623f4SDavid Yang return -ENOMEM; 2950186623f4SDavid Yang 2951186623f4SDavid Yang mdio = devm_kzalloc(dev, sizeof(*mdio), GFP_KERNEL); 2952186623f4SDavid Yang if (!mdio) 2953186623f4SDavid Yang return -ENOMEM; 2954186623f4SDavid Yang 2955186623f4SDavid Yang mdio->bus = mdiodev->bus; 2956186623f4SDavid Yang mdio->addr = mdiodev->addr; 2957186623f4SDavid Yang mdio->switchid = 0; 2958186623f4SDavid Yang 2959186623f4SDavid Yang mutex_init(&priv->reg_lock); 2960186623f4SDavid Yang 2961186623f4SDavid Yang priv->reg_ops = &yt921x_reg_ops_mdio; 2962186623f4SDavid Yang priv->reg_ctx = mdio; 2963186623f4SDavid Yang 2964186623f4SDavid Yang for (size_t i = 0; i < ARRAY_SIZE(priv->ports); i++) { 2965186623f4SDavid Yang struct yt921x_port *pp = &priv->ports[i]; 2966186623f4SDavid Yang 2967186623f4SDavid Yang pp->index = i; 2968186623f4SDavid Yang INIT_DELAYED_WORK(&pp->mib_read, yt921x_poll_mib); 2969186623f4SDavid Yang } 2970186623f4SDavid Yang 2971186623f4SDavid Yang ds = &priv->ds; 2972186623f4SDavid Yang ds->dev = dev; 2973186623f4SDavid Yang ds->assisted_learning_on_cpu_port = true; 2974186623f4SDavid Yang ds->priv = priv; 2975186623f4SDavid Yang ds->ops = &yt921x_dsa_switch_ops; 2976ea2d3befSDavid Yang ds->ageing_time_min = 1 * 5000; 2977ea2d3befSDavid Yang ds->ageing_time_max = U16_MAX * 5000; 2978186623f4SDavid Yang ds->phylink_mac_ops = &yt921x_phylink_mac_ops; 2979186623f4SDavid Yang ds->num_ports = YT921X_PORT_NUM; 2980186623f4SDavid Yang 2981186623f4SDavid Yang mdiodev_set_drvdata(mdiodev, priv); 2982186623f4SDavid Yang 2983186623f4SDavid Yang return dsa_register_switch(ds); 2984186623f4SDavid Yang } 2985186623f4SDavid Yang 2986186623f4SDavid Yang static const struct of_device_id yt921x_of_match[] = { 2987186623f4SDavid Yang { .compatible = "motorcomm,yt9215" }, 2988186623f4SDavid Yang {} 2989186623f4SDavid Yang }; 2990186623f4SDavid Yang MODULE_DEVICE_TABLE(of, yt921x_of_match); 2991186623f4SDavid Yang 2992186623f4SDavid Yang static struct mdio_driver yt921x_mdio_driver = { 2993186623f4SDavid Yang .probe = yt921x_mdio_probe, 2994186623f4SDavid Yang .remove = yt921x_mdio_remove, 2995186623f4SDavid Yang .shutdown = yt921x_mdio_shutdown, 2996186623f4SDavid Yang .mdiodrv.driver = { 2997186623f4SDavid Yang .name = YT921X_NAME, 2998186623f4SDavid Yang .of_match_table = yt921x_of_match, 2999186623f4SDavid Yang }, 3000186623f4SDavid Yang }; 3001186623f4SDavid Yang 3002186623f4SDavid Yang mdio_module_driver(yt921x_mdio_driver); 3003186623f4SDavid Yang 3004186623f4SDavid Yang MODULE_AUTHOR("David Yang <mmyangfl@gmail.com>"); 3005186623f4SDavid Yang MODULE_DESCRIPTION("Driver for Motorcomm YT921x Switch"); 3006186623f4SDavid Yang MODULE_LICENSE("GPL"); 3007