1802b17eaad731e3d7d2a5be7fefbc9811dfc045
[openwrt/staging/blogic.git] /
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
5
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
8 drivers.
9
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>
13 ---
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(-)
18
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;
23 }
24
25 -static bool
26 -qca8k_lag_can_offload(struct dsa_switch *ds,
27 - struct net_device *lag,
28 - struct netdev_lag_upper_info *info)
29 -{
30 - struct dsa_port *dp;
31 - int id, members = 0;
32 -
33 - id = dsa_lag_id(ds->dst, lag);
34 - if (id < 0 || id >= ds->num_lag_ids)
35 - return false;
36 -
37 - dsa_lag_foreach_port(dp, ds->dst, lag)
38 - /* Includes the port joining the LAG */
39 - members++;
40 -
41 - if (members > QCA8K_NUM_PORTS_FOR_LAG)
42 - return false;
43 -
44 - if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
45 - return false;
46 -
47 - if (info->hash_type != NETDEV_LAG_HASH_L2 &&
48 - info->hash_type != NETDEV_LAG_HASH_L23)
49 - return false;
50 -
51 - return true;
52 -}
53 -
54 -static int
55 -qca8k_lag_setup_hash(struct dsa_switch *ds,
56 - struct net_device *lag,
57 - struct netdev_lag_upper_info *info)
58 -{
59 - struct qca8k_priv *priv = ds->priv;
60 - bool unique_lag = true;
61 - u32 hash = 0;
62 - int i, id;
63 -
64 - id = dsa_lag_id(ds->dst, lag);
65 -
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;
70 - fallthrough;
71 - case NETDEV_LAG_HASH_L2:
72 - hash |= QCA8K_TRUNK_HASH_SA_EN;
73 - hash |= QCA8K_TRUNK_HASH_DA_EN;
74 - break;
75 - default: /* We should NEVER reach this */
76 - return -EOPNOTSUPP;
77 - }
78 -
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)) {
82 - unique_lag = false;
83 - break;
84 - }
85 -
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
89 - * mode we want.
90 - * To change hash mode it's needed to remove all LAG
91 - * and change the mode with the latest.
92 - */
93 - if (unique_lag) {
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");
97 - return -EOPNOTSUPP;
98 - }
99 -
100 - return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
101 - QCA8K_TRUNK_HASH_MASK, hash);
102 -}
103 -
104 -static int
105 -qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
106 - struct net_device *lag, bool delete)
107 -{
108 - struct qca8k_priv *priv = ds->priv;
109 - int ret, id, i;
110 - u32 val;
111 -
112 - id = dsa_lag_id(ds->dst, lag);
113 -
114 - /* Read current port member */
115 - ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
116 - if (ret)
117 - return ret;
118 -
119 - /* Shift val to the correct trunk */
120 - val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
121 - val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
122 - if (delete)
123 - val &= ~BIT(port);
124 - else
125 - val |= BIT(port);
126 -
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));
133 -
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);
137 - if (ret)
138 - return ret;
139 -
140 - val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
141 - val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
142 -
143 - if (delete) {
144 - /* If port flagged to be disabled assume this member is
145 - * empty
146 - */
147 - if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
148 - continue;
149 -
150 - val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
151 - if (val != port)
152 - continue;
153 - } else {
154 - /* If port flagged to be enabled assume this member is
155 - * already set
156 - */
157 - if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
158 - continue;
159 - }
160 -
161 - /* We have found the member to add/remove */
162 - break;
163 - }
164 -
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));
171 -}
172 -
173 -static int
174 -qca8k_port_lag_join(struct dsa_switch *ds, int port,
175 - struct net_device *lag,
176 - struct netdev_lag_upper_info *info)
177 -{
178 - int ret;
179 -
180 - if (!qca8k_lag_can_offload(ds, lag, info))
181 - return -EOPNOTSUPP;
182 -
183 - ret = qca8k_lag_setup_hash(ds, lag, info);
184 - if (ret)
185 - return ret;
186 -
187 - return qca8k_lag_refresh_portmap(ds, port, lag, false);
188 -}
189 -
190 -static int
191 -qca8k_port_lag_leave(struct dsa_switch *ds, int port,
192 - struct net_device *lag)
193 -{
194 - return qca8k_lag_refresh_portmap(ds, port, lag, true);
195 -}
196 -
197 static void
198 qca8k_master_change(struct dsa_switch *ds, const struct net_device *master,
199 bool operational)
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
203
204 return ret;
205 }
206 +
207 +static bool qca8k_lag_can_offload(struct dsa_switch *ds,
208 + struct net_device *lag,
209 + struct netdev_lag_upper_info *info)
210 +{
211 + struct dsa_port *dp;
212 + int id, members = 0;
213 +
214 + id = dsa_lag_id(ds->dst, lag);
215 + if (id < 0 || id >= ds->num_lag_ids)
216 + return false;
217 +
218 + dsa_lag_foreach_port(dp, ds->dst, lag)
219 + /* Includes the port joining the LAG */
220 + members++;
221 +
222 + if (members > QCA8K_NUM_PORTS_FOR_LAG)
223 + return false;
224 +
225 + if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH)
226 + return false;
227 +
228 + if (info->hash_type != NETDEV_LAG_HASH_L2 &&
229 + info->hash_type != NETDEV_LAG_HASH_L23)
230 + return false;
231 +
232 + return true;
233 +}
234 +
235 +static int qca8k_lag_setup_hash(struct dsa_switch *ds,
236 + struct net_device *lag,
237 + struct netdev_lag_upper_info *info)
238 +{
239 + struct qca8k_priv *priv = ds->priv;
240 + bool unique_lag = true;
241 + u32 hash = 0;
242 + int i, id;
243 +
244 + id = dsa_lag_id(ds->dst, lag);
245 +
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;
250 + fallthrough;
251 + case NETDEV_LAG_HASH_L2:
252 + hash |= QCA8K_TRUNK_HASH_SA_EN;
253 + hash |= QCA8K_TRUNK_HASH_DA_EN;
254 + break;
255 + default: /* We should NEVER reach this */
256 + return -EOPNOTSUPP;
257 + }
258 +
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;
263 + break;
264 + }
265 +
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
269 + * mode we want.
270 + * To change hash mode it's needed to remove all LAG
271 + * and change the mode with the latest.
272 + */
273 + if (unique_lag) {
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;
278 + }
279 +
280 + return regmap_update_bits(priv->regmap, QCA8K_TRUNK_HASH_EN_CTRL,
281 + QCA8K_TRUNK_HASH_MASK, hash);
282 +}
283 +
284 +static int qca8k_lag_refresh_portmap(struct dsa_switch *ds, int port,
285 + struct net_device *lag, bool delete)
286 +{
287 + struct qca8k_priv *priv = ds->priv;
288 + int ret, id, i;
289 + u32 val;
290 +
291 + id = dsa_lag_id(ds->dst, lag);
292 +
293 + /* Read current port member */
294 + ret = regmap_read(priv->regmap, QCA8K_REG_GOL_TRUNK_CTRL0, &val);
295 + if (ret)
296 + return ret;
297 +
298 + /* Shift val to the correct trunk */
299 + val >>= QCA8K_REG_GOL_TRUNK_SHIFT(id);
300 + val &= QCA8K_REG_GOL_TRUNK_MEMBER_MASK;
301 + if (delete)
302 + val &= ~BIT(port);
303 + else
304 + val |= BIT(port);
305 +
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));
312 +
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);
316 + if (ret)
317 + return ret;
318 +
319 + val >>= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_SHIFT(id, i);
320 + val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_MASK;
321 +
322 + if (delete) {
323 + /* If port flagged to be disabled assume this member is
324 + * empty
325 + */
326 + if (val != QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
327 + continue;
328 +
329 + val &= QCA8K_REG_GOL_TRUNK_ID_MEM_ID_PORT_MASK;
330 + if (val != port)
331 + continue;
332 + } else {
333 + /* If port flagged to be enabled assume this member is
334 + * already set
335 + */
336 + if (val == QCA8K_REG_GOL_TRUNK_ID_MEM_ID_EN_MASK)
337 + continue;
338 + }
339 +
340 + /* We have found the member to add/remove */
341 + break;
342 + }
343 +
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));
350 +}
351 +
352 +int qca8k_port_lag_join(struct dsa_switch *ds, int port, struct net_device *lag,
353 + struct netdev_lag_upper_info *info)
354 +{
355 + int ret;
356 +
357 + if (!qca8k_lag_can_offload(ds, lag, info))
358 + return -EOPNOTSUPP;
359 +
360 + ret = qca8k_lag_setup_hash(ds, lag, info);
361 + if (ret)
362 + return ret;
363 +
364 + return qca8k_lag_refresh_portmap(ds, port, lag, false);
365 +}
366 +
367 +int qca8k_port_lag_leave(struct dsa_switch *ds, int port,
368 + struct net_device *lag)
369 +{
370 + return qca8k_lag_refresh_portmap(ds, port, lag, true);
371 +}
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);
377
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);
383 +
384 #endif /* __QCA8K_H */