1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
3
4#include <linux/kernel.h>
5#include <linux/parman.h>
6
7#include "reg.h"
8#include "spectrum.h"
9#include "core_acl_flex_actions.h"
10#include "spectrum_mr.h"
11
12struct mlxsw_sp1_mr_tcam_region {
13 struct mlxsw_sp *mlxsw_sp;
14 enum mlxsw_reg_rtar_key_type rtar_key_type;
15 struct parman *parman;
16 struct parman_prio *parman_prios;
17};
18
19struct mlxsw_sp1_mr_tcam {
20 struct mlxsw_sp1_mr_tcam_region tcam_regions[MLXSW_SP_L3_PROTO_MAX];
21};
22
23struct mlxsw_sp1_mr_tcam_route {
24 struct parman_item parman_item;
25 struct parman_prio *parman_prio;
26};
27
28static int mlxsw_sp1_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp,
29 struct parman_item *parman_item,
30 struct mlxsw_sp_mr_route_key *key,
31 struct mlxsw_afa_block *afa_block)
32{
33 char rmft2_pl[MLXSW_REG_RMFT2_LEN];
34
35 switch (key->proto) {
36 case MLXSW_SP_L3_PROTO_IPV4:
37 mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, true, parman_item->index,
38 key->vrid,
39 MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0,
40 ntohl(key->group.addr4),
41 ntohl(key->group_mask.addr4),
42 ntohl(key->source.addr4),
43 ntohl(key->source_mask.addr4),
44 mlxsw_afa_block_first_set(afa_block));
45 break;
46 case MLXSW_SP_L3_PROTO_IPV6:
47 mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, true, parman_item->index,
48 key->vrid,
49 MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, 0,
50 key->group.addr6,
51 key->group_mask.addr6,
52 key->source.addr6,
53 key->source_mask.addr6,
54 mlxsw_afa_block_first_set(afa_block));
55 }
56
57 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
58}
59
60static int mlxsw_sp1_mr_tcam_route_remove(struct mlxsw_sp *mlxsw_sp,
61 struct parman_item *parman_item,
62 struct mlxsw_sp_mr_route_key *key)
63{
64 struct in6_addr zero_addr = IN6ADDR_ANY_INIT;
65 char rmft2_pl[MLXSW_REG_RMFT2_LEN];
66
67 switch (key->proto) {
68 case MLXSW_SP_L3_PROTO_IPV4:
69 mlxsw_reg_rmft2_ipv4_pack(rmft2_pl, false, parman_item->index,
70 key->vrid, 0, 0, 0, 0, 0, 0, NULL);
71 break;
72 case MLXSW_SP_L3_PROTO_IPV6:
73 mlxsw_reg_rmft2_ipv6_pack(rmft2_pl, false, parman_item->index,
74 key->vrid, 0, 0, zero_addr, zero_addr,
75 zero_addr, zero_addr, NULL);
76 break;
77 }
78
79 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rmft2), rmft2_pl);
80}
81
82static struct mlxsw_sp1_mr_tcam_region *
83mlxsw_sp1_mr_tcam_protocol_region(struct mlxsw_sp1_mr_tcam *mr_tcam,
84 enum mlxsw_sp_l3proto proto)
85{
86 return &mr_tcam->tcam_regions[proto];
87}
88
89static int
90mlxsw_sp1_mr_tcam_route_parman_item_add(struct mlxsw_sp1_mr_tcam *mr_tcam,
91 struct mlxsw_sp1_mr_tcam_route *route,
92 struct mlxsw_sp_mr_route_key *key,
93 enum mlxsw_sp_mr_route_prio prio)
94{
95 struct mlxsw_sp1_mr_tcam_region *tcam_region;
96 int err;
97
98 tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, key->proto);
99 err = parman_item_add(tcam_region->parman,
100 &tcam_region->parman_prios[prio],
101 &route->parman_item);
102 if (err)
103 return err;
104
105 route->parman_prio = &tcam_region->parman_prios[prio];
106 return 0;
107}
108
109static void
110mlxsw_sp1_mr_tcam_route_parman_item_remove(struct mlxsw_sp1_mr_tcam *mr_tcam,
111 struct mlxsw_sp1_mr_tcam_route *route,
112 struct mlxsw_sp_mr_route_key *key)
113{
114 struct mlxsw_sp1_mr_tcam_region *tcam_region;
115
116 tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, key->proto);
117 parman_item_remove(tcam_region->parman,
118 route->parman_prio, &route->parman_item);
119}
120
121static int
122mlxsw_sp1_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv,
123 void *route_priv,
124 struct mlxsw_sp_mr_route_key *key,
125 struct mlxsw_afa_block *afa_block,
126 enum mlxsw_sp_mr_route_prio prio)
127{
128 struct mlxsw_sp1_mr_tcam_route *route = route_priv;
129 struct mlxsw_sp1_mr_tcam *mr_tcam = priv;
130 int err;
131
132 err = mlxsw_sp1_mr_tcam_route_parman_item_add(mr_tcam, route,
133 key, prio);
134 if (err)
135 return err;
136
137 err = mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, &route->parman_item,
138 key, afa_block);
139 if (err)
140 goto err_route_replace;
141 return 0;
142
143err_route_replace:
144 mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key);
145 return err;
146}
147
148static void
149mlxsw_sp1_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv,
150 void *route_priv,
151 struct mlxsw_sp_mr_route_key *key)
152{
153 struct mlxsw_sp1_mr_tcam_route *route = route_priv;
154 struct mlxsw_sp1_mr_tcam *mr_tcam = priv;
155
156 mlxsw_sp1_mr_tcam_route_remove(mlxsw_sp, &route->parman_item, key);
157 mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key);
158}
159
160static int
161mlxsw_sp1_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp,
162 void *route_priv,
163 struct mlxsw_sp_mr_route_key *key,
164 struct mlxsw_afa_block *afa_block)
165{
166 struct mlxsw_sp1_mr_tcam_route *route = route_priv;
167
168 return mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, &route->parman_item,
169 key, afa_block);
170}
171
172#define MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT 16
173#define MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP 16
174
175static int
176mlxsw_sp1_mr_tcam_region_alloc(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region)
177{
178 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp;
179 char rtar_pl[MLXSW_REG_RTAR_LEN];
180
181 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_ALLOCATE,
182 mr_tcam_region->rtar_key_type,
183 MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT);
184 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl);
185}
186
187static void
188mlxsw_sp1_mr_tcam_region_free(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region)
189{
190 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp;
191 char rtar_pl[MLXSW_REG_RTAR_LEN];
192
193 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_DEALLOCATE,
194 mr_tcam_region->rtar_key_type, 0);
195 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl);
196}
197
198static int mlxsw_sp1_mr_tcam_region_parman_resize(void *priv,
199 unsigned long new_count)
200{
201 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv;
202 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp;
203 char rtar_pl[MLXSW_REG_RTAR_LEN];
204 u64 max_tcam_rules;
205
206 max_tcam_rules = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_TCAM_RULES);
207 if (new_count > max_tcam_rules)
208 return -EINVAL;
209 mlxsw_reg_rtar_pack(rtar_pl, MLXSW_REG_RTAR_OP_RESIZE,
210 mr_tcam_region->rtar_key_type, new_count);
211 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rtar), rtar_pl);
212}
213
214static void mlxsw_sp1_mr_tcam_region_parman_move(void *priv,
215 unsigned long from_index,
216 unsigned long to_index,
217 unsigned long count)
218{
219 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv;
220 struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp;
221 char rrcr_pl[MLXSW_REG_RRCR_LEN];
222
223 mlxsw_reg_rrcr_pack(rrcr_pl, MLXSW_REG_RRCR_OP_MOVE,
224 from_index, count,
225 mr_tcam_region->rtar_key_type, to_index);
226 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rrcr), rrcr_pl);
227}
228
229static const struct parman_ops mlxsw_sp1_mr_tcam_region_parman_ops = {
230 .base_count = MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT,
231 .resize_step = MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP,
232 .resize = mlxsw_sp1_mr_tcam_region_parman_resize,
233 .move = mlxsw_sp1_mr_tcam_region_parman_move,
234 .algo = PARMAN_ALGO_TYPE_LSORT,
235};
236
237static int
238mlxsw_sp1_mr_tcam_region_init(struct mlxsw_sp *mlxsw_sp,
239 struct mlxsw_sp1_mr_tcam_region *mr_tcam_region,
240 enum mlxsw_reg_rtar_key_type rtar_key_type)
241{
242 struct parman_prio *parman_prios;
243 struct parman *parman;
244 int err;
245 int i;
246
247 mr_tcam_region->rtar_key_type = rtar_key_type;
248 mr_tcam_region->mlxsw_sp = mlxsw_sp;
249
250 err = mlxsw_sp1_mr_tcam_region_alloc(mr_tcam_region);
251 if (err)
252 return err;
253
254 parman = parman_create(&mlxsw_sp1_mr_tcam_region_parman_ops,
255 mr_tcam_region);
256 if (!parman) {
257 err = -ENOMEM;
258 goto err_parman_create;
259 }
260 mr_tcam_region->parman = parman;
261
262 parman_prios = kmalloc_array(MLXSW_SP_MR_ROUTE_PRIO_MAX + 1,
263 sizeof(*parman_prios), GFP_KERNEL);
264 if (!parman_prios) {
265 err = -ENOMEM;
266 goto err_parman_prios_alloc;
267 }
268 mr_tcam_region->parman_prios = parman_prios;
269
270 for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++)
271 parman_prio_init(mr_tcam_region->parman,
272 &mr_tcam_region->parman_prios[i], i);
273 return 0;
274
275err_parman_prios_alloc:
276 parman_destroy(parman);
277err_parman_create:
278 mlxsw_sp1_mr_tcam_region_free(mr_tcam_region);
279 return err;
280}
281
282static void
283mlxsw_sp1_mr_tcam_region_fini(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region)
284{
285 int i;
286
287 for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++)
288 parman_prio_fini(&mr_tcam_region->parman_prios[i]);
289 kfree(mr_tcam_region->parman_prios);
290 parman_destroy(mr_tcam_region->parman);
291 mlxsw_sp1_mr_tcam_region_free(mr_tcam_region);
292}
293
294static int mlxsw_sp1_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv)
295{
296 struct mlxsw_sp1_mr_tcam *mr_tcam = priv;
297 struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
298 u32 rtar_key;
299 int err;
300
301 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES))
302 return -EIO;
303
304 rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST;
305 err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp,
306 &region[MLXSW_SP_L3_PROTO_IPV4],
307 rtar_key);
308 if (err)
309 return err;
310
311 rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV6_MULTICAST;
312 err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp,
313 &region[MLXSW_SP_L3_PROTO_IPV6],
314 rtar_key);
315 if (err)
316 goto err_ipv6_region_init;
317
318 return 0;
319
320err_ipv6_region_init:
321 mlxsw_sp1_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
322 return err;
323}
324
325static void mlxsw_sp1_mr_tcam_fini(void *priv)
326{
327 struct mlxsw_sp1_mr_tcam *mr_tcam = priv;
328 struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0];
329
330 mlxsw_sp1_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV6]);
331 mlxsw_sp1_mr_tcam_region_fini(&region[MLXSW_SP_L3_PROTO_IPV4]);
332}
333
334const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops = {
335 .priv_size = sizeof(struct mlxsw_sp1_mr_tcam),
336 .init = mlxsw_sp1_mr_tcam_init,
337 .fini = mlxsw_sp1_mr_tcam_fini,
338 .route_priv_size = sizeof(struct mlxsw_sp1_mr_tcam_route),
339 .route_create = mlxsw_sp1_mr_tcam_route_create,
340 .route_destroy = mlxsw_sp1_mr_tcam_route_destroy,
341 .route_update = mlxsw_sp1_mr_tcam_route_update,
342};
343

source code of linux/drivers/net/ethernet/mellanox/mlxsw/spectrum1_mr_tcam.c