1 From e9bbf019af44b204b71ef8edf224002550aab641 Mon Sep 17 00:00:00 2001
2 From: Christian Marangi <ansuelsmth@gmail.com>
3 Date: Wed, 27 Jul 2022 13:35:22 +0200
4 Subject: [PATCH 13/14] net: dsa: qca8k: move port LAG functions to common code
6 The same port LAG functions are used by drivers based on qca8k family
7 switch. Move them to common code to make them accessible also by other
10 Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
11 Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
12 Signed-off-by: Jakub Kicinski <kuba@kernel.org>
14 drivers/net/dsa/qca/qca8k-8xxx.c | 168 -----------------------------
15 drivers/net/dsa/qca/qca8k-common.c | 165 ++++++++++++++++++++++++++++
16 drivers/net/dsa/qca/qca8k.h | 6 ++
17 3 files changed, 171 insertions(+), 168 deletions(-)
19 --- a/drivers/net/dsa/qca/qca8k-8xxx.c
20 +++ b/drivers/net/dsa/qca/qca8k-8xxx.c
21 @@ -1743,178 +1743,6 @@ qca8k_get_tag_protocol(struct dsa_switch
22 return DSA_TAG_PROTO_QCA;
26 -qca8k_lag_can_offload(struct dsa_switch *ds,
27 - struct net_device *lag,
28 - struct netdev_lag_upper_info *info)
30 - struct dsa_port *dp;
31 - int id, members = 0;
33 - id = dsa_lag_id(ds->dst, lag);
34 - if (id < 0 || id >= ds->num_lag_ids)
37 - dsa_lag_foreach_port(dp, ds->dst, lag)
38 - /* Includes the port joining the LAG */
41 - if (members > QCA8K_NUM_PORTS_FOR_LAG)
44 - if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
47 - if (info->hash_type != NETDEV_LAG_HASH_L2 &&
48 - info->hash_type != NETDEV_LAG_HASH_L23)
55 -qca8k_lag_setup_hash(struct dsa_switch *ds,
56 - struct net_device *lag,
57 - struct netdev_lag_upper_info *info)
59 - struct qca8k_priv *priv = ds->priv;
60 - bool unique_lag = true;
64 - id = dsa_lag_id(ds->dst, lag);
66 - switch (info->hash_type) {
67 - case NETDEV_LAG_HASH_L23:
68 - hash |= QCA8K_TRUNK_HASH_SIP_EN;
69 - hash |= QCA8K_TRUNK_HASH_DIP_EN;
71 - case NETDEV_LAG_HASH_L2:
72 - hash |= QCA8K_TRUNK_HASH_SA_EN;
73 - hash |= QCA8K_TRUNK_HASH_DA_EN;
75 - default: /* We should NEVER reach this */
79 - /* Check if we are the unique configured LAG */
80 - dsa_lags_foreach_id(i, ds->dst)
81 - if (i != id && dsa_lag_dev(ds->dst, i)) {
86 - /* Hash Mode is global. Make sure the same Hash Mode
87 - * is set to all the 4 possible lag.
88 - * If we are the unique LAG we can set whatever hash
90 - * To change hash mode it's needed to remove all LAG
91 - * and change the mode with the latest.
94 - priv->lag_hash_mode = hash;
95 - } else if (priv->lag_hash_mode != hash) {
96 - netdev_err(lag, "Error: Mismateched Hash Mode across different lag is not supported\n");
100 - return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
101 - QCA8K_TRUNK_HASH_MASK, hash);
105 -qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
106 - struct net_device *lag, bool delete)
108 - struct qca8k_priv *priv = ds->priv;
112 - id = dsa_lag_id(ds->dst, lag);
114 - /* Read current port member */
115 - ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
119 - /* Shift val to the correct trunk */
120 - val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
121 - val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
127 - /* Update port member. With empty portmap disable trunk */
128 - ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
129 - QCA8K_REG_GOL_TRUNK_MEMBER(id) |
130 - QCA8K_REG_GOL_TRUNK_EN(id),
131 - !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
132 - val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
134 - /* Search empty member if adding or port on deleting */
135 - for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
136 - ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
140 - val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
141 - val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
144 - /* If port flagged to be disabled assume this member is
147 - if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
150 - val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
154 - /* If port flagged to be enabled assume this member is
157 - if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
161 - /* We have found the member to add/remove */
165 - /* Set port in the correct port mask or disable port if in delete mode */
166 - return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
167 - QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
168 - QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
169 - !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
170 - port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
174 -qca8k_port_lag_join(struct dsa_switch *ds, int port,
175 - struct net_device *lag,
176 - struct netdev_lag_upper_info *info)
180 - if (!qca8k_lag_can_offload(ds, lag, info))
181 - return -EOPNOTSUPP;
183 - ret = qca8k_lag_setup_hash(ds, lag, info);
187 - return qca8k_lag_refresh_portmap(ds, port, lag, false);
191 -qca8k_port_lag_leave(struct dsa_switch *ds, int port,
192 - struct net_device *lag)
194 - return qca8k_lag_refresh_portmap(ds, port, lag, true);
198 qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
200 --- a/drivers/net/dsa/qca/qca8k-common.c
201 +++ b/drivers/net/dsa/qca/qca8k-common.c
202 @@ -1009,3 +1009,169 @@ int qca8k_port_vlan_del(struct dsa_switc
207 +static bool qca8k_lag_can_offload(struct dsa_switch *ds,
208 + struct net_device *lag,
209 + struct netdev_lag_upper_info *info)
211 + struct dsa_port *dp;
212 + int id, members = 0;
214 + id = dsa_lag_id(ds->dst, lag);
215 + if (id < 0 || id >= ds->num_lag_ids)
218 + dsa_lag_foreach_port(dp, ds->dst, lag)
219 + /* Includes the port joining the LAG */
222 + if (members > QCA8K_NUM_PORTS_FOR_LAG)
225 + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
228 + if (info->hash_type != NETDEV_LAG_HASH_L2 &&
229 + info->hash_type != NETDEV_LAG_HASH_L23)
235 +static int qca8k_lag_setup_hash(struct dsa_switch *ds,
236 + struct net_device *lag,
237 + struct netdev_lag_upper_info *info)
239 + struct qca8k_priv *priv = ds->priv;
240 + bool unique_lag = true;
244 + id = dsa_lag_id(ds->dst, lag);
246 + switch (info->hash_type) {
247 + case NETDEV_LAG_HASH_L23:
248 + hash |= QCA8K_TRUNK_HASH_SIP_EN;
249 + hash |= QCA8K_TRUNK_HASH_DIP_EN;
251 + case NETDEV_LAG_HASH_L2:
252 + hash |= QCA8K_TRUNK_HASH_SA_EN;
253 + hash |= QCA8K_TRUNK_HASH_DA_EN;
255 + default: /* We should NEVER reach this */
256 + return -EOPNOTSUPP;
259 + /* Check if we are the unique configured LAG */
260 + dsa_lags_foreach_id(i, ds->dst)
261 + if (i != id && dsa_lag_dev(ds->dst, i)) {
262 + unique_lag = false;
266 + /* Hash Mode is global. Make sure the same Hash Mode
267 + * is set to all the 4 possible lag.
268 + * If we are the unique LAG we can set whatever hash
270 + * To change hash mode it's needed to remove all LAG
271 + * and change the mode with the latest.
274 + priv->lag_hash_mode = hash;
275 + } else if (priv->lag_hash_mode != hash) {
276 + netdev_err(lag, "Error: Mismatched Hash Mode across different lag is not supported\n");
277 + return -EOPNOTSUPP;
280 + return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
281 + QCA8K_TRUNK_HASH_MASK, hash);
284 +static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
285 + struct net_device *lag, bool delete)
287 + struct qca8k_priv *priv = ds->priv;
291 + id = dsa_lag_id(ds->dst, lag);
293 + /* Read current port member */
294 + ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
298 + /* Shift val to the correct trunk */
299 + val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
300 + val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
306 + /* Update port member. With empty portmap disable trunk */
307 + ret = regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0,
308 + QCA8K_REG_GOL_TRUNK_MEMBER(id) |
309 + QCA8K_REG_GOL_TRUNK_EN(id),
310 + !val << QCA8K_REG_GOL_TRUNK_SHIFT(id) |
311 + val << QCA8K_REG_GOL_TRUNK_SHIFT(id));
313 + /* Search empty member if adding or port on deleting */
314 + for (i = 0; i < QCA8K_NUM_PORTS_FOR_LAG; i++) {
315 + ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id), &val);
319 + val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
320 + val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
323 + /* If port flagged to be disabled assume this member is
326 + if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
329 + val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
333 + /* If port flagged to be enabled assume this member is
336 + if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
340 + /* We have found the member to add/remove */
344 + /* Set port in the correct port mask or disable port if in delete mode */
345 + return regmap_update_bits(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL(id),
346 + QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN(id, i) |
347 + QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT(id, i),
348 + !delete << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i) |
349 + port << QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i));
352 +int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
353 + struct netdev_lag_upper_info *info)
357 + if (!qca8k_lag_can_offload(ds, lag, info))
358 + return -EOPNOTSUPP;
360 + ret = qca8k_lag_setup_hash(ds, lag, info);
364 + return qca8k_lag_refresh_portmap(ds, port, lag, false);
367 +int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
368 + struct net_device *lag)
370 + return qca8k_lag_refresh_portmap(ds, port, lag, true);
372 --- a/drivers/net/dsa/qca/qca8k.h
373 +++ b/drivers/net/dsa/qca/qca8k.h
374 @@ -495,4 +495,10 @@ int qca8k_port_vlan_add(struct dsa_switc
375 int qca8k_port_vlan_del(struct dsa_switch *ds, int port,
376 const struct switchdev_obj_port_vlan *vlan);
378 +/* Common port LAG function */
379 +int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
380 + struct netdev_lag_upper_info *info);
381 +int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
382 + struct net_device *lag);
384 #endif /* __QCA8K_H */