xref: /OK3568_Linux_fs/kernel/drivers/net/ethernet/mellanox/mlxsw/spectrum2_kvdl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <linux/kernel.h>
5*4882a593Smuzhiyun #include <linux/bitops.h>
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include "spectrum.h"
8*4882a593Smuzhiyun #include "core.h"
9*4882a593Smuzhiyun #include "reg.h"
10*4882a593Smuzhiyun #include "resources.h"
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun struct mlxsw_sp2_kvdl_part_info {
13*4882a593Smuzhiyun 	u8 res_type;
14*4882a593Smuzhiyun 	/* For each defined partititon we need to know how many
15*4882a593Smuzhiyun 	 * usage bits we need and how many indexes there are
16*4882a593Smuzhiyun 	 * represented by a single bit. This could be got from FW
17*4882a593Smuzhiyun 	 * querying appropriate resources. So have the resource
18*4882a593Smuzhiyun 	 * ids for for this purpose in partition definition.
19*4882a593Smuzhiyun 	 */
20*4882a593Smuzhiyun 	enum mlxsw_res_id usage_bit_count_res_id;
21*4882a593Smuzhiyun 	enum mlxsw_res_id index_range_res_id;
22*4882a593Smuzhiyun };
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define MLXSW_SP2_KVDL_PART_INFO(_entry_type, _res_type,			\
25*4882a593Smuzhiyun 				 _usage_bit_count_res_id, _index_range_res_id)	\
26*4882a593Smuzhiyun [MLXSW_SP_KVDL_ENTRY_TYPE_##_entry_type] = {					\
27*4882a593Smuzhiyun 	.res_type = _res_type,							\
28*4882a593Smuzhiyun 	.usage_bit_count_res_id = MLXSW_RES_ID_##_usage_bit_count_res_id,	\
29*4882a593Smuzhiyun 	.index_range_res_id = MLXSW_RES_ID_##_index_range_res_id,		\
30*4882a593Smuzhiyun }
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun static const struct mlxsw_sp2_kvdl_part_info mlxsw_sp2_kvdl_parts_info[] = {
33*4882a593Smuzhiyun 	MLXSW_SP2_KVDL_PART_INFO(ADJ, 0x21, KVD_SIZE, MAX_KVD_LINEAR_RANGE),
34*4882a593Smuzhiyun 	MLXSW_SP2_KVDL_PART_INFO(ACTSET, 0x23, MAX_KVD_ACTION_SETS,
35*4882a593Smuzhiyun 				 MAX_KVD_ACTION_SETS),
36*4882a593Smuzhiyun 	MLXSW_SP2_KVDL_PART_INFO(PBS, 0x24, KVD_SIZE, KVD_SIZE),
37*4882a593Smuzhiyun 	MLXSW_SP2_KVDL_PART_INFO(MCRIGR, 0x26, KVD_SIZE, KVD_SIZE),
38*4882a593Smuzhiyun 	MLXSW_SP2_KVDL_PART_INFO(TNUMT, 0x29, KVD_SIZE, KVD_SIZE),
39*4882a593Smuzhiyun };
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun #define MLXSW_SP2_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp2_kvdl_parts_info)
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun struct mlxsw_sp2_kvdl_part {
44*4882a593Smuzhiyun 	const struct mlxsw_sp2_kvdl_part_info *info;
45*4882a593Smuzhiyun 	unsigned int usage_bit_count;
46*4882a593Smuzhiyun 	unsigned int indexes_per_usage_bit;
47*4882a593Smuzhiyun 	unsigned int last_allocated_bit;
48*4882a593Smuzhiyun 	unsigned long usage[];	/* Usage bits */
49*4882a593Smuzhiyun };
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun struct mlxsw_sp2_kvdl {
52*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl_part *parts[MLXSW_SP2_KVDL_PARTS_INFO_LEN];
53*4882a593Smuzhiyun };
54*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part * part,unsigned int bit_count,unsigned int * p_bit)55*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part *part,
56*4882a593Smuzhiyun 					      unsigned int bit_count,
57*4882a593Smuzhiyun 					      unsigned int *p_bit)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun 	unsigned int start_bit;
60*4882a593Smuzhiyun 	unsigned int bit;
61*4882a593Smuzhiyun 	unsigned int i;
62*4882a593Smuzhiyun 	bool wrap = false;
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 	start_bit = part->last_allocated_bit + 1;
65*4882a593Smuzhiyun 	if (start_bit == part->usage_bit_count)
66*4882a593Smuzhiyun 		start_bit = 0;
67*4882a593Smuzhiyun 	bit = start_bit;
68*4882a593Smuzhiyun again:
69*4882a593Smuzhiyun 	bit = find_next_zero_bit(part->usage, part->usage_bit_count, bit);
70*4882a593Smuzhiyun 	if (!wrap && bit + bit_count >= part->usage_bit_count) {
71*4882a593Smuzhiyun 		wrap = true;
72*4882a593Smuzhiyun 		bit = 0;
73*4882a593Smuzhiyun 		goto again;
74*4882a593Smuzhiyun 	}
75*4882a593Smuzhiyun 	if (wrap && bit + bit_count >= start_bit)
76*4882a593Smuzhiyun 		return -ENOBUFS;
77*4882a593Smuzhiyun 	for (i = 0; i < bit_count; i++) {
78*4882a593Smuzhiyun 		if (test_bit(bit + i, part->usage)) {
79*4882a593Smuzhiyun 			bit += bit_count;
80*4882a593Smuzhiyun 			goto again;
81*4882a593Smuzhiyun 		}
82*4882a593Smuzhiyun 	}
83*4882a593Smuzhiyun 	*p_bit = bit;
84*4882a593Smuzhiyun 	return 0;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part * part,unsigned int size,u32 * p_kvdl_index)87*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part *part,
88*4882a593Smuzhiyun 				     unsigned int size,
89*4882a593Smuzhiyun 				     u32 *p_kvdl_index)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	unsigned int bit_count;
92*4882a593Smuzhiyun 	unsigned int bit;
93*4882a593Smuzhiyun 	unsigned int i;
94*4882a593Smuzhiyun 	int err;
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
97*4882a593Smuzhiyun 	err = mlxsw_sp2_kvdl_part_find_zero_bits(part, bit_count, &bit);
98*4882a593Smuzhiyun 	if (err)
99*4882a593Smuzhiyun 		return err;
100*4882a593Smuzhiyun 	for (i = 0; i < bit_count; i++)
101*4882a593Smuzhiyun 		__set_bit(bit + i, part->usage);
102*4882a593Smuzhiyun 	*p_kvdl_index = bit * part->indexes_per_usage_bit;
103*4882a593Smuzhiyun 	return 0;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp * mlxsw_sp,u8 res_type,u16 size,u32 kvdl_index)106*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp *mlxsw_sp, u8 res_type,
107*4882a593Smuzhiyun 				  u16 size, u32 kvdl_index)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun 	char *iedr_pl;
110*4882a593Smuzhiyun 	int err;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	iedr_pl = kmalloc(MLXSW_REG_IEDR_LEN, GFP_KERNEL);
113*4882a593Smuzhiyun 	if (!iedr_pl)
114*4882a593Smuzhiyun 		return -ENOMEM;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 	mlxsw_reg_iedr_pack(iedr_pl);
117*4882a593Smuzhiyun 	mlxsw_reg_iedr_rec_pack(iedr_pl, 0, res_type, size, kvdl_index);
118*4882a593Smuzhiyun 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(iedr), iedr_pl);
119*4882a593Smuzhiyun 	kfree(iedr_pl);
120*4882a593Smuzhiyun 	return err;
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_part_free(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp2_kvdl_part * part,unsigned int size,u32 kvdl_index)123*4882a593Smuzhiyun static void mlxsw_sp2_kvdl_part_free(struct mlxsw_sp *mlxsw_sp,
124*4882a593Smuzhiyun 				     struct mlxsw_sp2_kvdl_part *part,
125*4882a593Smuzhiyun 				     unsigned int size, u32 kvdl_index)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	unsigned int bit_count;
128*4882a593Smuzhiyun 	unsigned int bit;
129*4882a593Smuzhiyun 	unsigned int i;
130*4882a593Smuzhiyun 	int err;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 	/* We need to ask FW to delete previously used KVD linear index */
133*4882a593Smuzhiyun 	err = mlxsw_sp2_kvdl_rec_del(mlxsw_sp, part->info->res_type,
134*4882a593Smuzhiyun 				     size, kvdl_index);
135*4882a593Smuzhiyun 	if (err)
136*4882a593Smuzhiyun 		return;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
139*4882a593Smuzhiyun 	bit = kvdl_index / part->indexes_per_usage_bit;
140*4882a593Smuzhiyun 	for (i = 0; i < bit_count; i++)
141*4882a593Smuzhiyun 		__clear_bit(bit + i, part->usage);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
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)144*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv,
145*4882a593Smuzhiyun 				enum mlxsw_sp_kvdl_entry_type type,
146*4882a593Smuzhiyun 				unsigned int entry_count,
147*4882a593Smuzhiyun 				u32 *p_entry_index)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun 	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
150*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl *kvdl = priv;
151*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	return mlxsw_sp2_kvdl_part_alloc(part, size, p_entry_index);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
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)156*4882a593Smuzhiyun static void mlxsw_sp2_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv,
157*4882a593Smuzhiyun 				enum mlxsw_sp_kvdl_entry_type type,
158*4882a593Smuzhiyun 				unsigned int entry_count,
159*4882a593Smuzhiyun 				int entry_index)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun 	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
162*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl *kvdl = priv;
163*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	return mlxsw_sp2_kvdl_part_free(mlxsw_sp, part, size, entry_index);
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun 
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)168*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
169*4882a593Smuzhiyun 					   void *priv,
170*4882a593Smuzhiyun 					   enum mlxsw_sp_kvdl_entry_type type,
171*4882a593Smuzhiyun 					   unsigned int entry_count,
172*4882a593Smuzhiyun 					   unsigned int *p_alloc_count)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun 	*p_alloc_count = entry_count;
175*4882a593Smuzhiyun 	return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun static struct mlxsw_sp2_kvdl_part *
mlxsw_sp2_kvdl_part_init(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp2_kvdl_part_info * info)179*4882a593Smuzhiyun mlxsw_sp2_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
180*4882a593Smuzhiyun 			 const struct mlxsw_sp2_kvdl_part_info *info)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	unsigned int indexes_per_usage_bit;
183*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl_part *part;
184*4882a593Smuzhiyun 	unsigned int index_range;
185*4882a593Smuzhiyun 	unsigned int usage_bit_count;
186*4882a593Smuzhiyun 	size_t usage_size;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	if (!mlxsw_core_res_valid(mlxsw_sp->core,
189*4882a593Smuzhiyun 				  info->usage_bit_count_res_id) ||
190*4882a593Smuzhiyun 	    !mlxsw_core_res_valid(mlxsw_sp->core,
191*4882a593Smuzhiyun 				  info->index_range_res_id))
192*4882a593Smuzhiyun 		return ERR_PTR(-EIO);
193*4882a593Smuzhiyun 	usage_bit_count = mlxsw_core_res_get(mlxsw_sp->core,
194*4882a593Smuzhiyun 					     info->usage_bit_count_res_id);
195*4882a593Smuzhiyun 	index_range = mlxsw_core_res_get(mlxsw_sp->core,
196*4882a593Smuzhiyun 					 info->index_range_res_id);
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun 	/* For some partitions, one usage bit represents a group of indexes.
199*4882a593Smuzhiyun 	 * That's why we compute the number of indexes per usage bit here,
200*4882a593Smuzhiyun 	 * according to queried resources.
201*4882a593Smuzhiyun 	 */
202*4882a593Smuzhiyun 	indexes_per_usage_bit = index_range / usage_bit_count;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	usage_size = BITS_TO_LONGS(usage_bit_count) * sizeof(unsigned long);
205*4882a593Smuzhiyun 	part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
206*4882a593Smuzhiyun 	if (!part)
207*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
208*4882a593Smuzhiyun 	part->info = info;
209*4882a593Smuzhiyun 	part->usage_bit_count = usage_bit_count;
210*4882a593Smuzhiyun 	part->indexes_per_usage_bit = indexes_per_usage_bit;
211*4882a593Smuzhiyun 	part->last_allocated_bit = usage_bit_count - 1;
212*4882a593Smuzhiyun 	return part;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part * part)215*4882a593Smuzhiyun static void mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part *part)
216*4882a593Smuzhiyun {
217*4882a593Smuzhiyun 	kfree(part);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp2_kvdl * kvdl)220*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp,
221*4882a593Smuzhiyun 				     struct mlxsw_sp2_kvdl *kvdl)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	const struct mlxsw_sp2_kvdl_part_info *info;
224*4882a593Smuzhiyun 	int i;
225*4882a593Smuzhiyun 	int err;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++) {
228*4882a593Smuzhiyun 		info = &mlxsw_sp2_kvdl_parts_info[i];
229*4882a593Smuzhiyun 		kvdl->parts[i] = mlxsw_sp2_kvdl_part_init(mlxsw_sp, info);
230*4882a593Smuzhiyun 		if (IS_ERR(kvdl->parts[i])) {
231*4882a593Smuzhiyun 			err = PTR_ERR(kvdl->parts[i]);
232*4882a593Smuzhiyun 			goto err_kvdl_part_init;
233*4882a593Smuzhiyun 		}
234*4882a593Smuzhiyun 	}
235*4882a593Smuzhiyun 	return 0;
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun err_kvdl_part_init:
238*4882a593Smuzhiyun 	for (i--; i >= 0; i--)
239*4882a593Smuzhiyun 		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
240*4882a593Smuzhiyun 	return err;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl * kvdl)243*4882a593Smuzhiyun static void mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl *kvdl)
244*4882a593Smuzhiyun {
245*4882a593Smuzhiyun 	int i;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++)
248*4882a593Smuzhiyun 		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
249*4882a593Smuzhiyun }
250*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_init(struct mlxsw_sp * mlxsw_sp,void * priv)251*4882a593Smuzhiyun static int mlxsw_sp2_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv)
252*4882a593Smuzhiyun {
253*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl *kvdl = priv;
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	return mlxsw_sp2_kvdl_parts_init(mlxsw_sp, kvdl);
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun 
mlxsw_sp2_kvdl_fini(struct mlxsw_sp * mlxsw_sp,void * priv)258*4882a593Smuzhiyun static void mlxsw_sp2_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
259*4882a593Smuzhiyun {
260*4882a593Smuzhiyun 	struct mlxsw_sp2_kvdl *kvdl = priv;
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	mlxsw_sp2_kvdl_parts_fini(kvdl);
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun const struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops = {
266*4882a593Smuzhiyun 	.priv_size = sizeof(struct mlxsw_sp2_kvdl),
267*4882a593Smuzhiyun 	.init = mlxsw_sp2_kvdl_init,
268*4882a593Smuzhiyun 	.fini = mlxsw_sp2_kvdl_fini,
269*4882a593Smuzhiyun 	.alloc = mlxsw_sp2_kvdl_alloc,
270*4882a593Smuzhiyun 	.free = mlxsw_sp2_kvdl_free,
271*4882a593Smuzhiyun 	.alloc_size_query = mlxsw_sp2_kvdl_alloc_size_query,
272*4882a593Smuzhiyun };
273