1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (C) 2020 NovaTech LLC |
4 | * George McCollister <george.mccollister@gmail.com> |
5 | */ |
6 | |
7 | #include <net/dsa.h> |
8 | #include <linux/etherdevice.h> |
9 | #include <linux/if_bridge.h> |
10 | #include <linux/of.h> |
11 | #include <linux/netdev_features.h> |
12 | #include <linux/if_hsr.h> |
13 | #include "xrs700x.h" |
14 | #include "xrs700x_reg.h" |
15 | |
16 | #define XRS700X_MIB_INTERVAL msecs_to_jiffies(3000) |
17 | |
18 | #define XRS7000X_SUPPORTED_HSR_FEATURES \ |
19 | (NETIF_F_HW_HSR_TAG_INS | NETIF_F_HW_HSR_TAG_RM | \ |
20 | NETIF_F_HW_HSR_FWD | NETIF_F_HW_HSR_DUP) |
21 | |
22 | #define XRS7003E_ID 0x100 |
23 | #define XRS7003F_ID 0x101 |
24 | #define XRS7004E_ID 0x200 |
25 | #define XRS7004F_ID 0x201 |
26 | |
27 | const struct xrs700x_info xrs7003e_info = {XRS7003E_ID, "XRS7003E" , 3}; |
28 | EXPORT_SYMBOL(xrs7003e_info); |
29 | |
30 | const struct xrs700x_info xrs7003f_info = {XRS7003F_ID, "XRS7003F" , 3}; |
31 | EXPORT_SYMBOL(xrs7003f_info); |
32 | |
33 | const struct xrs700x_info xrs7004e_info = {XRS7004E_ID, "XRS7004E" , 4}; |
34 | EXPORT_SYMBOL(xrs7004e_info); |
35 | |
36 | const struct xrs700x_info xrs7004f_info = {XRS7004F_ID, "XRS7004F" , 4}; |
37 | EXPORT_SYMBOL(xrs7004f_info); |
38 | |
39 | struct xrs700x_regfield { |
40 | struct reg_field rf; |
41 | struct regmap_field **rmf; |
42 | }; |
43 | |
44 | struct xrs700x_mib { |
45 | unsigned int offset; |
46 | const char *name; |
47 | int stats64_offset; |
48 | }; |
49 | |
50 | #define XRS700X_MIB_ETHTOOL_ONLY(o, n) {o, n, -1} |
51 | #define XRS700X_MIB(o, n, m) {o, n, offsetof(struct rtnl_link_stats64, m)} |
52 | |
53 | static const struct xrs700x_mib xrs700x_mibs[] = { |
54 | XRS700X_MIB(XRS_RX_GOOD_OCTETS_L, "rx_good_octets" , rx_bytes), |
55 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_BAD_OCTETS_L, "rx_bad_octets" ), |
56 | XRS700X_MIB(XRS_RX_UNICAST_L, "rx_unicast" , rx_packets), |
57 | XRS700X_MIB(XRS_RX_BROADCAST_L, "rx_broadcast" , rx_packets), |
58 | XRS700X_MIB(XRS_RX_MULTICAST_L, "rx_multicast" , multicast), |
59 | XRS700X_MIB(XRS_RX_UNDERSIZE_L, "rx_undersize" , rx_length_errors), |
60 | XRS700X_MIB(XRS_RX_FRAGMENTS_L, "rx_fragments" , rx_length_errors), |
61 | XRS700X_MIB(XRS_RX_OVERSIZE_L, "rx_oversize" , rx_length_errors), |
62 | XRS700X_MIB(XRS_RX_JABBER_L, "rx_jabber" , rx_length_errors), |
63 | XRS700X_MIB(XRS_RX_ERR_L, "rx_err" , rx_errors), |
64 | XRS700X_MIB(XRS_RX_CRC_L, "rx_crc" , rx_crc_errors), |
65 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_64_L, "rx_64" ), |
66 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_65_127_L, "rx_65_127" ), |
67 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_128_255_L, "rx_128_255" ), |
68 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_256_511_L, "rx_256_511" ), |
69 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_512_1023_L, "rx_512_1023" ), |
70 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_1024_1536_L, "rx_1024_1536" ), |
71 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_HSR_PRP_L, "rx_hsr_prp" ), |
72 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_WRONGLAN_L, "rx_wronglan" ), |
73 | XRS700X_MIB_ETHTOOL_ONLY(XRS_RX_DUPLICATE_L, "rx_duplicate" ), |
74 | XRS700X_MIB(XRS_TX_OCTETS_L, "tx_octets" , tx_bytes), |
75 | XRS700X_MIB(XRS_TX_UNICAST_L, "tx_unicast" , tx_packets), |
76 | XRS700X_MIB(XRS_TX_BROADCAST_L, "tx_broadcast" , tx_packets), |
77 | XRS700X_MIB(XRS_TX_MULTICAST_L, "tx_multicast" , tx_packets), |
78 | XRS700X_MIB_ETHTOOL_ONLY(XRS_TX_HSR_PRP_L, "tx_hsr_prp" ), |
79 | XRS700X_MIB(XRS_PRIQ_DROP_L, "priq_drop" , tx_dropped), |
80 | XRS700X_MIB(XRS_EARLY_DROP_L, "early_drop" , tx_dropped), |
81 | }; |
82 | |
83 | static const u8 eth_hsrsup_addr[ETH_ALEN] = { |
84 | 0x01, 0x15, 0x4e, 0x00, 0x01, 0x00}; |
85 | |
86 | static void xrs700x_get_strings(struct dsa_switch *ds, int port, |
87 | u32 stringset, u8 *data) |
88 | { |
89 | int i; |
90 | |
91 | if (stringset != ETH_SS_STATS) |
92 | return; |
93 | |
94 | for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) { |
95 | strscpy(data, xrs700x_mibs[i].name, ETH_GSTRING_LEN); |
96 | data += ETH_GSTRING_LEN; |
97 | } |
98 | } |
99 | |
100 | static int xrs700x_get_sset_count(struct dsa_switch *ds, int port, int sset) |
101 | { |
102 | if (sset != ETH_SS_STATS) |
103 | return -EOPNOTSUPP; |
104 | |
105 | return ARRAY_SIZE(xrs700x_mibs); |
106 | } |
107 | |
108 | static void xrs700x_read_port_counters(struct xrs700x *priv, int port) |
109 | { |
110 | struct xrs700x_port *p = &priv->ports[port]; |
111 | struct rtnl_link_stats64 stats; |
112 | unsigned long flags; |
113 | int i; |
114 | |
115 | memset(&stats, 0, sizeof(stats)); |
116 | |
117 | mutex_lock(&p->mib_mutex); |
118 | |
119 | /* Capture counter values */ |
120 | regmap_write(map: priv->regmap, XRS_CNT_CTRL(port), val: 1); |
121 | |
122 | for (i = 0; i < ARRAY_SIZE(xrs700x_mibs); i++) { |
123 | unsigned int high = 0, low = 0, reg; |
124 | |
125 | reg = xrs700x_mibs[i].offset + XRS_PORT_OFFSET * port; |
126 | regmap_read(map: priv->regmap, reg, val: &low); |
127 | regmap_read(map: priv->regmap, reg: reg + 2, val: &high); |
128 | |
129 | p->mib_data[i] += (high << 16) | low; |
130 | |
131 | if (xrs700x_mibs[i].stats64_offset >= 0) { |
132 | u8 *s = (u8 *)&stats + xrs700x_mibs[i].stats64_offset; |
133 | *(u64 *)s += p->mib_data[i]; |
134 | } |
135 | } |
136 | |
137 | /* multicast must be added to rx_packets (which already includes |
138 | * unicast and broadcast) |
139 | */ |
140 | stats.rx_packets += stats.multicast; |
141 | |
142 | flags = u64_stats_update_begin_irqsave(syncp: &p->syncp); |
143 | p->stats64 = stats; |
144 | u64_stats_update_end_irqrestore(syncp: &p->syncp, flags); |
145 | |
146 | mutex_unlock(lock: &p->mib_mutex); |
147 | } |
148 | |
149 | static void xrs700x_mib_work(struct work_struct *work) |
150 | { |
151 | struct xrs700x *priv = container_of(work, struct xrs700x, |
152 | mib_work.work); |
153 | int i; |
154 | |
155 | for (i = 0; i < priv->ds->num_ports; i++) |
156 | xrs700x_read_port_counters(priv, port: i); |
157 | |
158 | schedule_delayed_work(dwork: &priv->mib_work, XRS700X_MIB_INTERVAL); |
159 | } |
160 | |
161 | static void xrs700x_get_ethtool_stats(struct dsa_switch *ds, int port, |
162 | u64 *data) |
163 | { |
164 | struct xrs700x *priv = ds->priv; |
165 | struct xrs700x_port *p = &priv->ports[port]; |
166 | |
167 | xrs700x_read_port_counters(priv, port); |
168 | |
169 | mutex_lock(&p->mib_mutex); |
170 | memcpy(data, p->mib_data, sizeof(*data) * ARRAY_SIZE(xrs700x_mibs)); |
171 | mutex_unlock(lock: &p->mib_mutex); |
172 | } |
173 | |
174 | static void xrs700x_get_stats64(struct dsa_switch *ds, int port, |
175 | struct rtnl_link_stats64 *s) |
176 | { |
177 | struct xrs700x *priv = ds->priv; |
178 | struct xrs700x_port *p = &priv->ports[port]; |
179 | unsigned int start; |
180 | |
181 | do { |
182 | start = u64_stats_fetch_begin(syncp: &p->syncp); |
183 | *s = p->stats64; |
184 | } while (u64_stats_fetch_retry(syncp: &p->syncp, start)); |
185 | } |
186 | |
187 | static int xrs700x_setup_regmap_range(struct xrs700x *priv) |
188 | { |
189 | struct xrs700x_regfield regfields[] = { |
190 | { |
191 | .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 0, 1, |
192 | priv->ds->num_ports, |
193 | XRS_PORT_OFFSET), |
194 | .rmf = &priv->ps_forward |
195 | }, |
196 | { |
197 | .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 2, 3, |
198 | priv->ds->num_ports, |
199 | XRS_PORT_OFFSET), |
200 | .rmf = &priv->ps_management |
201 | }, |
202 | { |
203 | .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 4, 9, |
204 | priv->ds->num_ports, |
205 | XRS_PORT_OFFSET), |
206 | .rmf = &priv->ps_sel_speed |
207 | }, |
208 | { |
209 | .rf = REG_FIELD_ID(XRS_PORT_STATE(0), 10, 11, |
210 | priv->ds->num_ports, |
211 | XRS_PORT_OFFSET), |
212 | .rmf = &priv->ps_cur_speed |
213 | } |
214 | }; |
215 | int i = 0; |
216 | |
217 | for (; i < ARRAY_SIZE(regfields); i++) { |
218 | *regfields[i].rmf = devm_regmap_field_alloc(dev: priv->dev, |
219 | regmap: priv->regmap, |
220 | reg_field: regfields[i].rf); |
221 | if (IS_ERR(ptr: *regfields[i].rmf)) |
222 | return PTR_ERR(ptr: *regfields[i].rmf); |
223 | } |
224 | |
225 | return 0; |
226 | } |
227 | |
228 | static enum dsa_tag_protocol xrs700x_get_tag_protocol(struct dsa_switch *ds, |
229 | int port, |
230 | enum dsa_tag_protocol m) |
231 | { |
232 | return DSA_TAG_PROTO_XRS700X; |
233 | } |
234 | |
235 | static int xrs700x_reset(struct dsa_switch *ds) |
236 | { |
237 | struct xrs700x *priv = ds->priv; |
238 | unsigned int val; |
239 | int ret; |
240 | |
241 | ret = regmap_write(map: priv->regmap, XRS_GENERAL, XRS_GENERAL_RESET); |
242 | if (ret) |
243 | goto error; |
244 | |
245 | ret = regmap_read_poll_timeout(priv->regmap, XRS_GENERAL, |
246 | val, !(val & XRS_GENERAL_RESET), |
247 | 10, 1000); |
248 | error: |
249 | if (ret) { |
250 | dev_err_ratelimited(priv->dev, "error resetting switch: %d\n" , |
251 | ret); |
252 | } |
253 | |
254 | return ret; |
255 | } |
256 | |
257 | static void xrs700x_port_stp_state_set(struct dsa_switch *ds, int port, |
258 | u8 state) |
259 | { |
260 | struct xrs700x *priv = ds->priv; |
261 | unsigned int bpdus = 1; |
262 | unsigned int val; |
263 | |
264 | switch (state) { |
265 | case BR_STATE_DISABLED: |
266 | bpdus = 0; |
267 | fallthrough; |
268 | case BR_STATE_BLOCKING: |
269 | case BR_STATE_LISTENING: |
270 | val = XRS_PORT_DISABLED; |
271 | break; |
272 | case BR_STATE_LEARNING: |
273 | val = XRS_PORT_LEARNING; |
274 | break; |
275 | case BR_STATE_FORWARDING: |
276 | val = XRS_PORT_FORWARDING; |
277 | break; |
278 | default: |
279 | dev_err(ds->dev, "invalid STP state: %d\n" , state); |
280 | return; |
281 | } |
282 | |
283 | regmap_fields_write(field: priv->ps_forward, id: port, val); |
284 | |
285 | /* Enable/disable inbound policy added by xrs700x_port_add_bpdu_ipf() |
286 | * which allows BPDU forwarding to the CPU port when the front facing |
287 | * port is in disabled/learning state. |
288 | */ |
289 | regmap_update_bits(map: priv->regmap, XRS_ETH_ADDR_CFG(port, 0), mask: 1, val: bpdus); |
290 | |
291 | dev_dbg_ratelimited(priv->dev, "%s - port: %d, state: %u, val: 0x%x\n" , |
292 | __func__, port, state, val); |
293 | } |
294 | |
295 | /* Add an inbound policy filter which matches the BPDU destination MAC |
296 | * and forwards to the CPU port. Leave the policy disabled, it will be |
297 | * enabled as needed. |
298 | */ |
299 | static int xrs700x_port_add_bpdu_ipf(struct dsa_switch *ds, int port) |
300 | { |
301 | struct xrs700x *priv = ds->priv; |
302 | unsigned int val = 0; |
303 | int i = 0; |
304 | int ret; |
305 | |
306 | /* Compare all 48 bits of the destination MAC address. */ |
307 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_CFG(port, 0), val: 48 << 2); |
308 | if (ret) |
309 | return ret; |
310 | |
311 | /* match BPDU destination 01:80:c2:00:00:00 */ |
312 | for (i = 0; i < sizeof(eth_stp_addr); i += 2) { |
313 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_0(port, 0) + i, |
314 | eth_stp_addr[i] | |
315 | (eth_stp_addr[i + 1] << 8)); |
316 | if (ret) |
317 | return ret; |
318 | } |
319 | |
320 | /* Mirror BPDU to CPU port */ |
321 | for (i = 0; i < ds->num_ports; i++) { |
322 | if (dsa_is_cpu_port(ds, p: i)) |
323 | val |= BIT(i); |
324 | } |
325 | |
326 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_FWD_MIRROR(port, 0), val); |
327 | if (ret) |
328 | return ret; |
329 | |
330 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_FWD_ALLOW(port, 0), val: 0); |
331 | if (ret) |
332 | return ret; |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | /* Add an inbound policy filter which matches the HSR/PRP supervision MAC |
338 | * range and forwards to the CPU port without discarding duplicates. |
339 | * This is required to correctly populate the HSR/PRP node_table. |
340 | * Leave the policy disabled, it will be enabled as needed. |
341 | */ |
342 | static int xrs700x_port_add_hsrsup_ipf(struct dsa_switch *ds, int port, |
343 | int fwdport) |
344 | { |
345 | struct xrs700x *priv = ds->priv; |
346 | unsigned int val = 0; |
347 | int i = 0; |
348 | int ret; |
349 | |
350 | /* Compare 40 bits of the destination MAC address. */ |
351 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_CFG(port, 1), val: 40 << 2); |
352 | if (ret) |
353 | return ret; |
354 | |
355 | /* match HSR/PRP supervision destination 01:15:4e:00:01:XX */ |
356 | for (i = 0; i < sizeof(eth_hsrsup_addr); i += 2) { |
357 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_0(port, 1) + i, |
358 | val: eth_hsrsup_addr[i] | |
359 | (eth_hsrsup_addr[i + 1] << 8)); |
360 | if (ret) |
361 | return ret; |
362 | } |
363 | |
364 | /* Mirror HSR/PRP supervision to CPU port */ |
365 | for (i = 0; i < ds->num_ports; i++) { |
366 | if (dsa_is_cpu_port(ds, p: i)) |
367 | val |= BIT(i); |
368 | } |
369 | |
370 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_FWD_MIRROR(port, 1), val); |
371 | if (ret) |
372 | return ret; |
373 | |
374 | if (fwdport >= 0) |
375 | val |= BIT(fwdport); |
376 | |
377 | /* Allow must be set prevent duplicate discard */ |
378 | ret = regmap_write(map: priv->regmap, XRS_ETH_ADDR_FWD_ALLOW(port, 1), val); |
379 | if (ret) |
380 | return ret; |
381 | |
382 | return 0; |
383 | } |
384 | |
385 | static int xrs700x_port_setup(struct dsa_switch *ds, int port) |
386 | { |
387 | bool cpu_port = dsa_is_cpu_port(ds, p: port); |
388 | struct xrs700x *priv = ds->priv; |
389 | unsigned int val = 0; |
390 | int ret, i; |
391 | |
392 | xrs700x_port_stp_state_set(ds, port, BR_STATE_DISABLED); |
393 | |
394 | /* Disable forwarding to non-CPU ports */ |
395 | for (i = 0; i < ds->num_ports; i++) { |
396 | if (!dsa_is_cpu_port(ds, p: i)) |
397 | val |= BIT(i); |
398 | } |
399 | |
400 | /* 1 = Disable forwarding to the port */ |
401 | ret = regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(port), val); |
402 | if (ret) |
403 | return ret; |
404 | |
405 | val = cpu_port ? XRS_PORT_MODE_MANAGEMENT : XRS_PORT_MODE_NORMAL; |
406 | ret = regmap_fields_write(field: priv->ps_management, id: port, val); |
407 | if (ret) |
408 | return ret; |
409 | |
410 | if (!cpu_port) { |
411 | ret = xrs700x_port_add_bpdu_ipf(ds, port); |
412 | if (ret) |
413 | return ret; |
414 | } |
415 | |
416 | return 0; |
417 | } |
418 | |
419 | static int xrs700x_setup(struct dsa_switch *ds) |
420 | { |
421 | struct xrs700x *priv = ds->priv; |
422 | int ret, i; |
423 | |
424 | ret = xrs700x_reset(ds); |
425 | if (ret) |
426 | return ret; |
427 | |
428 | for (i = 0; i < ds->num_ports; i++) { |
429 | ret = xrs700x_port_setup(ds, port: i); |
430 | if (ret) |
431 | return ret; |
432 | } |
433 | |
434 | schedule_delayed_work(dwork: &priv->mib_work, XRS700X_MIB_INTERVAL); |
435 | |
436 | return 0; |
437 | } |
438 | |
439 | static void xrs700x_teardown(struct dsa_switch *ds) |
440 | { |
441 | struct xrs700x *priv = ds->priv; |
442 | |
443 | cancel_delayed_work_sync(dwork: &priv->mib_work); |
444 | } |
445 | |
446 | static void xrs700x_phylink_get_caps(struct dsa_switch *ds, int port, |
447 | struct phylink_config *config) |
448 | { |
449 | switch (port) { |
450 | case 0: |
451 | __set_bit(PHY_INTERFACE_MODE_RMII, |
452 | config->supported_interfaces); |
453 | config->mac_capabilities = MAC_10FD | MAC_100FD; |
454 | break; |
455 | |
456 | case 1: |
457 | case 2: |
458 | case 3: |
459 | phy_interface_set_rgmii(intf: config->supported_interfaces); |
460 | config->mac_capabilities = MAC_10FD | MAC_100FD | MAC_1000FD; |
461 | break; |
462 | |
463 | default: |
464 | dev_err(ds->dev, "Unsupported port: %i\n" , port); |
465 | break; |
466 | } |
467 | } |
468 | |
469 | static void xrs700x_mac_link_up(struct dsa_switch *ds, int port, |
470 | unsigned int mode, phy_interface_t interface, |
471 | struct phy_device *phydev, |
472 | int speed, int duplex, |
473 | bool tx_pause, bool rx_pause) |
474 | { |
475 | struct xrs700x *priv = ds->priv; |
476 | unsigned int val; |
477 | |
478 | switch (speed) { |
479 | case SPEED_1000: |
480 | val = XRS_PORT_SPEED_1000; |
481 | break; |
482 | case SPEED_100: |
483 | val = XRS_PORT_SPEED_100; |
484 | break; |
485 | case SPEED_10: |
486 | val = XRS_PORT_SPEED_10; |
487 | break; |
488 | default: |
489 | return; |
490 | } |
491 | |
492 | regmap_fields_write(field: priv->ps_sel_speed, id: port, val); |
493 | |
494 | dev_dbg_ratelimited(priv->dev, "%s: port: %d mode: %u speed: %u\n" , |
495 | __func__, port, mode, speed); |
496 | } |
497 | |
498 | static int xrs700x_bridge_common(struct dsa_switch *ds, int port, |
499 | struct dsa_bridge bridge, bool join) |
500 | { |
501 | unsigned int i, cpu_mask = 0, mask = 0; |
502 | struct xrs700x *priv = ds->priv; |
503 | int ret; |
504 | |
505 | for (i = 0; i < ds->num_ports; i++) { |
506 | if (dsa_is_cpu_port(ds, p: i)) |
507 | continue; |
508 | |
509 | cpu_mask |= BIT(i); |
510 | |
511 | if (dsa_port_offloads_bridge(dp: dsa_to_port(ds, p: i), bridge: &bridge)) |
512 | continue; |
513 | |
514 | mask |= BIT(i); |
515 | } |
516 | |
517 | for (i = 0; i < ds->num_ports; i++) { |
518 | if (!dsa_port_offloads_bridge(dp: dsa_to_port(ds, p: i), bridge: &bridge)) |
519 | continue; |
520 | |
521 | /* 1 = Disable forwarding to the port */ |
522 | ret = regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(i), val: mask); |
523 | if (ret) |
524 | return ret; |
525 | } |
526 | |
527 | if (!join) { |
528 | ret = regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(port), |
529 | val: cpu_mask); |
530 | if (ret) |
531 | return ret; |
532 | } |
533 | |
534 | return 0; |
535 | } |
536 | |
537 | static int xrs700x_bridge_join(struct dsa_switch *ds, int port, |
538 | struct dsa_bridge bridge, bool *tx_fwd_offload, |
539 | struct netlink_ext_ack *extack) |
540 | { |
541 | return xrs700x_bridge_common(ds, port, bridge, join: true); |
542 | } |
543 | |
544 | static void xrs700x_bridge_leave(struct dsa_switch *ds, int port, |
545 | struct dsa_bridge bridge) |
546 | { |
547 | xrs700x_bridge_common(ds, port, bridge, join: false); |
548 | } |
549 | |
550 | static int xrs700x_hsr_join(struct dsa_switch *ds, int port, |
551 | struct net_device *hsr, |
552 | struct netlink_ext_ack *extack) |
553 | { |
554 | unsigned int val = XRS_HSR_CFG_HSR_PRP; |
555 | struct dsa_port *partner = NULL, *dp; |
556 | struct xrs700x *priv = ds->priv; |
557 | struct net_device *user; |
558 | int ret, i, hsr_pair[2]; |
559 | enum hsr_version ver; |
560 | bool fwd = false; |
561 | |
562 | ret = hsr_get_version(dev: hsr, ver: &ver); |
563 | if (ret) |
564 | return ret; |
565 | |
566 | if (port != 1 && port != 2) { |
567 | NL_SET_ERR_MSG_MOD(extack, |
568 | "Only ports 1 and 2 can offload HSR/PRP" ); |
569 | return -EOPNOTSUPP; |
570 | } |
571 | |
572 | if (ver == HSR_V1) { |
573 | val |= XRS_HSR_CFG_HSR; |
574 | } else if (ver == PRP_V1) { |
575 | val |= XRS_HSR_CFG_PRP; |
576 | } else { |
577 | NL_SET_ERR_MSG_MOD(extack, |
578 | "Only HSR v1 and PRP v1 can be offloaded" ); |
579 | return -EOPNOTSUPP; |
580 | } |
581 | |
582 | dsa_hsr_foreach_port(dp, ds, hsr) { |
583 | if (dp->index != port) { |
584 | partner = dp; |
585 | break; |
586 | } |
587 | } |
588 | |
589 | /* We can't enable redundancy on the switch until both |
590 | * redundant ports have signed up. |
591 | */ |
592 | if (!partner) |
593 | return 0; |
594 | |
595 | regmap_fields_write(field: priv->ps_forward, id: partner->index, |
596 | XRS_PORT_DISABLED); |
597 | regmap_fields_write(field: priv->ps_forward, id: port, XRS_PORT_DISABLED); |
598 | |
599 | regmap_write(map: priv->regmap, XRS_HSR_CFG(partner->index), |
600 | val: val | XRS_HSR_CFG_LANID_A); |
601 | regmap_write(map: priv->regmap, XRS_HSR_CFG(port), |
602 | val: val | XRS_HSR_CFG_LANID_B); |
603 | |
604 | /* Clear bits for both redundant ports (HSR only) and the CPU port to |
605 | * enable forwarding. |
606 | */ |
607 | val = GENMASK(ds->num_ports - 1, 0); |
608 | if (ver == HSR_V1) { |
609 | val &= ~BIT(partner->index); |
610 | val &= ~BIT(port); |
611 | fwd = true; |
612 | } |
613 | val &= ~BIT(dsa_upstream_port(ds, port)); |
614 | regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(partner->index), val); |
615 | regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(port), val); |
616 | |
617 | regmap_fields_write(field: priv->ps_forward, id: partner->index, |
618 | XRS_PORT_FORWARDING); |
619 | regmap_fields_write(field: priv->ps_forward, id: port, XRS_PORT_FORWARDING); |
620 | |
621 | /* Enable inbound policy which allows HSR/PRP supervision forwarding |
622 | * to the CPU port without discarding duplicates. Continue to |
623 | * forward to redundant ports when in HSR mode while discarding |
624 | * duplicates. |
625 | */ |
626 | ret = xrs700x_port_add_hsrsup_ipf(ds, port: partner->index, fwdport: fwd ? port : -1); |
627 | if (ret) |
628 | return ret; |
629 | |
630 | ret = xrs700x_port_add_hsrsup_ipf(ds, port, fwdport: fwd ? partner->index : -1); |
631 | if (ret) |
632 | return ret; |
633 | |
634 | regmap_update_bits(map: priv->regmap, |
635 | XRS_ETH_ADDR_CFG(partner->index, 1), mask: 1, val: 1); |
636 | regmap_update_bits(map: priv->regmap, XRS_ETH_ADDR_CFG(port, 1), mask: 1, val: 1); |
637 | |
638 | hsr_pair[0] = port; |
639 | hsr_pair[1] = partner->index; |
640 | for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) { |
641 | user = dsa_to_port(ds, p: hsr_pair[i])->user; |
642 | user->features |= XRS7000X_SUPPORTED_HSR_FEATURES; |
643 | } |
644 | |
645 | return 0; |
646 | } |
647 | |
648 | static int xrs700x_hsr_leave(struct dsa_switch *ds, int port, |
649 | struct net_device *hsr) |
650 | { |
651 | struct dsa_port *partner = NULL, *dp; |
652 | struct xrs700x *priv = ds->priv; |
653 | struct net_device *user; |
654 | int i, hsr_pair[2]; |
655 | unsigned int val; |
656 | |
657 | dsa_hsr_foreach_port(dp, ds, hsr) { |
658 | if (dp->index != port) { |
659 | partner = dp; |
660 | break; |
661 | } |
662 | } |
663 | |
664 | if (!partner) |
665 | return 0; |
666 | |
667 | regmap_fields_write(field: priv->ps_forward, id: partner->index, |
668 | XRS_PORT_DISABLED); |
669 | regmap_fields_write(field: priv->ps_forward, id: port, XRS_PORT_DISABLED); |
670 | |
671 | regmap_write(map: priv->regmap, XRS_HSR_CFG(partner->index), val: 0); |
672 | regmap_write(map: priv->regmap, XRS_HSR_CFG(port), val: 0); |
673 | |
674 | /* Clear bit for the CPU port to enable forwarding. */ |
675 | val = GENMASK(ds->num_ports - 1, 0); |
676 | val &= ~BIT(dsa_upstream_port(ds, port)); |
677 | regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(partner->index), val); |
678 | regmap_write(map: priv->regmap, XRS_PORT_FWD_MASK(port), val); |
679 | |
680 | regmap_fields_write(field: priv->ps_forward, id: partner->index, |
681 | XRS_PORT_FORWARDING); |
682 | regmap_fields_write(field: priv->ps_forward, id: port, XRS_PORT_FORWARDING); |
683 | |
684 | /* Disable inbound policy added by xrs700x_port_add_hsrsup_ipf() |
685 | * which allows HSR/PRP supervision forwarding to the CPU port without |
686 | * discarding duplicates. |
687 | */ |
688 | regmap_update_bits(map: priv->regmap, |
689 | XRS_ETH_ADDR_CFG(partner->index, 1), mask: 1, val: 0); |
690 | regmap_update_bits(map: priv->regmap, XRS_ETH_ADDR_CFG(port, 1), mask: 1, val: 0); |
691 | |
692 | hsr_pair[0] = port; |
693 | hsr_pair[1] = partner->index; |
694 | for (i = 0; i < ARRAY_SIZE(hsr_pair); i++) { |
695 | user = dsa_to_port(ds, p: hsr_pair[i])->user; |
696 | user->features &= ~XRS7000X_SUPPORTED_HSR_FEATURES; |
697 | } |
698 | |
699 | return 0; |
700 | } |
701 | |
702 | static const struct dsa_switch_ops xrs700x_ops = { |
703 | .get_tag_protocol = xrs700x_get_tag_protocol, |
704 | .setup = xrs700x_setup, |
705 | .teardown = xrs700x_teardown, |
706 | .port_stp_state_set = xrs700x_port_stp_state_set, |
707 | .phylink_get_caps = xrs700x_phylink_get_caps, |
708 | .phylink_mac_link_up = xrs700x_mac_link_up, |
709 | .get_strings = xrs700x_get_strings, |
710 | .get_sset_count = xrs700x_get_sset_count, |
711 | .get_ethtool_stats = xrs700x_get_ethtool_stats, |
712 | .get_stats64 = xrs700x_get_stats64, |
713 | .port_bridge_join = xrs700x_bridge_join, |
714 | .port_bridge_leave = xrs700x_bridge_leave, |
715 | .port_hsr_join = xrs700x_hsr_join, |
716 | .port_hsr_leave = xrs700x_hsr_leave, |
717 | }; |
718 | |
719 | static int xrs700x_detect(struct xrs700x *priv) |
720 | { |
721 | const struct xrs700x_info *info; |
722 | unsigned int id; |
723 | int ret; |
724 | |
725 | ret = regmap_read(map: priv->regmap, XRS_DEV_ID0, val: &id); |
726 | if (ret) { |
727 | dev_err(priv->dev, "error %d while reading switch id.\n" , |
728 | ret); |
729 | return ret; |
730 | } |
731 | |
732 | info = of_device_get_match_data(dev: priv->dev); |
733 | if (!info) |
734 | return -EINVAL; |
735 | |
736 | if (info->id == id) { |
737 | priv->ds->num_ports = info->num_ports; |
738 | dev_info(priv->dev, "%s detected.\n" , info->name); |
739 | return 0; |
740 | } |
741 | |
742 | dev_err(priv->dev, "expected switch id 0x%x but found 0x%x.\n" , |
743 | info->id, id); |
744 | |
745 | return -ENODEV; |
746 | } |
747 | |
748 | struct xrs700x *xrs700x_switch_alloc(struct device *base, void *devpriv) |
749 | { |
750 | struct dsa_switch *ds; |
751 | struct xrs700x *priv; |
752 | |
753 | ds = devm_kzalloc(dev: base, size: sizeof(*ds), GFP_KERNEL); |
754 | if (!ds) |
755 | return NULL; |
756 | |
757 | ds->dev = base; |
758 | |
759 | priv = devm_kzalloc(dev: base, size: sizeof(*priv), GFP_KERNEL); |
760 | if (!priv) |
761 | return NULL; |
762 | |
763 | INIT_DELAYED_WORK(&priv->mib_work, xrs700x_mib_work); |
764 | |
765 | ds->ops = &xrs700x_ops; |
766 | ds->priv = priv; |
767 | priv->dev = base; |
768 | |
769 | priv->ds = ds; |
770 | priv->priv = devpriv; |
771 | |
772 | return priv; |
773 | } |
774 | EXPORT_SYMBOL(xrs700x_switch_alloc); |
775 | |
776 | static int xrs700x_alloc_port_mib(struct xrs700x *priv, int port) |
777 | { |
778 | struct xrs700x_port *p = &priv->ports[port]; |
779 | |
780 | p->mib_data = devm_kcalloc(dev: priv->dev, ARRAY_SIZE(xrs700x_mibs), |
781 | size: sizeof(*p->mib_data), GFP_KERNEL); |
782 | if (!p->mib_data) |
783 | return -ENOMEM; |
784 | |
785 | mutex_init(&p->mib_mutex); |
786 | u64_stats_init(syncp: &p->syncp); |
787 | |
788 | return 0; |
789 | } |
790 | |
791 | int xrs700x_switch_register(struct xrs700x *priv) |
792 | { |
793 | int ret; |
794 | int i; |
795 | |
796 | ret = xrs700x_detect(priv); |
797 | if (ret) |
798 | return ret; |
799 | |
800 | ret = xrs700x_setup_regmap_range(priv); |
801 | if (ret) |
802 | return ret; |
803 | |
804 | priv->ports = devm_kcalloc(dev: priv->dev, n: priv->ds->num_ports, |
805 | size: sizeof(*priv->ports), GFP_KERNEL); |
806 | if (!priv->ports) |
807 | return -ENOMEM; |
808 | |
809 | for (i = 0; i < priv->ds->num_ports; i++) { |
810 | ret = xrs700x_alloc_port_mib(priv, port: i); |
811 | if (ret) |
812 | return ret; |
813 | } |
814 | |
815 | return dsa_register_switch(ds: priv->ds); |
816 | } |
817 | EXPORT_SYMBOL(xrs700x_switch_register); |
818 | |
819 | void xrs700x_switch_remove(struct xrs700x *priv) |
820 | { |
821 | dsa_unregister_switch(ds: priv->ds); |
822 | } |
823 | EXPORT_SYMBOL(xrs700x_switch_remove); |
824 | |
825 | void xrs700x_switch_shutdown(struct xrs700x *priv) |
826 | { |
827 | dsa_switch_shutdown(ds: priv->ds); |
828 | } |
829 | EXPORT_SYMBOL(xrs700x_switch_shutdown); |
830 | |
831 | MODULE_AUTHOR("George McCollister <george.mccollister@gmail.com>" ); |
832 | MODULE_DESCRIPTION("Arrow SpeedChips XRS700x DSA driver" ); |
833 | MODULE_LICENSE("GPL v2" ); |
834 | |