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