18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/kernel.h>
58c2ecf20Sopenharmony_ci#include <linux/bitops.h>
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include "spectrum.h"
88c2ecf20Sopenharmony_ci#include "core.h"
98c2ecf20Sopenharmony_ci#include "reg.h"
108c2ecf20Sopenharmony_ci#include "resources.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistruct mlxsw_sp2_kvdl_part_info {
138c2ecf20Sopenharmony_ci	u8 res_type;
148c2ecf20Sopenharmony_ci	/* For each defined partititon we need to know how many
158c2ecf20Sopenharmony_ci	 * usage bits we need and how many indexes there are
168c2ecf20Sopenharmony_ci	 * represented by a single bit. This could be got from FW
178c2ecf20Sopenharmony_ci	 * querying appropriate resources. So have the resource
188c2ecf20Sopenharmony_ci	 * ids for for this purpose in partition definition.
198c2ecf20Sopenharmony_ci	 */
208c2ecf20Sopenharmony_ci	enum mlxsw_res_id usage_bit_count_res_id;
218c2ecf20Sopenharmony_ci	enum mlxsw_res_id index_range_res_id;
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MLXSW_SP2_KVDL_PART_INFO(_entry_type, _res_type,			\
258c2ecf20Sopenharmony_ci				 _usage_bit_count_res_id, _index_range_res_id)	\
268c2ecf20Sopenharmony_ci[MLXSW_SP_KVDL_ENTRY_TYPE_##_entry_type] = {					\
278c2ecf20Sopenharmony_ci	.res_type = _res_type,							\
288c2ecf20Sopenharmony_ci	.usage_bit_count_res_id = MLXSW_RES_ID_##_usage_bit_count_res_id,	\
298c2ecf20Sopenharmony_ci	.index_range_res_id = MLXSW_RES_ID_##_index_range_res_id,		\
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct mlxsw_sp2_kvdl_part_info mlxsw_sp2_kvdl_parts_info[] = {
338c2ecf20Sopenharmony_ci	MLXSW_SP2_KVDL_PART_INFO(ADJ, 0x21, KVD_SIZE, MAX_KVD_LINEAR_RANGE),
348c2ecf20Sopenharmony_ci	MLXSW_SP2_KVDL_PART_INFO(ACTSET, 0x23, MAX_KVD_ACTION_SETS,
358c2ecf20Sopenharmony_ci				 MAX_KVD_ACTION_SETS),
368c2ecf20Sopenharmony_ci	MLXSW_SP2_KVDL_PART_INFO(PBS, 0x24, KVD_SIZE, KVD_SIZE),
378c2ecf20Sopenharmony_ci	MLXSW_SP2_KVDL_PART_INFO(MCRIGR, 0x26, KVD_SIZE, KVD_SIZE),
388c2ecf20Sopenharmony_ci	MLXSW_SP2_KVDL_PART_INFO(TNUMT, 0x29, KVD_SIZE, KVD_SIZE),
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define MLXSW_SP2_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp2_kvdl_parts_info)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct mlxsw_sp2_kvdl_part {
448c2ecf20Sopenharmony_ci	const struct mlxsw_sp2_kvdl_part_info *info;
458c2ecf20Sopenharmony_ci	unsigned int usage_bit_count;
468c2ecf20Sopenharmony_ci	unsigned int indexes_per_usage_bit;
478c2ecf20Sopenharmony_ci	unsigned int last_allocated_bit;
488c2ecf20Sopenharmony_ci	unsigned long usage[];	/* Usage bits */
498c2ecf20Sopenharmony_ci};
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistruct mlxsw_sp2_kvdl {
528c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl_part *parts[MLXSW_SP2_KVDL_PARTS_INFO_LEN];
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part *part,
568c2ecf20Sopenharmony_ci					      unsigned int bit_count,
578c2ecf20Sopenharmony_ci					      unsigned int *p_bit)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	unsigned int start_bit;
608c2ecf20Sopenharmony_ci	unsigned int bit;
618c2ecf20Sopenharmony_ci	unsigned int i;
628c2ecf20Sopenharmony_ci	bool wrap = false;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	start_bit = part->last_allocated_bit + 1;
658c2ecf20Sopenharmony_ci	if (start_bit == part->usage_bit_count)
668c2ecf20Sopenharmony_ci		start_bit = 0;
678c2ecf20Sopenharmony_ci	bit = start_bit;
688c2ecf20Sopenharmony_ciagain:
698c2ecf20Sopenharmony_ci	bit = find_next_zero_bit(part->usage, part->usage_bit_count, bit);
708c2ecf20Sopenharmony_ci	if (!wrap && bit + bit_count >= part->usage_bit_count) {
718c2ecf20Sopenharmony_ci		wrap = true;
728c2ecf20Sopenharmony_ci		bit = 0;
738c2ecf20Sopenharmony_ci		goto again;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	if (wrap && bit + bit_count >= start_bit)
768c2ecf20Sopenharmony_ci		return -ENOBUFS;
778c2ecf20Sopenharmony_ci	for (i = 0; i < bit_count; i++) {
788c2ecf20Sopenharmony_ci		if (test_bit(bit + i, part->usage)) {
798c2ecf20Sopenharmony_ci			bit += bit_count;
808c2ecf20Sopenharmony_ci			goto again;
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	*p_bit = bit;
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part *part,
888c2ecf20Sopenharmony_ci				     unsigned int size,
898c2ecf20Sopenharmony_ci				     u32 *p_kvdl_index)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	unsigned int bit_count;
928c2ecf20Sopenharmony_ci	unsigned int bit;
938c2ecf20Sopenharmony_ci	unsigned int i;
948c2ecf20Sopenharmony_ci	int err;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
978c2ecf20Sopenharmony_ci	err = mlxsw_sp2_kvdl_part_find_zero_bits(part, bit_count, &bit);
988c2ecf20Sopenharmony_ci	if (err)
998c2ecf20Sopenharmony_ci		return err;
1008c2ecf20Sopenharmony_ci	for (i = 0; i < bit_count; i++)
1018c2ecf20Sopenharmony_ci		__set_bit(bit + i, part->usage);
1028c2ecf20Sopenharmony_ci	*p_kvdl_index = bit * part->indexes_per_usage_bit;
1038c2ecf20Sopenharmony_ci	return 0;
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp *mlxsw_sp, u8 res_type,
1078c2ecf20Sopenharmony_ci				  u16 size, u32 kvdl_index)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	char *iedr_pl;
1108c2ecf20Sopenharmony_ci	int err;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	iedr_pl = kmalloc(MLXSW_REG_IEDR_LEN, GFP_KERNEL);
1138c2ecf20Sopenharmony_ci	if (!iedr_pl)
1148c2ecf20Sopenharmony_ci		return -ENOMEM;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	mlxsw_reg_iedr_pack(iedr_pl);
1178c2ecf20Sopenharmony_ci	mlxsw_reg_iedr_rec_pack(iedr_pl, 0, res_type, size, kvdl_index);
1188c2ecf20Sopenharmony_ci	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(iedr), iedr_pl);
1198c2ecf20Sopenharmony_ci	kfree(iedr_pl);
1208c2ecf20Sopenharmony_ci	return err;
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic void mlxsw_sp2_kvdl_part_free(struct mlxsw_sp *mlxsw_sp,
1248c2ecf20Sopenharmony_ci				     struct mlxsw_sp2_kvdl_part *part,
1258c2ecf20Sopenharmony_ci				     unsigned int size, u32 kvdl_index)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	unsigned int bit_count;
1288c2ecf20Sopenharmony_ci	unsigned int bit;
1298c2ecf20Sopenharmony_ci	unsigned int i;
1308c2ecf20Sopenharmony_ci	int err;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/* We need to ask FW to delete previously used KVD linear index */
1338c2ecf20Sopenharmony_ci	err = mlxsw_sp2_kvdl_rec_del(mlxsw_sp, part->info->res_type,
1348c2ecf20Sopenharmony_ci				     size, kvdl_index);
1358c2ecf20Sopenharmony_ci	if (err)
1368c2ecf20Sopenharmony_ci		return;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit);
1398c2ecf20Sopenharmony_ci	bit = kvdl_index / part->indexes_per_usage_bit;
1408c2ecf20Sopenharmony_ci	for (i = 0; i < bit_count; i++)
1418c2ecf20Sopenharmony_ci		__clear_bit(bit + i, part->usage);
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv,
1458c2ecf20Sopenharmony_ci				enum mlxsw_sp_kvdl_entry_type type,
1468c2ecf20Sopenharmony_ci				unsigned int entry_count,
1478c2ecf20Sopenharmony_ci				u32 *p_entry_index)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
1508c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl *kvdl = priv;
1518c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return mlxsw_sp2_kvdl_part_alloc(part, size, p_entry_index);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic void mlxsw_sp2_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv,
1578c2ecf20Sopenharmony_ci				enum mlxsw_sp_kvdl_entry_type type,
1588c2ecf20Sopenharmony_ci				unsigned int entry_count,
1598c2ecf20Sopenharmony_ci				int entry_index)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type);
1628c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl *kvdl = priv;
1638c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type];
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return mlxsw_sp2_kvdl_part_free(mlxsw_sp, part, size, entry_index);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp,
1698c2ecf20Sopenharmony_ci					   void *priv,
1708c2ecf20Sopenharmony_ci					   enum mlxsw_sp_kvdl_entry_type type,
1718c2ecf20Sopenharmony_ci					   unsigned int entry_count,
1728c2ecf20Sopenharmony_ci					   unsigned int *p_alloc_count)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	*p_alloc_count = entry_count;
1758c2ecf20Sopenharmony_ci	return 0;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_cistatic struct mlxsw_sp2_kvdl_part *
1798c2ecf20Sopenharmony_cimlxsw_sp2_kvdl_part_init(struct mlxsw_sp *mlxsw_sp,
1808c2ecf20Sopenharmony_ci			 const struct mlxsw_sp2_kvdl_part_info *info)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	unsigned int indexes_per_usage_bit;
1838c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl_part *part;
1848c2ecf20Sopenharmony_ci	unsigned int index_range;
1858c2ecf20Sopenharmony_ci	unsigned int usage_bit_count;
1868c2ecf20Sopenharmony_ci	size_t usage_size;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!mlxsw_core_res_valid(mlxsw_sp->core,
1898c2ecf20Sopenharmony_ci				  info->usage_bit_count_res_id) ||
1908c2ecf20Sopenharmony_ci	    !mlxsw_core_res_valid(mlxsw_sp->core,
1918c2ecf20Sopenharmony_ci				  info->index_range_res_id))
1928c2ecf20Sopenharmony_ci		return ERR_PTR(-EIO);
1938c2ecf20Sopenharmony_ci	usage_bit_count = mlxsw_core_res_get(mlxsw_sp->core,
1948c2ecf20Sopenharmony_ci					     info->usage_bit_count_res_id);
1958c2ecf20Sopenharmony_ci	index_range = mlxsw_core_res_get(mlxsw_sp->core,
1968c2ecf20Sopenharmony_ci					 info->index_range_res_id);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	/* For some partitions, one usage bit represents a group of indexes.
1998c2ecf20Sopenharmony_ci	 * That's why we compute the number of indexes per usage bit here,
2008c2ecf20Sopenharmony_ci	 * according to queried resources.
2018c2ecf20Sopenharmony_ci	 */
2028c2ecf20Sopenharmony_ci	indexes_per_usage_bit = index_range / usage_bit_count;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	usage_size = BITS_TO_LONGS(usage_bit_count) * sizeof(unsigned long);
2058c2ecf20Sopenharmony_ci	part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL);
2068c2ecf20Sopenharmony_ci	if (!part)
2078c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2088c2ecf20Sopenharmony_ci	part->info = info;
2098c2ecf20Sopenharmony_ci	part->usage_bit_count = usage_bit_count;
2108c2ecf20Sopenharmony_ci	part->indexes_per_usage_bit = indexes_per_usage_bit;
2118c2ecf20Sopenharmony_ci	part->last_allocated_bit = usage_bit_count - 1;
2128c2ecf20Sopenharmony_ci	return part;
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_cistatic void mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part *part)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	kfree(part);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp,
2218c2ecf20Sopenharmony_ci				     struct mlxsw_sp2_kvdl *kvdl)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	const struct mlxsw_sp2_kvdl_part_info *info;
2248c2ecf20Sopenharmony_ci	int i;
2258c2ecf20Sopenharmony_ci	int err;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++) {
2288c2ecf20Sopenharmony_ci		info = &mlxsw_sp2_kvdl_parts_info[i];
2298c2ecf20Sopenharmony_ci		kvdl->parts[i] = mlxsw_sp2_kvdl_part_init(mlxsw_sp, info);
2308c2ecf20Sopenharmony_ci		if (IS_ERR(kvdl->parts[i])) {
2318c2ecf20Sopenharmony_ci			err = PTR_ERR(kvdl->parts[i]);
2328c2ecf20Sopenharmony_ci			goto err_kvdl_part_init;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	return 0;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cierr_kvdl_part_init:
2388c2ecf20Sopenharmony_ci	for (i--; i >= 0; i--)
2398c2ecf20Sopenharmony_ci		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
2408c2ecf20Sopenharmony_ci	return err;
2418c2ecf20Sopenharmony_ci}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic void mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl *kvdl)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	int i;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++)
2488c2ecf20Sopenharmony_ci		mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]);
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_cistatic int mlxsw_sp2_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl *kvdl = priv;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return mlxsw_sp2_kvdl_parts_init(mlxsw_sp, kvdl);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void mlxsw_sp2_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct mlxsw_sp2_kvdl *kvdl = priv;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	mlxsw_sp2_kvdl_parts_fini(kvdl);
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ciconst struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops = {
2668c2ecf20Sopenharmony_ci	.priv_size = sizeof(struct mlxsw_sp2_kvdl),
2678c2ecf20Sopenharmony_ci	.init = mlxsw_sp2_kvdl_init,
2688c2ecf20Sopenharmony_ci	.fini = mlxsw_sp2_kvdl_fini,
2698c2ecf20Sopenharmony_ci	.alloc = mlxsw_sp2_kvdl_alloc,
2708c2ecf20Sopenharmony_ci	.free = mlxsw_sp2_kvdl_free,
2718c2ecf20Sopenharmony_ci	.alloc_size_query = mlxsw_sp2_kvdl_alloc_size_query,
2728c2ecf20Sopenharmony_ci};
273