1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* Microsemi Ocelot Switch driver |
3 | * |
4 | * Copyright (c) 2017, 2019 Microsemi Corporation |
5 | * Copyright 2020-2021 NXP |
6 | */ |
7 | |
8 | #include <linux/if_bridge.h> |
9 | #include <linux/mrp_bridge.h> |
10 | #include <soc/mscc/ocelot_vcap.h> |
11 | #include <uapi/linux/mrp_bridge.h> |
12 | #include "ocelot.h" |
13 | #include "ocelot_vcap.h" |
14 | |
15 | static const u8 mrp_test_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x01 }; |
16 | static const u8 mrp_control_dmac[] = { 0x01, 0x15, 0x4e, 0x00, 0x00, 0x02 }; |
17 | |
18 | static int ocelot_mrp_find_partner_port(struct ocelot *ocelot, |
19 | struct ocelot_port *p) |
20 | { |
21 | int i; |
22 | |
23 | for (i = 0; i < ocelot->num_phys_ports; ++i) { |
24 | struct ocelot_port *ocelot_port = ocelot->ports[i]; |
25 | |
26 | if (!ocelot_port || p == ocelot_port) |
27 | continue; |
28 | |
29 | if (ocelot_port->mrp_ring_id == p->mrp_ring_id) |
30 | return i; |
31 | } |
32 | |
33 | return -1; |
34 | } |
35 | |
36 | static int ocelot_mrp_del_vcap(struct ocelot *ocelot, int id) |
37 | { |
38 | struct ocelot_vcap_block *block_vcap_is2; |
39 | struct ocelot_vcap_filter *filter; |
40 | |
41 | block_vcap_is2 = &ocelot->block[VCAP_IS2]; |
42 | filter = ocelot_vcap_block_find_filter_by_id(block: block_vcap_is2, cookie: id, |
43 | tc_offload: false); |
44 | if (!filter) |
45 | return 0; |
46 | |
47 | return ocelot_vcap_filter_del(ocelot, rule: filter); |
48 | } |
49 | |
50 | static int ocelot_mrp_redirect_add_vcap(struct ocelot *ocelot, int src_port, |
51 | int dst_port) |
52 | { |
53 | const u8 mrp_test_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; |
54 | struct ocelot_vcap_filter *filter; |
55 | int err; |
56 | |
57 | filter = kzalloc(size: sizeof(*filter), GFP_KERNEL); |
58 | if (!filter) |
59 | return -ENOMEM; |
60 | |
61 | filter->key_type = OCELOT_VCAP_KEY_ETYPE; |
62 | filter->prio = 1; |
63 | filter->id.cookie = OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, src_port); |
64 | filter->id.tc_offload = false; |
65 | filter->block_id = VCAP_IS2; |
66 | filter->type = OCELOT_VCAP_FILTER_OFFLOAD; |
67 | filter->ingress_port_mask = BIT(src_port); |
68 | ether_addr_copy(dst: filter->key.etype.dmac.value, src: mrp_test_dmac); |
69 | ether_addr_copy(dst: filter->key.etype.dmac.mask, src: mrp_test_mask); |
70 | filter->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; |
71 | filter->action.port_mask = BIT(dst_port); |
72 | |
73 | err = ocelot_vcap_filter_add(ocelot, rule: filter, NULL); |
74 | if (err) |
75 | kfree(objp: filter); |
76 | |
77 | return err; |
78 | } |
79 | |
80 | static void ocelot_populate_mrp_trap_key(struct ocelot_vcap_filter *filter) |
81 | { |
82 | const u8 mrp_mask[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 }; |
83 | |
84 | /* Here is possible to use control or test dmac because the mask |
85 | * doesn't cover the LSB |
86 | */ |
87 | ether_addr_copy(dst: filter->key.etype.dmac.value, src: mrp_test_dmac); |
88 | ether_addr_copy(dst: filter->key.etype.dmac.mask, src: mrp_mask); |
89 | } |
90 | |
91 | static int ocelot_mrp_trap_add(struct ocelot *ocelot, int port) |
92 | { |
93 | unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); |
94 | |
95 | return ocelot_trap_add(ocelot, port, cookie, take_ts: false, |
96 | populate: ocelot_populate_mrp_trap_key); |
97 | } |
98 | |
99 | static int ocelot_mrp_trap_del(struct ocelot *ocelot, int port) |
100 | { |
101 | unsigned long cookie = OCELOT_VCAP_IS2_MRP_TRAP(ocelot); |
102 | |
103 | return ocelot_trap_del(ocelot, port, cookie); |
104 | } |
105 | |
106 | static void ocelot_mrp_save_mac(struct ocelot *ocelot, |
107 | struct ocelot_port *port) |
108 | { |
109 | ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mac: mrp_test_dmac, |
110 | OCELOT_STANDALONE_PVID, type: ENTRYTYPE_LOCKED); |
111 | ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mac: mrp_control_dmac, |
112 | OCELOT_STANDALONE_PVID, type: ENTRYTYPE_LOCKED); |
113 | } |
114 | |
115 | static void ocelot_mrp_del_mac(struct ocelot *ocelot, |
116 | struct ocelot_port *port) |
117 | { |
118 | ocelot_mact_forget(ocelot, mac: mrp_test_dmac, OCELOT_STANDALONE_PVID); |
119 | ocelot_mact_forget(ocelot, mac: mrp_control_dmac, OCELOT_STANDALONE_PVID); |
120 | } |
121 | |
122 | int ocelot_mrp_add(struct ocelot *ocelot, int port, |
123 | const struct switchdev_obj_mrp *mrp) |
124 | { |
125 | struct ocelot_port *ocelot_port = ocelot->ports[port]; |
126 | struct ocelot_port_private *priv; |
127 | struct net_device *dev; |
128 | |
129 | if (!ocelot_port) |
130 | return -EOPNOTSUPP; |
131 | |
132 | priv = container_of(ocelot_port, struct ocelot_port_private, port); |
133 | dev = priv->dev; |
134 | |
135 | if (mrp->p_port != dev && mrp->s_port != dev) |
136 | return 0; |
137 | |
138 | ocelot_port->mrp_ring_id = mrp->ring_id; |
139 | |
140 | return 0; |
141 | } |
142 | EXPORT_SYMBOL(ocelot_mrp_add); |
143 | |
144 | int ocelot_mrp_del(struct ocelot *ocelot, int port, |
145 | const struct switchdev_obj_mrp *mrp) |
146 | { |
147 | struct ocelot_port *ocelot_port = ocelot->ports[port]; |
148 | |
149 | if (!ocelot_port) |
150 | return -EOPNOTSUPP; |
151 | |
152 | if (ocelot_port->mrp_ring_id != mrp->ring_id) |
153 | return 0; |
154 | |
155 | ocelot_port->mrp_ring_id = 0; |
156 | |
157 | return 0; |
158 | } |
159 | EXPORT_SYMBOL(ocelot_mrp_del); |
160 | |
161 | int ocelot_mrp_add_ring_role(struct ocelot *ocelot, int port, |
162 | const struct switchdev_obj_ring_role_mrp *mrp) |
163 | { |
164 | struct ocelot_port *ocelot_port = ocelot->ports[port]; |
165 | int dst_port; |
166 | int err; |
167 | |
168 | if (!ocelot_port) |
169 | return -EOPNOTSUPP; |
170 | |
171 | if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup) |
172 | return -EOPNOTSUPP; |
173 | |
174 | if (ocelot_port->mrp_ring_id != mrp->ring_id) |
175 | return 0; |
176 | |
177 | ocelot_mrp_save_mac(ocelot, port: ocelot_port); |
178 | |
179 | if (mrp->ring_role != BR_MRP_RING_ROLE_MRC) |
180 | return ocelot_mrp_trap_add(ocelot, port); |
181 | |
182 | dst_port = ocelot_mrp_find_partner_port(ocelot, p: ocelot_port); |
183 | if (dst_port == -1) |
184 | return -EINVAL; |
185 | |
186 | err = ocelot_mrp_redirect_add_vcap(ocelot, src_port: port, dst_port); |
187 | if (err) |
188 | return err; |
189 | |
190 | err = ocelot_mrp_trap_add(ocelot, port); |
191 | if (err) { |
192 | ocelot_mrp_del_vcap(ocelot, |
193 | OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); |
194 | return err; |
195 | } |
196 | |
197 | return 0; |
198 | } |
199 | EXPORT_SYMBOL(ocelot_mrp_add_ring_role); |
200 | |
201 | int ocelot_mrp_del_ring_role(struct ocelot *ocelot, int port, |
202 | const struct switchdev_obj_ring_role_mrp *mrp) |
203 | { |
204 | struct ocelot_port *ocelot_port = ocelot->ports[port]; |
205 | int err, i; |
206 | |
207 | if (!ocelot_port) |
208 | return -EOPNOTSUPP; |
209 | |
210 | if (mrp->ring_role != BR_MRP_RING_ROLE_MRC && !mrp->sw_backup) |
211 | return -EOPNOTSUPP; |
212 | |
213 | if (ocelot_port->mrp_ring_id != mrp->ring_id) |
214 | return 0; |
215 | |
216 | err = ocelot_mrp_trap_del(ocelot, port); |
217 | if (err) |
218 | return err; |
219 | |
220 | ocelot_mrp_del_vcap(ocelot, OCELOT_VCAP_IS2_MRP_REDIRECT(ocelot, port)); |
221 | |
222 | for (i = 0; i < ocelot->num_phys_ports; ++i) { |
223 | ocelot_port = ocelot->ports[i]; |
224 | |
225 | if (!ocelot_port) |
226 | continue; |
227 | |
228 | if (ocelot_port->mrp_ring_id != 0) |
229 | goto out; |
230 | } |
231 | |
232 | ocelot_mrp_del_mac(ocelot, port: ocelot->ports[port]); |
233 | out: |
234 | return 0; |
235 | } |
236 | EXPORT_SYMBOL(ocelot_mrp_del_ring_role); |
237 | |