162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 262306a36Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/kernel.h> 562306a36Sopenharmony_ci#include <linux/bitops.h> 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "spectrum.h" 862306a36Sopenharmony_ci#include "core.h" 962306a36Sopenharmony_ci#include "reg.h" 1062306a36Sopenharmony_ci#include "resources.h" 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_cistruct mlxsw_sp2_kvdl_part_info { 1362306a36Sopenharmony_ci u8 res_type; 1462306a36Sopenharmony_ci /* For each defined partititon we need to know how many 1562306a36Sopenharmony_ci * usage bits we need and how many indexes there are 1662306a36Sopenharmony_ci * represented by a single bit. This could be got from FW 1762306a36Sopenharmony_ci * querying appropriate resources. So have the resource 1862306a36Sopenharmony_ci * ids for this purpose in partition definition. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci enum mlxsw_res_id usage_bit_count_res_id; 2162306a36Sopenharmony_ci enum mlxsw_res_id index_range_res_id; 2262306a36Sopenharmony_ci}; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MLXSW_SP2_KVDL_PART_INFO(_entry_type, _res_type, \ 2562306a36Sopenharmony_ci _usage_bit_count_res_id, _index_range_res_id) \ 2662306a36Sopenharmony_ci[MLXSW_SP_KVDL_ENTRY_TYPE_##_entry_type] = { \ 2762306a36Sopenharmony_ci .res_type = _res_type, \ 2862306a36Sopenharmony_ci .usage_bit_count_res_id = MLXSW_RES_ID_##_usage_bit_count_res_id, \ 2962306a36Sopenharmony_ci .index_range_res_id = MLXSW_RES_ID_##_index_range_res_id, \ 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic const struct mlxsw_sp2_kvdl_part_info mlxsw_sp2_kvdl_parts_info[] = { 3362306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(ADJ, 0x21, KVD_SIZE, MAX_KVD_LINEAR_RANGE), 3462306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(ACTSET, 0x23, MAX_KVD_ACTION_SETS, 3562306a36Sopenharmony_ci MAX_KVD_ACTION_SETS), 3662306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(PBS, 0x24, KVD_SIZE, KVD_SIZE), 3762306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(MCRIGR, 0x26, KVD_SIZE, KVD_SIZE), 3862306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(IPV6_ADDRESS, 0x28, KVD_SIZE, KVD_SIZE), 3962306a36Sopenharmony_ci MLXSW_SP2_KVDL_PART_INFO(TNUMT, 0x29, KVD_SIZE, KVD_SIZE), 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define MLXSW_SP2_KVDL_PARTS_INFO_LEN ARRAY_SIZE(mlxsw_sp2_kvdl_parts_info) 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct mlxsw_sp2_kvdl_part { 4562306a36Sopenharmony_ci const struct mlxsw_sp2_kvdl_part_info *info; 4662306a36Sopenharmony_ci unsigned int usage_bit_count; 4762306a36Sopenharmony_ci unsigned int indexes_per_usage_bit; 4862306a36Sopenharmony_ci unsigned int last_allocated_bit; 4962306a36Sopenharmony_ci unsigned long usage[]; /* Usage bits */ 5062306a36Sopenharmony_ci}; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistruct mlxsw_sp2_kvdl { 5362306a36Sopenharmony_ci struct mlxsw_sp2_kvdl_part *parts[MLXSW_SP2_KVDL_PARTS_INFO_LEN]; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_part_find_zero_bits(struct mlxsw_sp2_kvdl_part *part, 5762306a36Sopenharmony_ci unsigned int bit_count, 5862306a36Sopenharmony_ci unsigned int *p_bit) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci unsigned int start_bit; 6162306a36Sopenharmony_ci unsigned int bit; 6262306a36Sopenharmony_ci unsigned int i; 6362306a36Sopenharmony_ci bool wrap = false; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci start_bit = part->last_allocated_bit + 1; 6662306a36Sopenharmony_ci if (start_bit == part->usage_bit_count) 6762306a36Sopenharmony_ci start_bit = 0; 6862306a36Sopenharmony_ci bit = start_bit; 6962306a36Sopenharmony_ciagain: 7062306a36Sopenharmony_ci bit = find_next_zero_bit(part->usage, part->usage_bit_count, bit); 7162306a36Sopenharmony_ci if (!wrap && bit + bit_count >= part->usage_bit_count) { 7262306a36Sopenharmony_ci wrap = true; 7362306a36Sopenharmony_ci bit = 0; 7462306a36Sopenharmony_ci goto again; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci if (wrap && bit + bit_count >= start_bit) 7762306a36Sopenharmony_ci return -ENOBUFS; 7862306a36Sopenharmony_ci for (i = 0; i < bit_count; i++) { 7962306a36Sopenharmony_ci if (test_bit(bit + i, part->usage)) { 8062306a36Sopenharmony_ci bit += bit_count; 8162306a36Sopenharmony_ci goto again; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci *p_bit = bit; 8562306a36Sopenharmony_ci return 0; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_part_alloc(struct mlxsw_sp2_kvdl_part *part, 8962306a36Sopenharmony_ci unsigned int size, 9062306a36Sopenharmony_ci u32 *p_kvdl_index) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci unsigned int bit_count; 9362306a36Sopenharmony_ci unsigned int bit; 9462306a36Sopenharmony_ci unsigned int i; 9562306a36Sopenharmony_ci int err; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit); 9862306a36Sopenharmony_ci err = mlxsw_sp2_kvdl_part_find_zero_bits(part, bit_count, &bit); 9962306a36Sopenharmony_ci if (err) 10062306a36Sopenharmony_ci return err; 10162306a36Sopenharmony_ci for (i = 0; i < bit_count; i++) 10262306a36Sopenharmony_ci __set_bit(bit + i, part->usage); 10362306a36Sopenharmony_ci *p_kvdl_index = bit * part->indexes_per_usage_bit; 10462306a36Sopenharmony_ci return 0; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_rec_del(struct mlxsw_sp *mlxsw_sp, u8 res_type, 10862306a36Sopenharmony_ci u16 size, u32 kvdl_index) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci char *iedr_pl; 11162306a36Sopenharmony_ci int err; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci iedr_pl = kmalloc(MLXSW_REG_IEDR_LEN, GFP_KERNEL); 11462306a36Sopenharmony_ci if (!iedr_pl) 11562306a36Sopenharmony_ci return -ENOMEM; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci mlxsw_reg_iedr_pack(iedr_pl); 11862306a36Sopenharmony_ci mlxsw_reg_iedr_rec_pack(iedr_pl, 0, res_type, size, kvdl_index); 11962306a36Sopenharmony_ci err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(iedr), iedr_pl); 12062306a36Sopenharmony_ci kfree(iedr_pl); 12162306a36Sopenharmony_ci return err; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic void mlxsw_sp2_kvdl_part_free(struct mlxsw_sp *mlxsw_sp, 12562306a36Sopenharmony_ci struct mlxsw_sp2_kvdl_part *part, 12662306a36Sopenharmony_ci unsigned int size, u32 kvdl_index) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned int bit_count; 12962306a36Sopenharmony_ci unsigned int bit; 13062306a36Sopenharmony_ci unsigned int i; 13162306a36Sopenharmony_ci int err; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* We need to ask FW to delete previously used KVD linear index */ 13462306a36Sopenharmony_ci err = mlxsw_sp2_kvdl_rec_del(mlxsw_sp, part->info->res_type, 13562306a36Sopenharmony_ci size, kvdl_index); 13662306a36Sopenharmony_ci if (err) 13762306a36Sopenharmony_ci return; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci bit_count = DIV_ROUND_UP(size, part->indexes_per_usage_bit); 14062306a36Sopenharmony_ci bit = kvdl_index / part->indexes_per_usage_bit; 14162306a36Sopenharmony_ci for (i = 0; i < bit_count; i++) 14262306a36Sopenharmony_ci __clear_bit(bit + i, part->usage); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_alloc(struct mlxsw_sp *mlxsw_sp, void *priv, 14662306a36Sopenharmony_ci enum mlxsw_sp_kvdl_entry_type type, 14762306a36Sopenharmony_ci unsigned int entry_count, 14862306a36Sopenharmony_ci u32 *p_entry_index) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type); 15162306a36Sopenharmony_ci struct mlxsw_sp2_kvdl *kvdl = priv; 15262306a36Sopenharmony_ci struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type]; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return mlxsw_sp2_kvdl_part_alloc(part, size, p_entry_index); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void mlxsw_sp2_kvdl_free(struct mlxsw_sp *mlxsw_sp, void *priv, 15862306a36Sopenharmony_ci enum mlxsw_sp_kvdl_entry_type type, 15962306a36Sopenharmony_ci unsigned int entry_count, 16062306a36Sopenharmony_ci int entry_index) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci unsigned int size = entry_count * mlxsw_sp_kvdl_entry_size(type); 16362306a36Sopenharmony_ci struct mlxsw_sp2_kvdl *kvdl = priv; 16462306a36Sopenharmony_ci struct mlxsw_sp2_kvdl_part *part = kvdl->parts[type]; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci return mlxsw_sp2_kvdl_part_free(mlxsw_sp, part, size, entry_index); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_alloc_size_query(struct mlxsw_sp *mlxsw_sp, 17062306a36Sopenharmony_ci void *priv, 17162306a36Sopenharmony_ci enum mlxsw_sp_kvdl_entry_type type, 17262306a36Sopenharmony_ci unsigned int entry_count, 17362306a36Sopenharmony_ci unsigned int *p_alloc_count) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci *p_alloc_count = entry_count; 17662306a36Sopenharmony_ci return 0; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct mlxsw_sp2_kvdl_part * 18062306a36Sopenharmony_cimlxsw_sp2_kvdl_part_init(struct mlxsw_sp *mlxsw_sp, 18162306a36Sopenharmony_ci const struct mlxsw_sp2_kvdl_part_info *info) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci unsigned int indexes_per_usage_bit; 18462306a36Sopenharmony_ci struct mlxsw_sp2_kvdl_part *part; 18562306a36Sopenharmony_ci unsigned int index_range; 18662306a36Sopenharmony_ci unsigned int usage_bit_count; 18762306a36Sopenharmony_ci size_t usage_size; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (!mlxsw_core_res_valid(mlxsw_sp->core, 19062306a36Sopenharmony_ci info->usage_bit_count_res_id) || 19162306a36Sopenharmony_ci !mlxsw_core_res_valid(mlxsw_sp->core, 19262306a36Sopenharmony_ci info->index_range_res_id)) 19362306a36Sopenharmony_ci return ERR_PTR(-EIO); 19462306a36Sopenharmony_ci usage_bit_count = mlxsw_core_res_get(mlxsw_sp->core, 19562306a36Sopenharmony_ci info->usage_bit_count_res_id); 19662306a36Sopenharmony_ci index_range = mlxsw_core_res_get(mlxsw_sp->core, 19762306a36Sopenharmony_ci info->index_range_res_id); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* For some partitions, one usage bit represents a group of indexes. 20062306a36Sopenharmony_ci * That's why we compute the number of indexes per usage bit here, 20162306a36Sopenharmony_ci * according to queried resources. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci indexes_per_usage_bit = index_range / usage_bit_count; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci usage_size = BITS_TO_LONGS(usage_bit_count) * sizeof(unsigned long); 20662306a36Sopenharmony_ci part = kzalloc(sizeof(*part) + usage_size, GFP_KERNEL); 20762306a36Sopenharmony_ci if (!part) 20862306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 20962306a36Sopenharmony_ci part->info = info; 21062306a36Sopenharmony_ci part->usage_bit_count = usage_bit_count; 21162306a36Sopenharmony_ci part->indexes_per_usage_bit = indexes_per_usage_bit; 21262306a36Sopenharmony_ci part->last_allocated_bit = usage_bit_count - 1; 21362306a36Sopenharmony_ci return part; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void mlxsw_sp2_kvdl_part_fini(struct mlxsw_sp2_kvdl_part *part) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci kfree(part); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_parts_init(struct mlxsw_sp *mlxsw_sp, 22262306a36Sopenharmony_ci struct mlxsw_sp2_kvdl *kvdl) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci const struct mlxsw_sp2_kvdl_part_info *info; 22562306a36Sopenharmony_ci int i; 22662306a36Sopenharmony_ci int err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++) { 22962306a36Sopenharmony_ci info = &mlxsw_sp2_kvdl_parts_info[i]; 23062306a36Sopenharmony_ci kvdl->parts[i] = mlxsw_sp2_kvdl_part_init(mlxsw_sp, info); 23162306a36Sopenharmony_ci if (IS_ERR(kvdl->parts[i])) { 23262306a36Sopenharmony_ci err = PTR_ERR(kvdl->parts[i]); 23362306a36Sopenharmony_ci goto err_kvdl_part_init; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cierr_kvdl_part_init: 23962306a36Sopenharmony_ci for (i--; i >= 0; i--) 24062306a36Sopenharmony_ci mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]); 24162306a36Sopenharmony_ci return err; 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic void mlxsw_sp2_kvdl_parts_fini(struct mlxsw_sp2_kvdl *kvdl) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci int i; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci for (i = 0; i < MLXSW_SP2_KVDL_PARTS_INFO_LEN; i++) 24962306a36Sopenharmony_ci mlxsw_sp2_kvdl_part_fini(kvdl->parts[i]); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int mlxsw_sp2_kvdl_init(struct mlxsw_sp *mlxsw_sp, void *priv) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct mlxsw_sp2_kvdl *kvdl = priv; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return mlxsw_sp2_kvdl_parts_init(mlxsw_sp, kvdl); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void mlxsw_sp2_kvdl_fini(struct mlxsw_sp *mlxsw_sp, void *priv) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct mlxsw_sp2_kvdl *kvdl = priv; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci mlxsw_sp2_kvdl_parts_fini(kvdl); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciconst struct mlxsw_sp_kvdl_ops mlxsw_sp2_kvdl_ops = { 26762306a36Sopenharmony_ci .priv_size = sizeof(struct mlxsw_sp2_kvdl), 26862306a36Sopenharmony_ci .init = mlxsw_sp2_kvdl_init, 26962306a36Sopenharmony_ci .fini = mlxsw_sp2_kvdl_fini, 27062306a36Sopenharmony_ci .alloc = mlxsw_sp2_kvdl_alloc, 27162306a36Sopenharmony_ci .free = mlxsw_sp2_kvdl_free, 27262306a36Sopenharmony_ci .alloc_size_query = mlxsw_sp2_kvdl_alloc_size_query, 27362306a36Sopenharmony_ci}; 274