1027152b8SChristian Marangi // SPDX-License-Identifier: GPL-2.0 2027152b8SChristian Marangi /* 3027152b8SChristian Marangi * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> 4027152b8SChristian Marangi * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org> 5027152b8SChristian Marangi * Copyright (c) 2015, 2019, The Linux Foundation. All rights reserved. 6027152b8SChristian Marangi * Copyright (c) 2016 John Crispin <john@phrozen.org> 7027152b8SChristian Marangi */ 8027152b8SChristian Marangi 9027152b8SChristian Marangi #include <linux/netdevice.h> 10027152b8SChristian Marangi #include <net/dsa.h> 11*fd3cae2fSChristian Marangi #include <linux/if_bridge.h> 12027152b8SChristian Marangi 13027152b8SChristian Marangi #include "qca8k.h" 14027152b8SChristian Marangi 15027152b8SChristian Marangi #define MIB_DESC(_s, _o, _n) \ 16027152b8SChristian Marangi { \ 17027152b8SChristian Marangi .size = (_s), \ 18027152b8SChristian Marangi .offset = (_o), \ 19027152b8SChristian Marangi .name = (_n), \ 20027152b8SChristian Marangi } 21027152b8SChristian Marangi 22027152b8SChristian Marangi const struct qca8k_mib_desc ar8327_mib[] = { 23027152b8SChristian Marangi MIB_DESC(1, 0x00, "RxBroad"), 24027152b8SChristian Marangi MIB_DESC(1, 0x04, "RxPause"), 25027152b8SChristian Marangi MIB_DESC(1, 0x08, "RxMulti"), 26027152b8SChristian Marangi MIB_DESC(1, 0x0c, "RxFcsErr"), 27027152b8SChristian Marangi MIB_DESC(1, 0x10, "RxAlignErr"), 28027152b8SChristian Marangi MIB_DESC(1, 0x14, "RxRunt"), 29027152b8SChristian Marangi MIB_DESC(1, 0x18, "RxFragment"), 30027152b8SChristian Marangi MIB_DESC(1, 0x1c, "Rx64Byte"), 31027152b8SChristian Marangi MIB_DESC(1, 0x20, "Rx128Byte"), 32027152b8SChristian Marangi MIB_DESC(1, 0x24, "Rx256Byte"), 33027152b8SChristian Marangi MIB_DESC(1, 0x28, "Rx512Byte"), 34027152b8SChristian Marangi MIB_DESC(1, 0x2c, "Rx1024Byte"), 35027152b8SChristian Marangi MIB_DESC(1, 0x30, "Rx1518Byte"), 36027152b8SChristian Marangi MIB_DESC(1, 0x34, "RxMaxByte"), 37027152b8SChristian Marangi MIB_DESC(1, 0x38, "RxTooLong"), 38027152b8SChristian Marangi MIB_DESC(2, 0x3c, "RxGoodByte"), 39027152b8SChristian Marangi MIB_DESC(2, 0x44, "RxBadByte"), 40027152b8SChristian Marangi MIB_DESC(1, 0x4c, "RxOverFlow"), 41027152b8SChristian Marangi MIB_DESC(1, 0x50, "Filtered"), 42027152b8SChristian Marangi MIB_DESC(1, 0x54, "TxBroad"), 43027152b8SChristian Marangi MIB_DESC(1, 0x58, "TxPause"), 44027152b8SChristian Marangi MIB_DESC(1, 0x5c, "TxMulti"), 45027152b8SChristian Marangi MIB_DESC(1, 0x60, "TxUnderRun"), 46027152b8SChristian Marangi MIB_DESC(1, 0x64, "Tx64Byte"), 47027152b8SChristian Marangi MIB_DESC(1, 0x68, "Tx128Byte"), 48027152b8SChristian Marangi MIB_DESC(1, 0x6c, "Tx256Byte"), 49027152b8SChristian Marangi MIB_DESC(1, 0x70, "Tx512Byte"), 50027152b8SChristian Marangi MIB_DESC(1, 0x74, "Tx1024Byte"), 51027152b8SChristian Marangi MIB_DESC(1, 0x78, "Tx1518Byte"), 52027152b8SChristian Marangi MIB_DESC(1, 0x7c, "TxMaxByte"), 53027152b8SChristian Marangi MIB_DESC(1, 0x80, "TxOverSize"), 54027152b8SChristian Marangi MIB_DESC(2, 0x84, "TxByte"), 55027152b8SChristian Marangi MIB_DESC(1, 0x8c, "TxCollision"), 56027152b8SChristian Marangi MIB_DESC(1, 0x90, "TxAbortCol"), 57027152b8SChristian Marangi MIB_DESC(1, 0x94, "TxMultiCol"), 58027152b8SChristian Marangi MIB_DESC(1, 0x98, "TxSingleCol"), 59027152b8SChristian Marangi MIB_DESC(1, 0x9c, "TxExcDefer"), 60027152b8SChristian Marangi MIB_DESC(1, 0xa0, "TxDefer"), 61027152b8SChristian Marangi MIB_DESC(1, 0xa4, "TxLateCol"), 62027152b8SChristian Marangi MIB_DESC(1, 0xa8, "RXUnicast"), 63027152b8SChristian Marangi MIB_DESC(1, 0xac, "TXUnicast"), 64027152b8SChristian Marangi }; 65d5f901eaSChristian Marangi 66d5f901eaSChristian Marangi int qca8k_read(struct qca8k_priv *priv, u32 reg, u32 *val) 67d5f901eaSChristian Marangi { 68d5f901eaSChristian Marangi return regmap_read(priv->regmap, reg, val); 69d5f901eaSChristian Marangi } 70d5f901eaSChristian Marangi 71d5f901eaSChristian Marangi int qca8k_write(struct qca8k_priv *priv, u32 reg, u32 val) 72d5f901eaSChristian Marangi { 73d5f901eaSChristian Marangi return regmap_write(priv->regmap, reg, val); 74d5f901eaSChristian Marangi } 75d5f901eaSChristian Marangi 76d5f901eaSChristian Marangi int qca8k_rmw(struct qca8k_priv *priv, u32 reg, u32 mask, u32 write_val) 77d5f901eaSChristian Marangi { 78d5f901eaSChristian Marangi return regmap_update_bits(priv->regmap, reg, mask, write_val); 79d5f901eaSChristian Marangi } 80d5f901eaSChristian Marangi 81d5f901eaSChristian Marangi static const struct regmap_range qca8k_readable_ranges[] = { 82d5f901eaSChristian Marangi regmap_reg_range(0x0000, 0x00e4), /* Global control */ 83d5f901eaSChristian Marangi regmap_reg_range(0x0100, 0x0168), /* EEE control */ 84d5f901eaSChristian Marangi regmap_reg_range(0x0200, 0x0270), /* Parser control */ 85d5f901eaSChristian Marangi regmap_reg_range(0x0400, 0x0454), /* ACL */ 86d5f901eaSChristian Marangi regmap_reg_range(0x0600, 0x0718), /* Lookup */ 87d5f901eaSChristian Marangi regmap_reg_range(0x0800, 0x0b70), /* QM */ 88d5f901eaSChristian Marangi regmap_reg_range(0x0c00, 0x0c80), /* PKT */ 89d5f901eaSChristian Marangi regmap_reg_range(0x0e00, 0x0e98), /* L3 */ 90d5f901eaSChristian Marangi regmap_reg_range(0x1000, 0x10ac), /* MIB - Port0 */ 91d5f901eaSChristian Marangi regmap_reg_range(0x1100, 0x11ac), /* MIB - Port1 */ 92d5f901eaSChristian Marangi regmap_reg_range(0x1200, 0x12ac), /* MIB - Port2 */ 93d5f901eaSChristian Marangi regmap_reg_range(0x1300, 0x13ac), /* MIB - Port3 */ 94d5f901eaSChristian Marangi regmap_reg_range(0x1400, 0x14ac), /* MIB - Port4 */ 95d5f901eaSChristian Marangi regmap_reg_range(0x1500, 0x15ac), /* MIB - Port5 */ 96d5f901eaSChristian Marangi regmap_reg_range(0x1600, 0x16ac), /* MIB - Port6 */ 97d5f901eaSChristian Marangi }; 98d5f901eaSChristian Marangi 99d5f901eaSChristian Marangi const struct regmap_access_table qca8k_readable_table = { 100d5f901eaSChristian Marangi .yes_ranges = qca8k_readable_ranges, 101d5f901eaSChristian Marangi .n_yes_ranges = ARRAY_SIZE(qca8k_readable_ranges), 102d5f901eaSChristian Marangi }; 10391074644SChristian Marangi 10491074644SChristian Marangi /* TODO: remove these extra ops when we can support regmap bulk read/write */ 10591074644SChristian Marangi int qca8k_bulk_read(struct qca8k_priv *priv, u32 reg, u32 *val, int len) 10691074644SChristian Marangi { 10791074644SChristian Marangi int i, count = len / sizeof(u32), ret; 10891074644SChristian Marangi 10991074644SChristian Marangi if (priv->mgmt_master && priv->info->ops->read_eth && 11091074644SChristian Marangi !priv->info->ops->read_eth(priv, reg, val, len)) 11191074644SChristian Marangi return 0; 11291074644SChristian Marangi 11391074644SChristian Marangi for (i = 0; i < count; i++) { 11491074644SChristian Marangi ret = regmap_read(priv->regmap, reg + (i * 4), val + i); 11591074644SChristian Marangi if (ret < 0) 11691074644SChristian Marangi return ret; 11791074644SChristian Marangi } 11891074644SChristian Marangi 11991074644SChristian Marangi return 0; 12091074644SChristian Marangi } 12191074644SChristian Marangi 12291074644SChristian Marangi /* TODO: remove these extra ops when we can support regmap bulk read/write */ 12391074644SChristian Marangi int qca8k_bulk_write(struct qca8k_priv *priv, u32 reg, u32 *val, int len) 12491074644SChristian Marangi { 12591074644SChristian Marangi int i, count = len / sizeof(u32), ret; 12691074644SChristian Marangi u32 tmp; 12791074644SChristian Marangi 12891074644SChristian Marangi if (priv->mgmt_master && priv->info->ops->write_eth && 12991074644SChristian Marangi !priv->info->ops->write_eth(priv, reg, val, len)) 13091074644SChristian Marangi return 0; 13191074644SChristian Marangi 13291074644SChristian Marangi for (i = 0; i < count; i++) { 13391074644SChristian Marangi tmp = val[i]; 13491074644SChristian Marangi 13591074644SChristian Marangi ret = regmap_write(priv->regmap, reg + (i * 4), tmp); 13691074644SChristian Marangi if (ret < 0) 13791074644SChristian Marangi return ret; 13891074644SChristian Marangi } 13991074644SChristian Marangi 14091074644SChristian Marangi return 0; 14191074644SChristian Marangi } 142fce1ec0cSChristian Marangi 143fce1ec0cSChristian Marangi int qca8k_busy_wait(struct qca8k_priv *priv, u32 reg, u32 mask) 144fce1ec0cSChristian Marangi { 145fce1ec0cSChristian Marangi u32 val; 146fce1ec0cSChristian Marangi 147fce1ec0cSChristian Marangi return regmap_read_poll_timeout(priv->regmap, reg, val, !(val & mask), 0, 148fce1ec0cSChristian Marangi QCA8K_BUSY_WAIT_TIMEOUT * USEC_PER_MSEC); 149fce1ec0cSChristian Marangi } 150fce1ec0cSChristian Marangi 151fce1ec0cSChristian Marangi int qca8k_mib_init(struct qca8k_priv *priv) 152fce1ec0cSChristian Marangi { 153fce1ec0cSChristian Marangi int ret; 154fce1ec0cSChristian Marangi 155fce1ec0cSChristian Marangi mutex_lock(&priv->reg_mutex); 156fce1ec0cSChristian Marangi ret = regmap_update_bits(priv->regmap, QCA8K_REG_MIB, 157fce1ec0cSChristian Marangi QCA8K_MIB_FUNC | QCA8K_MIB_BUSY, 158fce1ec0cSChristian Marangi FIELD_PREP(QCA8K_MIB_FUNC, QCA8K_MIB_FLUSH) | 159fce1ec0cSChristian Marangi QCA8K_MIB_BUSY); 160fce1ec0cSChristian Marangi if (ret) 161fce1ec0cSChristian Marangi goto exit; 162fce1ec0cSChristian Marangi 163fce1ec0cSChristian Marangi ret = qca8k_busy_wait(priv, QCA8K_REG_MIB, QCA8K_MIB_BUSY); 164fce1ec0cSChristian Marangi if (ret) 165fce1ec0cSChristian Marangi goto exit; 166fce1ec0cSChristian Marangi 167fce1ec0cSChristian Marangi ret = regmap_set_bits(priv->regmap, QCA8K_REG_MIB, QCA8K_MIB_CPU_KEEP); 168fce1ec0cSChristian Marangi if (ret) 169fce1ec0cSChristian Marangi goto exit; 170fce1ec0cSChristian Marangi 171fce1ec0cSChristian Marangi ret = qca8k_write(priv, QCA8K_REG_MODULE_EN, QCA8K_MODULE_EN_MIB); 172fce1ec0cSChristian Marangi 173fce1ec0cSChristian Marangi exit: 174fce1ec0cSChristian Marangi mutex_unlock(&priv->reg_mutex); 175fce1ec0cSChristian Marangi return ret; 176fce1ec0cSChristian Marangi } 177472fcea1SChristian Marangi 178472fcea1SChristian Marangi void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) 179472fcea1SChristian Marangi { 180472fcea1SChristian Marangi u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; 181472fcea1SChristian Marangi 182472fcea1SChristian Marangi /* Port 0 and 6 have no internal PHY */ 183472fcea1SChristian Marangi if (port > 0 && port < 6) 184472fcea1SChristian Marangi mask |= QCA8K_PORT_STATUS_LINK_AUTO; 185472fcea1SChristian Marangi 186472fcea1SChristian Marangi if (enable) 187472fcea1SChristian Marangi regmap_set_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); 188472fcea1SChristian Marangi else 189472fcea1SChristian Marangi regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); 190472fcea1SChristian Marangi } 191472fcea1SChristian Marangi 192472fcea1SChristian Marangi void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, 193472fcea1SChristian Marangi uint8_t *data) 194472fcea1SChristian Marangi { 195472fcea1SChristian Marangi struct qca8k_priv *priv = ds->priv; 196472fcea1SChristian Marangi int i; 197472fcea1SChristian Marangi 198472fcea1SChristian Marangi if (stringset != ETH_SS_STATS) 199472fcea1SChristian Marangi return; 200472fcea1SChristian Marangi 201472fcea1SChristian Marangi for (i = 0; i < priv->info->mib_count; i++) 202472fcea1SChristian Marangi strncpy(data + i * ETH_GSTRING_LEN, ar8327_mib[i].name, 203472fcea1SChristian Marangi ETH_GSTRING_LEN); 204472fcea1SChristian Marangi } 205472fcea1SChristian Marangi 206472fcea1SChristian Marangi void qca8k_get_ethtool_stats(struct dsa_switch *ds, int port, 207472fcea1SChristian Marangi uint64_t *data) 208472fcea1SChristian Marangi { 209472fcea1SChristian Marangi struct qca8k_priv *priv = ds->priv; 210472fcea1SChristian Marangi const struct qca8k_mib_desc *mib; 211472fcea1SChristian Marangi u32 reg, i, val; 212472fcea1SChristian Marangi u32 hi = 0; 213472fcea1SChristian Marangi int ret; 214472fcea1SChristian Marangi 215472fcea1SChristian Marangi if (priv->mgmt_master && priv->info->ops->autocast_mib && 216472fcea1SChristian Marangi priv->info->ops->autocast_mib(ds, port, data) > 0) 217472fcea1SChristian Marangi return; 218472fcea1SChristian Marangi 219472fcea1SChristian Marangi for (i = 0; i < priv->info->mib_count; i++) { 220472fcea1SChristian Marangi mib = &ar8327_mib[i]; 221472fcea1SChristian Marangi reg = QCA8K_PORT_MIB_COUNTER(port) + mib->offset; 222472fcea1SChristian Marangi 223472fcea1SChristian Marangi ret = qca8k_read(priv, reg, &val); 224472fcea1SChristian Marangi if (ret < 0) 225472fcea1SChristian Marangi continue; 226472fcea1SChristian Marangi 227472fcea1SChristian Marangi if (mib->size == 2) { 228472fcea1SChristian Marangi ret = qca8k_read(priv, reg + 4, &hi); 229472fcea1SChristian Marangi if (ret < 0) 230472fcea1SChristian Marangi continue; 231472fcea1SChristian Marangi } 232472fcea1SChristian Marangi 233472fcea1SChristian Marangi data[i] = val; 234472fcea1SChristian Marangi if (mib->size == 2) 235472fcea1SChristian Marangi data[i] |= (u64)hi << 32; 236472fcea1SChristian Marangi } 237472fcea1SChristian Marangi } 238472fcea1SChristian Marangi 239472fcea1SChristian Marangi int qca8k_get_sset_count(struct dsa_switch *ds, int port, int sset) 240472fcea1SChristian Marangi { 241472fcea1SChristian Marangi struct qca8k_priv *priv = ds->priv; 242472fcea1SChristian Marangi 243472fcea1SChristian Marangi if (sset != ETH_SS_STATS) 244472fcea1SChristian Marangi return 0; 245472fcea1SChristian Marangi 246472fcea1SChristian Marangi return priv->info->mib_count; 247472fcea1SChristian Marangi } 248472fcea1SChristian Marangi 249472fcea1SChristian Marangi int qca8k_set_mac_eee(struct dsa_switch *ds, int port, 250472fcea1SChristian Marangi struct ethtool_eee *eee) 251472fcea1SChristian Marangi { 252472fcea1SChristian Marangi u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); 253472fcea1SChristian Marangi struct qca8k_priv *priv = ds->priv; 254472fcea1SChristian Marangi u32 reg; 255472fcea1SChristian Marangi int ret; 256472fcea1SChristian Marangi 257472fcea1SChristian Marangi mutex_lock(&priv->reg_mutex); 258472fcea1SChristian Marangi ret = qca8k_read(priv, QCA8K_REG_EEE_CTRL, ®); 259472fcea1SChristian Marangi if (ret < 0) 260472fcea1SChristian Marangi goto exit; 261472fcea1SChristian Marangi 262472fcea1SChristian Marangi if (eee->eee_enabled) 263472fcea1SChristian Marangi reg |= lpi_en; 264472fcea1SChristian Marangi else 265472fcea1SChristian Marangi reg &= ~lpi_en; 266472fcea1SChristian Marangi ret = qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); 267472fcea1SChristian Marangi 268472fcea1SChristian Marangi exit: 269472fcea1SChristian Marangi mutex_unlock(&priv->reg_mutex); 270472fcea1SChristian Marangi return ret; 271472fcea1SChristian Marangi } 272472fcea1SChristian Marangi 273472fcea1SChristian Marangi int qca8k_get_mac_eee(struct dsa_switch *ds, int port, 274472fcea1SChristian Marangi struct ethtool_eee *e) 275472fcea1SChristian Marangi { 276472fcea1SChristian Marangi /* Nothing to do on the port's MAC */ 277472fcea1SChristian Marangi return 0; 278472fcea1SChristian Marangi } 279*fd3cae2fSChristian Marangi 280*fd3cae2fSChristian Marangi void qca8k_port_stp_state_set(struct dsa_switch *ds, int port, u8 state) 281*fd3cae2fSChristian Marangi { 282*fd3cae2fSChristian Marangi struct qca8k_priv *priv = ds->priv; 283*fd3cae2fSChristian Marangi u32 stp_state; 284*fd3cae2fSChristian Marangi 285*fd3cae2fSChristian Marangi switch (state) { 286*fd3cae2fSChristian Marangi case BR_STATE_DISABLED: 287*fd3cae2fSChristian Marangi stp_state = QCA8K_PORT_LOOKUP_STATE_DISABLED; 288*fd3cae2fSChristian Marangi break; 289*fd3cae2fSChristian Marangi case BR_STATE_BLOCKING: 290*fd3cae2fSChristian Marangi stp_state = QCA8K_PORT_LOOKUP_STATE_BLOCKING; 291*fd3cae2fSChristian Marangi break; 292*fd3cae2fSChristian Marangi case BR_STATE_LISTENING: 293*fd3cae2fSChristian Marangi stp_state = QCA8K_PORT_LOOKUP_STATE_LISTENING; 294*fd3cae2fSChristian Marangi break; 295*fd3cae2fSChristian Marangi case BR_STATE_LEARNING: 296*fd3cae2fSChristian Marangi stp_state = QCA8K_PORT_LOOKUP_STATE_LEARNING; 297*fd3cae2fSChristian Marangi break; 298*fd3cae2fSChristian Marangi case BR_STATE_FORWARDING: 299*fd3cae2fSChristian Marangi default: 300*fd3cae2fSChristian Marangi stp_state = QCA8K_PORT_LOOKUP_STATE_FORWARD; 301*fd3cae2fSChristian Marangi break; 302*fd3cae2fSChristian Marangi } 303*fd3cae2fSChristian Marangi 304*fd3cae2fSChristian Marangi qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), 305*fd3cae2fSChristian Marangi QCA8K_PORT_LOOKUP_STATE_MASK, stp_state); 306*fd3cae2fSChristian Marangi } 307*fd3cae2fSChristian Marangi 308*fd3cae2fSChristian Marangi int qca8k_port_bridge_join(struct dsa_switch *ds, int port, 309*fd3cae2fSChristian Marangi struct dsa_bridge bridge, 310*fd3cae2fSChristian Marangi bool *tx_fwd_offload, 311*fd3cae2fSChristian Marangi struct netlink_ext_ack *extack) 312*fd3cae2fSChristian Marangi { 313*fd3cae2fSChristian Marangi struct qca8k_priv *priv = ds->priv; 314*fd3cae2fSChristian Marangi int port_mask, cpu_port; 315*fd3cae2fSChristian Marangi int i, ret; 316*fd3cae2fSChristian Marangi 317*fd3cae2fSChristian Marangi cpu_port = dsa_to_port(ds, port)->cpu_dp->index; 318*fd3cae2fSChristian Marangi port_mask = BIT(cpu_port); 319*fd3cae2fSChristian Marangi 320*fd3cae2fSChristian Marangi for (i = 0; i < QCA8K_NUM_PORTS; i++) { 321*fd3cae2fSChristian Marangi if (dsa_is_cpu_port(ds, i)) 322*fd3cae2fSChristian Marangi continue; 323*fd3cae2fSChristian Marangi if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 324*fd3cae2fSChristian Marangi continue; 325*fd3cae2fSChristian Marangi /* Add this port to the portvlan mask of the other ports 326*fd3cae2fSChristian Marangi * in the bridge 327*fd3cae2fSChristian Marangi */ 328*fd3cae2fSChristian Marangi ret = regmap_set_bits(priv->regmap, 329*fd3cae2fSChristian Marangi QCA8K_PORT_LOOKUP_CTRL(i), 330*fd3cae2fSChristian Marangi BIT(port)); 331*fd3cae2fSChristian Marangi if (ret) 332*fd3cae2fSChristian Marangi return ret; 333*fd3cae2fSChristian Marangi if (i != port) 334*fd3cae2fSChristian Marangi port_mask |= BIT(i); 335*fd3cae2fSChristian Marangi } 336*fd3cae2fSChristian Marangi 337*fd3cae2fSChristian Marangi /* Add all other ports to this ports portvlan mask */ 338*fd3cae2fSChristian Marangi ret = qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), 339*fd3cae2fSChristian Marangi QCA8K_PORT_LOOKUP_MEMBER, port_mask); 340*fd3cae2fSChristian Marangi 341*fd3cae2fSChristian Marangi return ret; 342*fd3cae2fSChristian Marangi } 343*fd3cae2fSChristian Marangi 344*fd3cae2fSChristian Marangi void qca8k_port_bridge_leave(struct dsa_switch *ds, int port, 345*fd3cae2fSChristian Marangi struct dsa_bridge bridge) 346*fd3cae2fSChristian Marangi { 347*fd3cae2fSChristian Marangi struct qca8k_priv *priv = ds->priv; 348*fd3cae2fSChristian Marangi int cpu_port, i; 349*fd3cae2fSChristian Marangi 350*fd3cae2fSChristian Marangi cpu_port = dsa_to_port(ds, port)->cpu_dp->index; 351*fd3cae2fSChristian Marangi 352*fd3cae2fSChristian Marangi for (i = 0; i < QCA8K_NUM_PORTS; i++) { 353*fd3cae2fSChristian Marangi if (dsa_is_cpu_port(ds, i)) 354*fd3cae2fSChristian Marangi continue; 355*fd3cae2fSChristian Marangi if (!dsa_port_offloads_bridge(dsa_to_port(ds, i), &bridge)) 356*fd3cae2fSChristian Marangi continue; 357*fd3cae2fSChristian Marangi /* Remove this port to the portvlan mask of the other ports 358*fd3cae2fSChristian Marangi * in the bridge 359*fd3cae2fSChristian Marangi */ 360*fd3cae2fSChristian Marangi regmap_clear_bits(priv->regmap, 361*fd3cae2fSChristian Marangi QCA8K_PORT_LOOKUP_CTRL(i), 362*fd3cae2fSChristian Marangi BIT(port)); 363*fd3cae2fSChristian Marangi } 364*fd3cae2fSChristian Marangi 365*fd3cae2fSChristian Marangi /* Set the cpu port to be the only one in the portvlan mask of 366*fd3cae2fSChristian Marangi * this port 367*fd3cae2fSChristian Marangi */ 368*fd3cae2fSChristian Marangi qca8k_rmw(priv, QCA8K_PORT_LOOKUP_CTRL(port), 369*fd3cae2fSChristian Marangi QCA8K_PORT_LOOKUP_MEMBER, BIT(cpu_port)); 370*fd3cae2fSChristian Marangi } 371