xref: /openbmc/linux/drivers/net/dsa/qca/qca8k-common.c (revision fd3cae2f)
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, &reg);
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