1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /* Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
4 #include <linux/refcount.h>
12 u16 end_index; /* Exclusive. */
13 struct mutex lock; /* Protects PGT. */
14 bool smpe_index_valid;
17 struct mlxsw_sp_pgt_entry {
18 struct list_head ports_list;
23 struct mlxsw_sp_pgt_entry_port {
24 struct list_head list; /* Member of 'ports_list'. */
28 int mlxsw_sp_pgt_mid_alloc(struct mlxsw_sp *mlxsw_sp, u16 *p_mid)
32 mutex_lock(&mlxsw_sp->pgt->lock);
33 index = idr_alloc(&mlxsw_sp->pgt->pgt_idr, NULL, 0,
34 mlxsw_sp->pgt->end_index, GFP_KERNEL);
42 mutex_unlock(&mlxsw_sp->pgt->lock);
46 mutex_unlock(&mlxsw_sp->pgt->lock);
50 void mlxsw_sp_pgt_mid_free(struct mlxsw_sp *mlxsw_sp, u16 mid_base)
52 mutex_lock(&mlxsw_sp->pgt->lock);
53 WARN_ON(idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base));
54 mutex_unlock(&mlxsw_sp->pgt->lock);
57 int mlxsw_sp_pgt_mid_alloc_range(struct mlxsw_sp *mlxsw_sp, u16 *p_mid_base,
60 unsigned int mid_base;
63 mutex_lock(&mlxsw_sp->pgt->lock);
65 mid_base = idr_get_cursor(&mlxsw_sp->pgt->pgt_idr);
66 for (i = 0; i < count; i++) {
67 err = idr_alloc_cyclic(&mlxsw_sp->pgt->pgt_idr, NULL,
68 mid_base, mid_base + count, GFP_KERNEL);
70 goto err_idr_alloc_cyclic;
73 mutex_unlock(&mlxsw_sp->pgt->lock);
74 *p_mid_base = mid_base;
78 for (i--; i >= 0; i--)
79 idr_remove(&mlxsw_sp->pgt->pgt_idr, mid_base + i);
80 mutex_unlock(&mlxsw_sp->pgt->lock);
85 mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count)
87 struct idr *pgt_idr = &mlxsw_sp->pgt->pgt_idr;
90 mutex_lock(&mlxsw_sp->pgt->lock);
92 for (i = 0; i < count; i++)
93 WARN_ON_ONCE(idr_remove(pgt_idr, mid_base + i));
95 mutex_unlock(&mlxsw_sp->pgt->lock);
98 static struct mlxsw_sp_pgt_entry_port *
99 mlxsw_sp_pgt_entry_port_lookup(struct mlxsw_sp_pgt_entry *pgt_entry,
102 struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
104 list_for_each_entry(pgt_entry_port, &pgt_entry->ports_list, list) {
105 if (pgt_entry_port->local_port == local_port)
106 return pgt_entry_port;
112 static struct mlxsw_sp_pgt_entry *
113 mlxsw_sp_pgt_entry_create(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
115 struct mlxsw_sp_pgt_entry *pgt_entry;
119 pgt_entry = kzalloc(sizeof(*pgt_entry), GFP_KERNEL);
121 return ERR_PTR(-ENOMEM);
123 ret = idr_replace(&pgt->pgt_idr, pgt_entry, mid);
126 goto err_idr_replace;
129 INIT_LIST_HEAD(&pgt_entry->ports_list);
130 pgt_entry->index = mid;
131 pgt_entry->smpe_index = smpe;
139 static void mlxsw_sp_pgt_entry_destroy(struct mlxsw_sp_pgt *pgt,
140 struct mlxsw_sp_pgt_entry *pgt_entry)
142 WARN_ON(!list_empty(&pgt_entry->ports_list));
144 pgt_entry = idr_replace(&pgt->pgt_idr, NULL, pgt_entry->index);
145 if (WARN_ON(IS_ERR(pgt_entry)))
151 static struct mlxsw_sp_pgt_entry *
152 mlxsw_sp_pgt_entry_get(struct mlxsw_sp_pgt *pgt, u16 mid, u16 smpe)
154 struct mlxsw_sp_pgt_entry *pgt_entry;
156 pgt_entry = idr_find(&pgt->pgt_idr, mid);
160 return mlxsw_sp_pgt_entry_create(pgt, mid, smpe);
163 static void mlxsw_sp_pgt_entry_put(struct mlxsw_sp_pgt *pgt, u16 mid)
165 struct mlxsw_sp_pgt_entry *pgt_entry;
167 pgt_entry = idr_find(&pgt->pgt_idr, mid);
168 if (WARN_ON(!pgt_entry))
171 if (list_empty(&pgt_entry->ports_list))
172 mlxsw_sp_pgt_entry_destroy(pgt, pgt_entry);
175 static void mlxsw_sp_pgt_smid2_port_set(char *smid2_pl, u16 local_port,
178 mlxsw_reg_smid2_port_set(smid2_pl, local_port, member);
179 mlxsw_reg_smid2_port_mask_set(smid2_pl, local_port, 1);
183 mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp *mlxsw_sp,
184 const struct mlxsw_sp_pgt_entry *pgt_entry,
185 u16 local_port, bool member)
190 smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL);
194 mlxsw_reg_smid2_pack(smid2_pl, pgt_entry->index, 0, 0,
195 mlxsw_sp->pgt->smpe_index_valid,
196 pgt_entry->smpe_index);
198 mlxsw_sp_pgt_smid2_port_set(smid2_pl, local_port, member);
199 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl);
206 static struct mlxsw_sp_pgt_entry_port *
207 mlxsw_sp_pgt_entry_port_create(struct mlxsw_sp *mlxsw_sp,
208 struct mlxsw_sp_pgt_entry *pgt_entry,
211 struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
214 pgt_entry_port = kzalloc(sizeof(*pgt_entry_port), GFP_KERNEL);
216 return ERR_PTR(-ENOMEM);
218 err = mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry, local_port,
221 goto err_pgt_entry_port_write;
223 pgt_entry_port->local_port = local_port;
224 list_add(&pgt_entry_port->list, &pgt_entry->ports_list);
226 return pgt_entry_port;
228 err_pgt_entry_port_write:
229 kfree(pgt_entry_port);
234 mlxsw_sp_pgt_entry_port_destroy(struct mlxsw_sp *mlxsw_sp,
235 struct mlxsw_sp_pgt_entry *pgt_entry,
236 struct mlxsw_sp_pgt_entry_port *pgt_entry_port)
239 list_del(&pgt_entry_port->list);
240 mlxsw_sp_pgt_entry_port_write(mlxsw_sp, pgt_entry,
241 pgt_entry_port->local_port, false);
242 kfree(pgt_entry_port);
245 static int mlxsw_sp_pgt_entry_port_add(struct mlxsw_sp *mlxsw_sp, u16 mid,
246 u16 smpe, u16 local_port)
248 struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
249 struct mlxsw_sp_pgt_entry *pgt_entry;
252 mutex_lock(&mlxsw_sp->pgt->lock);
254 pgt_entry = mlxsw_sp_pgt_entry_get(mlxsw_sp->pgt, mid, smpe);
255 if (IS_ERR(pgt_entry)) {
256 err = PTR_ERR(pgt_entry);
257 goto err_pgt_entry_get;
260 pgt_entry_port = mlxsw_sp_pgt_entry_port_create(mlxsw_sp, pgt_entry,
262 if (IS_ERR(pgt_entry_port)) {
263 err = PTR_ERR(pgt_entry_port);
264 goto err_pgt_entry_port_get;
267 mutex_unlock(&mlxsw_sp->pgt->lock);
270 err_pgt_entry_port_get:
271 mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
273 mutex_unlock(&mlxsw_sp->pgt->lock);
277 static void mlxsw_sp_pgt_entry_port_del(struct mlxsw_sp *mlxsw_sp,
278 u16 mid, u16 smpe, u16 local_port)
280 struct mlxsw_sp_pgt_entry_port *pgt_entry_port;
281 struct mlxsw_sp_pgt_entry *pgt_entry;
283 mutex_lock(&mlxsw_sp->pgt->lock);
285 pgt_entry = idr_find(&mlxsw_sp->pgt->pgt_idr, mid);
289 pgt_entry_port = mlxsw_sp_pgt_entry_port_lookup(pgt_entry, local_port);
293 mlxsw_sp_pgt_entry_port_destroy(mlxsw_sp, pgt_entry, pgt_entry_port);
294 mlxsw_sp_pgt_entry_put(mlxsw_sp->pgt, mid);
297 mutex_unlock(&mlxsw_sp->pgt->lock);
300 int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid,
301 u16 smpe, u16 local_port, bool member)
304 return mlxsw_sp_pgt_entry_port_add(mlxsw_sp, mid, smpe,
307 mlxsw_sp_pgt_entry_port_del(mlxsw_sp, mid, smpe, local_port);
311 int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp)
313 struct mlxsw_sp_pgt *pgt;
315 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, PGT_SIZE))
318 pgt = kzalloc(sizeof(*mlxsw_sp->pgt), GFP_KERNEL);
322 idr_init(&pgt->pgt_idr);
323 pgt->end_index = MLXSW_CORE_RES_GET(mlxsw_sp->core, PGT_SIZE);
324 mutex_init(&pgt->lock);
325 pgt->smpe_index_valid = mlxsw_sp->pgt_smpe_index_valid;
330 void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp)
332 mutex_destroy(&mlxsw_sp->pgt->lock);
333 WARN_ON(!idr_is_empty(&mlxsw_sp->pgt->pgt_idr));
334 idr_destroy(&mlxsw_sp->pgt->pgt_idr);
335 kfree(mlxsw_sp->pgt);