xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3742f75a6SJiri Pirko 
4742f75a6SJiri Pirko #include <linux/kernel.h>
5742f75a6SJiri Pirko #include <linux/bitops.h>
6742f75a6SJiri Pirko 
7742f75a6SJiri Pirko #include "spectrum.h"
8742f75a6SJiri Pirko #include "core.h"
9742f75a6SJiri Pirko #include "reg.h"
10742f75a6SJiri Pirko #include "resources.h"
11742f75a6SJiri Pirko 
12742f75a6SJiri Pirko struct mlxsw_sp2_kvdl_part_info {
13742f75a6SJiri Pirko 	u8 res_type;
14742f75a6SJiri Pirko 	/* For each defined partititon we need to know how many
15742f75a6SJiri Pirko 	 * usage bits we need and how many indexes there are
16742f75a6SJiri Pirko 	 * represented by a single bit. This could be got from FW
17742f75a6SJiri Pirko 	 * querying appropriate resources. So have the resource
18*62783827SJilin Yuan 	 * ids for this purpose in partition definition.
19742f75a6SJiri Pirko 	 */
20742f75a6SJiri Pirko 	enum mlxsw_res_id usage_bit_count_res_id;
21742f75a6SJiri Pirko 	enum mlxsw_res_id index_range_res_id;
22742f75a6SJiri Pirko };
23742f75a6SJiri Pirko 
24742f75a6SJiri Pirko #define MLXSW_SP2_KVDL_PART_INFO(_entry_type, _res_type,			\
25742f75a6SJiri Pirko 				 _usage_bit_count_res_id, _index_range_res_id)	\
26742f75a6SJiri Pirko [MLXSW_SP_KVDL_ENTRY_TYPE_##_entry_type] = {					\
27742f75a6SJiri Pirko 	.res_type = _res_type,							\
28742f75a6SJiri Pirko 	.usage_bit_count_res_id = MLXSW_RES_ID_##_usage_bit_count_res_id,	\
29742f75a6SJiri Pirko 	.index_range_res_id = MLXSW_RES_ID_##_index_range_res_id,		\
30742f75a6SJiri Pirko }
31742f75a6SJiri Pirko 
32742f75a6SJiri Pirko static const struct mlxsw_sp2_kvdl_part_info mlxsw_sp2_kvdl_parts_info[] = {
33742f75a6SJiri Pirko 	MLXSW_SP2_KVDL_PART_INFO(ADJ, 0x21, KVD_SIZE, MAX_KVD_LINEAR_RANGE),
34742f75a6SJiri Pirko 	MLXSW_SP2_KVDL_PART_INFO(ACTSET, 0x23, MAX_KVD_ACTION_SETS,
35742f75a6SJiri Pirko 				 MAX_KVD_ACTION_SETS),
36742f75a6SJiri Pirko 	MLXSW_SP2_KVDL_PART_INFO(PBS, 0x24, KVD_SIZE, KVD_SIZE),
37742f75a6SJiri Pirko 	MLXSW_SP2_KVDL_PART_INFO(MCRIGR, 0x26, KVD_SIZE, KVD_SIZE),
3853eedd61SAmit Cohen 	MLXSW_SP2_KVDL_PART_INFO(IPV6_ADDRESS, 0x28, KVD_SIZE, KVD_SIZE),
3990ea0bb5SIdo Schimmel 	MLXSW_SP2_KVDL_PART_INFO(TNUMT, 0x29, KVD_SIZE, KVD_SIZE),
40742f75a6SJiri Pirko };
41742f75a6SJiri Pirko 
42742f75a6SJiri Pirko #define MLXSW_SP2_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp2_kvdl_parts_info)
43742f75a6SJiri Pirko 
44742f75a6SJiri Pirko struct mlxsw_sp2_kvdl_part {
45742f75a6SJiri Pirko 	const struct mlxsw_sp2_kvdl_part_info *info;
46742f75a6SJiri Pirko 	unsigned int usage_bit_count;
47742f75a6SJiri Pirko 	unsigned int indexes_per_usage_bit;
48742f75a6SJiri Pirko 	unsigned int last_allocated_bit;
49e99f8e7fSGustavo A. R. Silva 	unsigned long usage[];	/* Usage bits */
50742f75a6SJiri Pirko };
51742f75a6SJiri Pirko 
52742f75a6SJiri Pirko struct mlxsw_sp2_kvdl {
53742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl_part *parts[MLXSW_SP2_KVDL_PARTS_INFO_LEN];
54742f75a6SJiri Pirko };
55742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part * part,unsigned int bit_count,unsigned int * p_bit)56742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part *part,
57742f75a6SJiri Pirko 					      unsigned int bit_count,
58742f75a6SJiri Pirko 					      unsigned int *p_bit)
59742f75a6SJiri Pirko {
60742f75a6SJiri Pirko 	unsigned int start_bit;
61742f75a6SJiri Pirko 	unsigned int bit;
62742f75a6SJiri Pirko 	unsigned int i;
63742f75a6SJiri Pirko 	bool wrap = false;
64742f75a6SJiri Pirko 
65742f75a6SJiri Pirko 	start_bit = part->last_allocated_bit + 1;
66742f75a6SJiri Pirko 	if (start_bit == part->usage_bit_count)
67742f75a6SJiri Pirko 		start_bit = 0;
68742f75a6SJiri Pirko 	bit = start_bit;
69742f75a6SJiri Pirko again:
70742f75a6SJiri Pirko 	bit = find_next_zero_bit(part->usage, part->usage_bit_count, bit);
71742f75a6SJiri Pirko 	if (!wrap && bit + bit_count >= part->usage_bit_count) {
72742f75a6SJiri Pirko 		wrap = true;
73742f75a6SJiri Pirko 		bit = 0;
74742f75a6SJiri Pirko 		goto again;
75742f75a6SJiri Pirko 	}
76742f75a6SJiri Pirko 	if (wrap && bit + bit_count >= start_bit)
77742f75a6SJiri Pirko 		return -ENOBUFS;
78742f75a6SJiri Pirko 	for (i = 0; i < bit_count; i++) {
79742f75a6SJiri Pirko 		if (test_bit(bit + i, part->usage)) {
80742f75a6SJiri Pirko 			bit += bit_count;
81742f75a6SJiri Pirko 			goto again;
82742f75a6SJiri Pirko 		}
83742f75a6SJiri Pirko 	}
84742f75a6SJiri Pirko 	*p_bit = bit;
85742f75a6SJiri Pirko 	return 0;
86742f75a6SJiri Pirko }
87742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part * part,unsigned int size,u32 * p_kvdl_index)88742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part *part,
89742f75a6SJiri Pirko 				     unsigned int size,
90742f75a6SJiri Pirko 				     u32 *p_kvdl_index)
91742f75a6SJiri Pirko {
92742f75a6SJiri Pirko 	unsigned int bit_count;
93742f75a6SJiri Pirko 	unsigned int bit;
94742f75a6SJiri Pirko 	unsigned int i;
95742f75a6SJiri Pirko 	int err;
96742f75a6SJiri Pirko 
97742f75a6SJiri Pirko 	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
98742f75a6SJiri Pirko 	err = mlxsw_sp2_kvdl_part_find_zero_bits(part, bit_count, &bit);
99742f75a6SJiri Pirko 	if (err)
100742f75a6SJiri Pirko 		return err;
101742f75a6SJiri Pirko 	for (i = 0; i < bit_count; i++)
102742f75a6SJiri Pirko 		__set_bit(bit + i, part->usage);
103742f75a6SJiri Pirko 	*p_kvdl_index = bit * part->indexes_per_usage_bit;
104742f75a6SJiri Pirko 	return 0;
105742f75a6SJiri Pirko }
106742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp * mlxsw_sp,u8 res_type,u16 size,u32 kvdl_index)107742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp *mlxsw_sp, u8 res_type,
108742f75a6SJiri Pirko 				  u16 size, u32 kvdl_index)
109742f75a6SJiri Pirko {
110742f75a6SJiri Pirko 	char *iedr_pl;
111742f75a6SJiri Pirko 	int err;
112742f75a6SJiri Pirko 
113742f75a6SJiri Pirko 	iedr_pl = kmalloc(MLXSW_REG_IEDR_LEN, GFP_KERNEL);
114742f75a6SJiri Pirko 	if (!iedr_pl)
115742f75a6SJiri Pirko 		return -ENOMEM;
116742f75a6SJiri Pirko 
117742f75a6SJiri Pirko 	mlxsw_reg_iedr_pack(iedr_pl);
118742f75a6SJiri Pirko 	mlxsw_reg_iedr_rec_pack(iedr_pl, 0, res_type, size, kvdl_index);
119742f75a6SJiri Pirko 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(iedr), iedr_pl);
120742f75a6SJiri Pirko 	kfree(iedr_pl);
121742f75a6SJiri Pirko 	return err;
122742f75a6SJiri Pirko }
123742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_part_free(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp2_kvdl_part * part,unsigned int size,u32 kvdl_index)124742f75a6SJiri Pirko static void mlxsw_sp2_kvdl_part_free(struct mlxsw_sp *mlxsw_sp,
125742f75a6SJiri Pirko 				     struct mlxsw_sp2_kvdl_part *part,
126742f75a6SJiri Pirko 				     unsigned int size, u32 kvdl_index)
127742f75a6SJiri Pirko {
128742f75a6SJiri Pirko 	unsigned int bit_count;
129742f75a6SJiri Pirko 	unsigned int bit;
130742f75a6SJiri Pirko 	unsigned int i;
131742f75a6SJiri Pirko 	int err;
132742f75a6SJiri Pirko 
133742f75a6SJiri Pirko 	/* We need to ask FW to delete previously used KVD linear index */
134742f75a6SJiri Pirko 	err = mlxsw_sp2_kvdl_rec_del(mlxsw_sp, part->info->res_type,
135742f75a6SJiri Pirko 				     size, kvdl_index);
136742f75a6SJiri Pirko 	if (err)
137742f75a6SJiri Pirko 		return;
138742f75a6SJiri Pirko 
139742f75a6SJiri Pirko 	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
140742f75a6SJiri Pirko 	bit = kvdl_index / part->indexes_per_usage_bit;
141742f75a6SJiri Pirko 	for (i = 0; i < bit_count; i++)
142742f75a6SJiri Pirko 		__clear_bit(bit + i, part->usage);
143742f75a6SJiri Pirko }
144742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_alloc(struct mlxsw_sp * mlxsw_sp,void * priv,enum mlxsw_sp_kvdl_entry_type type,unsigned int entry_count,u32 * p_entry_index)145742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv,
146742f75a6SJiri Pirko 				enum mlxsw_sp_kvdl_entry_type type,
147742f75a6SJiri Pirko 				unsigned int entry_count,
148742f75a6SJiri Pirko 				u32 *p_entry_index)
149742f75a6SJiri Pirko {
150742f75a6SJiri Pirko 	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
151742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl *kvdl = priv;
152742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
153742f75a6SJiri Pirko 
154742f75a6SJiri Pirko 	return mlxsw_sp2_kvdl_part_alloc(part, size, p_entry_index);
155742f75a6SJiri Pirko }
156742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_free(struct mlxsw_sp * mlxsw_sp,void * priv,enum mlxsw_sp_kvdl_entry_type type,unsigned int entry_count,int entry_index)157742f75a6SJiri Pirko static void mlxsw_sp2_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv,
158742f75a6SJiri Pirko 				enum mlxsw_sp_kvdl_entry_type type,
159742f75a6SJiri Pirko 				unsigned int entry_count,
160742f75a6SJiri Pirko 				int entry_index)
161742f75a6SJiri Pirko {
162742f75a6SJiri Pirko 	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
163742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl *kvdl = priv;
164742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
165742f75a6SJiri Pirko 
166742f75a6SJiri Pirko 	return mlxsw_sp2_kvdl_part_free(mlxsw_sp, part, size, entry_index);
167742f75a6SJiri Pirko }
168742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_alloc_size_query(struct mlxsw_sp * mlxsw_sp,void * priv,enum mlxsw_sp_kvdl_entry_type type,unsigned int entry_count,unsigned int * p_alloc_count)169742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
170742f75a6SJiri Pirko 					   void *priv,
171742f75a6SJiri Pirko 					   enum mlxsw_sp_kvdl_entry_type type,
172742f75a6SJiri Pirko 					   unsigned int entry_count,
173742f75a6SJiri Pirko 					   unsigned int *p_alloc_count)
174742f75a6SJiri Pirko {
175742f75a6SJiri Pirko 	*p_alloc_count = entry_count;
176742f75a6SJiri Pirko 	return 0;
177742f75a6SJiri Pirko }
178742f75a6SJiri Pirko 
179742f75a6SJiri Pirko static struct mlxsw_sp2_kvdl_part *
mlxsw_sp2_kvdl_part_init(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp2_kvdl_part_info * info)180742f75a6SJiri Pirko mlxsw_sp2_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
181742f75a6SJiri Pirko 			 const struct mlxsw_sp2_kvdl_part_info *info)
182742f75a6SJiri Pirko {
183742f75a6SJiri Pirko 	unsigned int indexes_per_usage_bit;
184742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl_part *part;
185742f75a6SJiri Pirko 	unsigned int index_range;
186742f75a6SJiri Pirko 	unsigned int usage_bit_count;
187742f75a6SJiri Pirko 	size_t usage_size;
188742f75a6SJiri Pirko 
189742f75a6SJiri Pirko 	if (!mlxsw_core_res_valid(mlxsw_sp->core,
190742f75a6SJiri Pirko 				  info->usage_bit_count_res_id) ||
191742f75a6SJiri Pirko 	    !mlxsw_core_res_valid(mlxsw_sp->core,
192742f75a6SJiri Pirko 				  info->index_range_res_id))
193742f75a6SJiri Pirko 		return ERR_PTR(-EIO);
194742f75a6SJiri Pirko 	usage_bit_count = mlxsw_core_res_get(mlxsw_sp->core,
195742f75a6SJiri Pirko 					     info->usage_bit_count_res_id);
196742f75a6SJiri Pirko 	index_range = mlxsw_core_res_get(mlxsw_sp->core,
197742f75a6SJiri Pirko 					 info->index_range_res_id);
198742f75a6SJiri Pirko 
199742f75a6SJiri Pirko 	/* For some partitions, one usage bit represents a group of indexes.
200742f75a6SJiri Pirko 	 * That's why we compute the number of indexes per usage bit here,
201742f75a6SJiri Pirko 	 * according to queried resources.
202742f75a6SJiri Pirko 	 */
203742f75a6SJiri Pirko 	indexes_per_usage_bit = index_range / usage_bit_count;
204742f75a6SJiri Pirko 
205742f75a6SJiri Pirko 	usage_size = BITS_TO_LONGS(usage_bit_count) * sizeof(unsigned long);
206742f75a6SJiri Pirko 	part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
207742f75a6SJiri Pirko 	if (!part)
208742f75a6SJiri Pirko 		return ERR_PTR(-ENOMEM);
209742f75a6SJiri Pirko 	part->info = info;
210742f75a6SJiri Pirko 	part->usage_bit_count = usage_bit_count;
211742f75a6SJiri Pirko 	part->indexes_per_usage_bit = indexes_per_usage_bit;
212742f75a6SJiri Pirko 	part->last_allocated_bit = usage_bit_count - 1;
213742f75a6SJiri Pirko 	return part;
214742f75a6SJiri Pirko }
215742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part * part)216742f75a6SJiri Pirko static void mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part *part)
217742f75a6SJiri Pirko {
218742f75a6SJiri Pirko 	kfree(part);
219742f75a6SJiri Pirko }
220742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp2_kvdl * kvdl)221742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp,
222742f75a6SJiri Pirko 				     struct mlxsw_sp2_kvdl *kvdl)
223742f75a6SJiri Pirko {
224742f75a6SJiri Pirko 	const struct mlxsw_sp2_kvdl_part_info *info;
225742f75a6SJiri Pirko 	int i;
226742f75a6SJiri Pirko 	int err;
227742f75a6SJiri Pirko 
228742f75a6SJiri Pirko 	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++) {
229742f75a6SJiri Pirko 		info = &mlxsw_sp2_kvdl_parts_info[i];
230742f75a6SJiri Pirko 		kvdl->parts[i] = mlxsw_sp2_kvdl_part_init(mlxsw_sp, info);
231742f75a6SJiri Pirko 		if (IS_ERR(kvdl->parts[i])) {
232742f75a6SJiri Pirko 			err = PTR_ERR(kvdl->parts[i]);
233742f75a6SJiri Pirko 			goto err_kvdl_part_init;
234742f75a6SJiri Pirko 		}
235742f75a6SJiri Pirko 	}
236742f75a6SJiri Pirko 	return 0;
237742f75a6SJiri Pirko 
238742f75a6SJiri Pirko err_kvdl_part_init:
239742f75a6SJiri Pirko 	for (i--; i >= 0; i--)
240742f75a6SJiri Pirko 		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
241742f75a6SJiri Pirko 	return err;
242742f75a6SJiri Pirko }
243742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl * kvdl)244742f75a6SJiri Pirko static void mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl *kvdl)
245742f75a6SJiri Pirko {
246742f75a6SJiri Pirko 	int i;
247742f75a6SJiri Pirko 
248742f75a6SJiri Pirko 	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++)
249742f75a6SJiri Pirko 		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
250742f75a6SJiri Pirko }
251742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_init(struct mlxsw_sp * mlxsw_sp,void * priv)252742f75a6SJiri Pirko static int mlxsw_sp2_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv)
253742f75a6SJiri Pirko {
254742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl *kvdl = priv;
255742f75a6SJiri Pirko 
256742f75a6SJiri Pirko 	return mlxsw_sp2_kvdl_parts_init(mlxsw_sp, kvdl);
257742f75a6SJiri Pirko }
258742f75a6SJiri Pirko 
mlxsw_sp2_kvdl_fini(struct mlxsw_sp * mlxsw_sp,void * priv)259742f75a6SJiri Pirko static void mlxsw_sp2_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
260742f75a6SJiri Pirko {
261742f75a6SJiri Pirko 	struct mlxsw_sp2_kvdl *kvdl = priv;
262742f75a6SJiri Pirko 
263742f75a6SJiri Pirko 	mlxsw_sp2_kvdl_parts_fini(kvdl);
264742f75a6SJiri Pirko }
265742f75a6SJiri Pirko 
266742f75a6SJiri Pirko const struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops = {
267742f75a6SJiri Pirko 	.priv_size = sizeof(struct mlxsw_sp2_kvdl),
268742f75a6SJiri Pirko 	.init = mlxsw_sp2_kvdl_init,
269742f75a6SJiri Pirko 	.fini = mlxsw_sp2_kvdl_fini,
270742f75a6SJiri Pirko 	.alloc = mlxsw_sp2_kvdl_alloc,
271742f75a6SJiri Pirko 	.free = mlxsw_sp2_kvdl_free,
272742f75a6SJiri Pirko 	.alloc_size_query = mlxsw_sp2_kvdl_alloc_size_query,
273742f75a6SJiri Pirko };
274