162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2016 Hisilicon Limited. 362306a36Sopenharmony_ci * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include "hns_roce_device.h" 3562306a36Sopenharmony_ci#include "hns_roce_hem.h" 3662306a36Sopenharmony_ci#include "hns_roce_common.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define HEM_INDEX_BUF BIT(0) 3962306a36Sopenharmony_ci#define HEM_INDEX_L0 BIT(1) 4062306a36Sopenharmony_ci#define HEM_INDEX_L1 BIT(2) 4162306a36Sopenharmony_cistruct hns_roce_hem_index { 4262306a36Sopenharmony_ci u64 buf; 4362306a36Sopenharmony_ci u64 l0; 4462306a36Sopenharmony_ci u64 l1; 4562306a36Sopenharmony_ci u32 inited; /* indicate which index is available */ 4662306a36Sopenharmony_ci}; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cibool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci int hop_num = 0; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci switch (type) { 5362306a36Sopenharmony_ci case HEM_TYPE_QPC: 5462306a36Sopenharmony_ci hop_num = hr_dev->caps.qpc_hop_num; 5562306a36Sopenharmony_ci break; 5662306a36Sopenharmony_ci case HEM_TYPE_MTPT: 5762306a36Sopenharmony_ci hop_num = hr_dev->caps.mpt_hop_num; 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci case HEM_TYPE_CQC: 6062306a36Sopenharmony_ci hop_num = hr_dev->caps.cqc_hop_num; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case HEM_TYPE_SRQC: 6362306a36Sopenharmony_ci hop_num = hr_dev->caps.srqc_hop_num; 6462306a36Sopenharmony_ci break; 6562306a36Sopenharmony_ci case HEM_TYPE_SCCC: 6662306a36Sopenharmony_ci hop_num = hr_dev->caps.sccc_hop_num; 6762306a36Sopenharmony_ci break; 6862306a36Sopenharmony_ci case HEM_TYPE_QPC_TIMER: 6962306a36Sopenharmony_ci hop_num = hr_dev->caps.qpc_timer_hop_num; 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci case HEM_TYPE_CQC_TIMER: 7262306a36Sopenharmony_ci hop_num = hr_dev->caps.cqc_timer_hop_num; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case HEM_TYPE_GMV: 7562306a36Sopenharmony_ci hop_num = hr_dev->caps.gmv_hop_num; 7662306a36Sopenharmony_ci break; 7762306a36Sopenharmony_ci default: 7862306a36Sopenharmony_ci return false; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return hop_num; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 hem_idx, 8562306a36Sopenharmony_ci u32 bt_chunk_num, u64 hem_max_num) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u64 start_idx = round_down(hem_idx, bt_chunk_num); 8862306a36Sopenharmony_ci u64 check_max_num = start_idx + bt_chunk_num; 8962306a36Sopenharmony_ci u64 i; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci for (i = start_idx; (i < check_max_num) && (i < hem_max_num); i++) 9262306a36Sopenharmony_ci if (i != hem_idx && hem[i]) 9362306a36Sopenharmony_ci return false; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return true; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic bool hns_roce_check_bt_null(u64 **bt, u64 ba_idx, u32 bt_chunk_num) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u64 start_idx = round_down(ba_idx, bt_chunk_num); 10162306a36Sopenharmony_ci int i; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (i = 0; i < bt_chunk_num; i++) 10462306a36Sopenharmony_ci if (i != ba_idx && bt[start_idx + i]) 10562306a36Sopenharmony_ci return false; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return true; 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int hns_roce_get_bt_num(u32 table_type, u32 hop_num) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci if (check_whether_bt_num_3(table_type, hop_num)) 11362306a36Sopenharmony_ci return 3; 11462306a36Sopenharmony_ci else if (check_whether_bt_num_2(table_type, hop_num)) 11562306a36Sopenharmony_ci return 2; 11662306a36Sopenharmony_ci else if (check_whether_bt_num_1(table_type, hop_num)) 11762306a36Sopenharmony_ci return 1; 11862306a36Sopenharmony_ci else 11962306a36Sopenharmony_ci return 0; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int get_hem_table_config(struct hns_roce_dev *hr_dev, 12362306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 12462306a36Sopenharmony_ci u32 type) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci switch (type) { 12962306a36Sopenharmony_ci case HEM_TYPE_QPC: 13062306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_buf_pg_sz 13162306a36Sopenharmony_ci + PAGE_SHIFT); 13262306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_ba_pg_sz 13362306a36Sopenharmony_ci + PAGE_SHIFT); 13462306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.qpc_bt_num; 13562306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.qpc_hop_num; 13662306a36Sopenharmony_ci break; 13762306a36Sopenharmony_ci case HEM_TYPE_MTPT: 13862306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.mpt_buf_pg_sz 13962306a36Sopenharmony_ci + PAGE_SHIFT); 14062306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.mpt_ba_pg_sz 14162306a36Sopenharmony_ci + PAGE_SHIFT); 14262306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.mpt_bt_num; 14362306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.mpt_hop_num; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci case HEM_TYPE_CQC: 14662306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_buf_pg_sz 14762306a36Sopenharmony_ci + PAGE_SHIFT); 14862306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_ba_pg_sz 14962306a36Sopenharmony_ci + PAGE_SHIFT); 15062306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.cqc_bt_num; 15162306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.cqc_hop_num; 15262306a36Sopenharmony_ci break; 15362306a36Sopenharmony_ci case HEM_TYPE_SCCC: 15462306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz 15562306a36Sopenharmony_ci + PAGE_SHIFT); 15662306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz 15762306a36Sopenharmony_ci + PAGE_SHIFT); 15862306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.sccc_bt_num; 15962306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.sccc_hop_num; 16062306a36Sopenharmony_ci break; 16162306a36Sopenharmony_ci case HEM_TYPE_QPC_TIMER: 16262306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz 16362306a36Sopenharmony_ci + PAGE_SHIFT); 16462306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz 16562306a36Sopenharmony_ci + PAGE_SHIFT); 16662306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.qpc_timer_bt_num; 16762306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.qpc_timer_hop_num; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case HEM_TYPE_CQC_TIMER: 17062306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz 17162306a36Sopenharmony_ci + PAGE_SHIFT); 17262306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz 17362306a36Sopenharmony_ci + PAGE_SHIFT); 17462306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.cqc_timer_bt_num; 17562306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.cqc_timer_hop_num; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci case HEM_TYPE_SRQC: 17862306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz 17962306a36Sopenharmony_ci + PAGE_SHIFT); 18062306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.srqc_ba_pg_sz 18162306a36Sopenharmony_ci + PAGE_SHIFT); 18262306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.srqc_bt_num; 18362306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.srqc_hop_num; 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci case HEM_TYPE_GMV: 18662306a36Sopenharmony_ci mhop->buf_chunk_size = 1 << (hr_dev->caps.gmv_buf_pg_sz + 18762306a36Sopenharmony_ci PAGE_SHIFT); 18862306a36Sopenharmony_ci mhop->bt_chunk_size = 1 << (hr_dev->caps.gmv_ba_pg_sz + 18962306a36Sopenharmony_ci PAGE_SHIFT); 19062306a36Sopenharmony_ci mhop->ba_l0_num = hr_dev->caps.gmv_bt_num; 19162306a36Sopenharmony_ci mhop->hop_num = hr_dev->caps.gmv_hop_num; 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci default: 19462306a36Sopenharmony_ci dev_err(dev, "table %u not support multi-hop addressing!\n", 19562306a36Sopenharmony_ci type); 19662306a36Sopenharmony_ci return -EINVAL; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciint hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev, 20362306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long *obj, 20462306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 20762306a36Sopenharmony_ci u32 chunk_ba_num; 20862306a36Sopenharmony_ci u32 chunk_size; 20962306a36Sopenharmony_ci u32 table_idx; 21062306a36Sopenharmony_ci u32 bt_num; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (get_hem_table_config(hr_dev, mhop, table->type)) 21362306a36Sopenharmony_ci return -EINVAL; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci if (!obj) 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci /* 21962306a36Sopenharmony_ci * QPC/MTPT/CQC/SRQC/SCCC alloc hem for buffer pages. 22062306a36Sopenharmony_ci * MTT/CQE alloc hem for bt pages. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num); 22362306a36Sopenharmony_ci chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN; 22462306a36Sopenharmony_ci chunk_size = table->type < HEM_TYPE_MTT ? mhop->buf_chunk_size : 22562306a36Sopenharmony_ci mhop->bt_chunk_size; 22662306a36Sopenharmony_ci table_idx = *obj / (chunk_size / table->obj_size); 22762306a36Sopenharmony_ci switch (bt_num) { 22862306a36Sopenharmony_ci case 3: 22962306a36Sopenharmony_ci mhop->l2_idx = table_idx & (chunk_ba_num - 1); 23062306a36Sopenharmony_ci mhop->l1_idx = table_idx / chunk_ba_num & (chunk_ba_num - 1); 23162306a36Sopenharmony_ci mhop->l0_idx = (table_idx / chunk_ba_num) / chunk_ba_num; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case 2: 23462306a36Sopenharmony_ci mhop->l1_idx = table_idx & (chunk_ba_num - 1); 23562306a36Sopenharmony_ci mhop->l0_idx = table_idx / chunk_ba_num; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case 1: 23862306a36Sopenharmony_ci mhop->l0_idx = table_idx; 23962306a36Sopenharmony_ci break; 24062306a36Sopenharmony_ci default: 24162306a36Sopenharmony_ci dev_err(dev, "table %u not support hop_num = %u!\n", 24262306a36Sopenharmony_ci table->type, mhop->hop_num); 24362306a36Sopenharmony_ci return -EINVAL; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci if (mhop->l0_idx >= mhop->ba_l0_num) 24662306a36Sopenharmony_ci mhop->l0_idx %= mhop->ba_l0_num; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic struct hns_roce_hem *hns_roce_alloc_hem(struct hns_roce_dev *hr_dev, 25262306a36Sopenharmony_ci int npages, 25362306a36Sopenharmony_ci unsigned long hem_alloc_size, 25462306a36Sopenharmony_ci gfp_t gfp_mask) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci struct hns_roce_hem_chunk *chunk = NULL; 25762306a36Sopenharmony_ci struct hns_roce_hem *hem; 25862306a36Sopenharmony_ci struct scatterlist *mem; 25962306a36Sopenharmony_ci int order; 26062306a36Sopenharmony_ci void *buf; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci WARN_ON(gfp_mask & __GFP_HIGHMEM); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci hem = kmalloc(sizeof(*hem), 26562306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 26662306a36Sopenharmony_ci if (!hem) 26762306a36Sopenharmony_ci return NULL; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci INIT_LIST_HEAD(&hem->chunk_list); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci order = get_order(hem_alloc_size); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci while (npages > 0) { 27462306a36Sopenharmony_ci if (!chunk) { 27562306a36Sopenharmony_ci chunk = kmalloc(sizeof(*chunk), 27662306a36Sopenharmony_ci gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN)); 27762306a36Sopenharmony_ci if (!chunk) 27862306a36Sopenharmony_ci goto fail; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci sg_init_table(chunk->mem, HNS_ROCE_HEM_CHUNK_LEN); 28162306a36Sopenharmony_ci chunk->npages = 0; 28262306a36Sopenharmony_ci chunk->nsg = 0; 28362306a36Sopenharmony_ci memset(chunk->buf, 0, sizeof(chunk->buf)); 28462306a36Sopenharmony_ci list_add_tail(&chunk->list, &hem->chunk_list); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci while (1 << order > npages) 28862306a36Sopenharmony_ci --order; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * Alloc memory one time. If failed, don't alloc small block 29262306a36Sopenharmony_ci * memory, directly return fail. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci mem = &chunk->mem[chunk->npages]; 29562306a36Sopenharmony_ci buf = dma_alloc_coherent(hr_dev->dev, PAGE_SIZE << order, 29662306a36Sopenharmony_ci &sg_dma_address(mem), gfp_mask); 29762306a36Sopenharmony_ci if (!buf) 29862306a36Sopenharmony_ci goto fail; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci chunk->buf[chunk->npages] = buf; 30162306a36Sopenharmony_ci sg_dma_len(mem) = PAGE_SIZE << order; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ++chunk->npages; 30462306a36Sopenharmony_ci ++chunk->nsg; 30562306a36Sopenharmony_ci npages -= 1 << order; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci return hem; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cifail: 31162306a36Sopenharmony_ci hns_roce_free_hem(hr_dev, hem); 31262306a36Sopenharmony_ci return NULL; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_civoid hns_roce_free_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem *hem) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct hns_roce_hem_chunk *chunk, *tmp; 31862306a36Sopenharmony_ci int i; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (!hem) 32162306a36Sopenharmony_ci return; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci list_for_each_entry_safe(chunk, tmp, &hem->chunk_list, list) { 32462306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) 32562306a36Sopenharmony_ci dma_free_coherent(hr_dev->dev, 32662306a36Sopenharmony_ci sg_dma_len(&chunk->mem[i]), 32762306a36Sopenharmony_ci chunk->buf[i], 32862306a36Sopenharmony_ci sg_dma_address(&chunk->mem[i])); 32962306a36Sopenharmony_ci kfree(chunk); 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci kfree(hem); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int calc_hem_config(struct hns_roce_dev *hr_dev, 33662306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long obj, 33762306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 33862306a36Sopenharmony_ci struct hns_roce_hem_index *index) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct ib_device *ibdev = &hr_dev->ib_dev; 34162306a36Sopenharmony_ci unsigned long mhop_obj = obj; 34262306a36Sopenharmony_ci u32 l0_idx, l1_idx, l2_idx; 34362306a36Sopenharmony_ci u32 chunk_ba_num; 34462306a36Sopenharmony_ci u32 bt_num; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ret = hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, mhop); 34862306a36Sopenharmony_ci if (ret) 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci l0_idx = mhop->l0_idx; 35262306a36Sopenharmony_ci l1_idx = mhop->l1_idx; 35362306a36Sopenharmony_ci l2_idx = mhop->l2_idx; 35462306a36Sopenharmony_ci chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN; 35562306a36Sopenharmony_ci bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num); 35662306a36Sopenharmony_ci switch (bt_num) { 35762306a36Sopenharmony_ci case 3: 35862306a36Sopenharmony_ci index->l1 = l0_idx * chunk_ba_num + l1_idx; 35962306a36Sopenharmony_ci index->l0 = l0_idx; 36062306a36Sopenharmony_ci index->buf = l0_idx * chunk_ba_num * chunk_ba_num + 36162306a36Sopenharmony_ci l1_idx * chunk_ba_num + l2_idx; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci case 2: 36462306a36Sopenharmony_ci index->l0 = l0_idx; 36562306a36Sopenharmony_ci index->buf = l0_idx * chunk_ba_num + l1_idx; 36662306a36Sopenharmony_ci break; 36762306a36Sopenharmony_ci case 1: 36862306a36Sopenharmony_ci index->buf = l0_idx; 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci default: 37162306a36Sopenharmony_ci ibdev_err(ibdev, "table %u not support mhop.hop_num = %u!\n", 37262306a36Sopenharmony_ci table->type, mhop->hop_num); 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (unlikely(index->buf >= table->num_hem)) { 37762306a36Sopenharmony_ci ibdev_err(ibdev, "table %u exceed hem limt idx %llu, max %lu!\n", 37862306a36Sopenharmony_ci table->type, index->buf, table->num_hem); 37962306a36Sopenharmony_ci return -EINVAL; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci} 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_cistatic void free_mhop_hem(struct hns_roce_dev *hr_dev, 38662306a36Sopenharmony_ci struct hns_roce_hem_table *table, 38762306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 38862306a36Sopenharmony_ci struct hns_roce_hem_index *index) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci u32 bt_size = mhop->bt_chunk_size; 39162306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (index->inited & HEM_INDEX_BUF) { 39462306a36Sopenharmony_ci hns_roce_free_hem(hr_dev, table->hem[index->buf]); 39562306a36Sopenharmony_ci table->hem[index->buf] = NULL; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L1) { 39962306a36Sopenharmony_ci dma_free_coherent(dev, bt_size, table->bt_l1[index->l1], 40062306a36Sopenharmony_ci table->bt_l1_dma_addr[index->l1]); 40162306a36Sopenharmony_ci table->bt_l1[index->l1] = NULL; 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L0) { 40562306a36Sopenharmony_ci dma_free_coherent(dev, bt_size, table->bt_l0[index->l0], 40662306a36Sopenharmony_ci table->bt_l0_dma_addr[index->l0]); 40762306a36Sopenharmony_ci table->bt_l0[index->l0] = NULL; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int alloc_mhop_hem(struct hns_roce_dev *hr_dev, 41262306a36Sopenharmony_ci struct hns_roce_hem_table *table, 41362306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 41462306a36Sopenharmony_ci struct hns_roce_hem_index *index) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci u32 bt_size = mhop->bt_chunk_size; 41762306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 41862306a36Sopenharmony_ci struct hns_roce_hem_iter iter; 41962306a36Sopenharmony_ci gfp_t flag; 42062306a36Sopenharmony_ci u64 bt_ba; 42162306a36Sopenharmony_ci u32 size; 42262306a36Sopenharmony_ci int ret; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* alloc L1 BA's chunk */ 42562306a36Sopenharmony_ci if ((check_whether_bt_num_3(table->type, mhop->hop_num) || 42662306a36Sopenharmony_ci check_whether_bt_num_2(table->type, mhop->hop_num)) && 42762306a36Sopenharmony_ci !table->bt_l0[index->l0]) { 42862306a36Sopenharmony_ci table->bt_l0[index->l0] = dma_alloc_coherent(dev, bt_size, 42962306a36Sopenharmony_ci &table->bt_l0_dma_addr[index->l0], 43062306a36Sopenharmony_ci GFP_KERNEL); 43162306a36Sopenharmony_ci if (!table->bt_l0[index->l0]) { 43262306a36Sopenharmony_ci ret = -ENOMEM; 43362306a36Sopenharmony_ci goto out; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci index->inited |= HEM_INDEX_L0; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* alloc L2 BA's chunk */ 43962306a36Sopenharmony_ci if (check_whether_bt_num_3(table->type, mhop->hop_num) && 44062306a36Sopenharmony_ci !table->bt_l1[index->l1]) { 44162306a36Sopenharmony_ci table->bt_l1[index->l1] = dma_alloc_coherent(dev, bt_size, 44262306a36Sopenharmony_ci &table->bt_l1_dma_addr[index->l1], 44362306a36Sopenharmony_ci GFP_KERNEL); 44462306a36Sopenharmony_ci if (!table->bt_l1[index->l1]) { 44562306a36Sopenharmony_ci ret = -ENOMEM; 44662306a36Sopenharmony_ci goto err_alloc_hem; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci index->inited |= HEM_INDEX_L1; 44962306a36Sopenharmony_ci *(table->bt_l0[index->l0] + mhop->l1_idx) = 45062306a36Sopenharmony_ci table->bt_l1_dma_addr[index->l1]; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* 45462306a36Sopenharmony_ci * alloc buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC. 45562306a36Sopenharmony_ci * alloc bt space chunk for MTT/CQE. 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_ci size = table->type < HEM_TYPE_MTT ? mhop->buf_chunk_size : bt_size; 45862306a36Sopenharmony_ci flag = GFP_KERNEL | __GFP_NOWARN; 45962306a36Sopenharmony_ci table->hem[index->buf] = hns_roce_alloc_hem(hr_dev, size >> PAGE_SHIFT, 46062306a36Sopenharmony_ci size, flag); 46162306a36Sopenharmony_ci if (!table->hem[index->buf]) { 46262306a36Sopenharmony_ci ret = -ENOMEM; 46362306a36Sopenharmony_ci goto err_alloc_hem; 46462306a36Sopenharmony_ci } 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci index->inited |= HEM_INDEX_BUF; 46762306a36Sopenharmony_ci hns_roce_hem_first(table->hem[index->buf], &iter); 46862306a36Sopenharmony_ci bt_ba = hns_roce_hem_addr(&iter); 46962306a36Sopenharmony_ci if (table->type < HEM_TYPE_MTT) { 47062306a36Sopenharmony_ci if (mhop->hop_num == 2) 47162306a36Sopenharmony_ci *(table->bt_l1[index->l1] + mhop->l2_idx) = bt_ba; 47262306a36Sopenharmony_ci else if (mhop->hop_num == 1) 47362306a36Sopenharmony_ci *(table->bt_l0[index->l0] + mhop->l1_idx) = bt_ba; 47462306a36Sopenharmony_ci } else if (mhop->hop_num == 2) { 47562306a36Sopenharmony_ci *(table->bt_l0[index->l0] + mhop->l1_idx) = bt_ba; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_cierr_alloc_hem: 48062306a36Sopenharmony_ci free_mhop_hem(hr_dev, table, mhop, index); 48162306a36Sopenharmony_ciout: 48262306a36Sopenharmony_ci return ret; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic int set_mhop_hem(struct hns_roce_dev *hr_dev, 48662306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long obj, 48762306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 48862306a36Sopenharmony_ci struct hns_roce_hem_index *index) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct ib_device *ibdev = &hr_dev->ib_dev; 49162306a36Sopenharmony_ci u32 step_idx; 49262306a36Sopenharmony_ci int ret = 0; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L0) { 49562306a36Sopenharmony_ci ret = hr_dev->hw->set_hem(hr_dev, table, obj, 0); 49662306a36Sopenharmony_ci if (ret) { 49762306a36Sopenharmony_ci ibdev_err(ibdev, "set HEM step 0 failed!\n"); 49862306a36Sopenharmony_ci goto out; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L1) { 50362306a36Sopenharmony_ci ret = hr_dev->hw->set_hem(hr_dev, table, obj, 1); 50462306a36Sopenharmony_ci if (ret) { 50562306a36Sopenharmony_ci ibdev_err(ibdev, "set HEM step 1 failed!\n"); 50662306a36Sopenharmony_ci goto out; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (index->inited & HEM_INDEX_BUF) { 51162306a36Sopenharmony_ci if (mhop->hop_num == HNS_ROCE_HOP_NUM_0) 51262306a36Sopenharmony_ci step_idx = 0; 51362306a36Sopenharmony_ci else 51462306a36Sopenharmony_ci step_idx = mhop->hop_num; 51562306a36Sopenharmony_ci ret = hr_dev->hw->set_hem(hr_dev, table, obj, step_idx); 51662306a36Sopenharmony_ci if (ret) 51762306a36Sopenharmony_ci ibdev_err(ibdev, "set HEM step last failed!\n"); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ciout: 52062306a36Sopenharmony_ci return ret; 52162306a36Sopenharmony_ci} 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_cistatic int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev, 52462306a36Sopenharmony_ci struct hns_roce_hem_table *table, 52562306a36Sopenharmony_ci unsigned long obj) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct ib_device *ibdev = &hr_dev->ib_dev; 52862306a36Sopenharmony_ci struct hns_roce_hem_index index = {}; 52962306a36Sopenharmony_ci struct hns_roce_hem_mhop mhop = {}; 53062306a36Sopenharmony_ci int ret; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ret = calc_hem_config(hr_dev, table, obj, &mhop, &index); 53362306a36Sopenharmony_ci if (ret) { 53462306a36Sopenharmony_ci ibdev_err(ibdev, "calc hem config failed!\n"); 53562306a36Sopenharmony_ci return ret; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci mutex_lock(&table->mutex); 53962306a36Sopenharmony_ci if (table->hem[index.buf]) { 54062306a36Sopenharmony_ci refcount_inc(&table->hem[index.buf]->refcount); 54162306a36Sopenharmony_ci goto out; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = alloc_mhop_hem(hr_dev, table, &mhop, &index); 54562306a36Sopenharmony_ci if (ret) { 54662306a36Sopenharmony_ci ibdev_err(ibdev, "alloc mhop hem failed!\n"); 54762306a36Sopenharmony_ci goto out; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* set HEM base address to hardware */ 55162306a36Sopenharmony_ci if (table->type < HEM_TYPE_MTT) { 55262306a36Sopenharmony_ci ret = set_mhop_hem(hr_dev, table, obj, &mhop, &index); 55362306a36Sopenharmony_ci if (ret) { 55462306a36Sopenharmony_ci ibdev_err(ibdev, "set HEM address to HW failed!\n"); 55562306a36Sopenharmony_ci goto err_alloc; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci refcount_set(&table->hem[index.buf]->refcount, 1); 56062306a36Sopenharmony_ci goto out; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cierr_alloc: 56362306a36Sopenharmony_ci free_mhop_hem(hr_dev, table, &mhop, &index); 56462306a36Sopenharmony_ciout: 56562306a36Sopenharmony_ci mutex_unlock(&table->mutex); 56662306a36Sopenharmony_ci return ret; 56762306a36Sopenharmony_ci} 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ciint hns_roce_table_get(struct hns_roce_dev *hr_dev, 57062306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long obj) 57162306a36Sopenharmony_ci{ 57262306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 57362306a36Sopenharmony_ci unsigned long i; 57462306a36Sopenharmony_ci int ret = 0; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (hns_roce_check_whether_mhop(hr_dev, table->type)) 57762306a36Sopenharmony_ci return hns_roce_table_mhop_get(hr_dev, table, obj); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci i = obj / (table->table_chunk_size / table->obj_size); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci mutex_lock(&table->mutex); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (table->hem[i]) { 58462306a36Sopenharmony_ci refcount_inc(&table->hem[i]->refcount); 58562306a36Sopenharmony_ci goto out; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci table->hem[i] = hns_roce_alloc_hem(hr_dev, 58962306a36Sopenharmony_ci table->table_chunk_size >> PAGE_SHIFT, 59062306a36Sopenharmony_ci table->table_chunk_size, 59162306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 59262306a36Sopenharmony_ci if (!table->hem[i]) { 59362306a36Sopenharmony_ci ret = -ENOMEM; 59462306a36Sopenharmony_ci goto out; 59562306a36Sopenharmony_ci } 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci /* Set HEM base address(128K/page, pa) to Hardware */ 59862306a36Sopenharmony_ci ret = hr_dev->hw->set_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT); 59962306a36Sopenharmony_ci if (ret) { 60062306a36Sopenharmony_ci hns_roce_free_hem(hr_dev, table->hem[i]); 60162306a36Sopenharmony_ci table->hem[i] = NULL; 60262306a36Sopenharmony_ci dev_err(dev, "set HEM base address to HW failed, ret = %d.\n", 60362306a36Sopenharmony_ci ret); 60462306a36Sopenharmony_ci goto out; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci refcount_set(&table->hem[i]->refcount, 1); 60862306a36Sopenharmony_ciout: 60962306a36Sopenharmony_ci mutex_unlock(&table->mutex); 61062306a36Sopenharmony_ci return ret; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void clear_mhop_hem(struct hns_roce_dev *hr_dev, 61462306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long obj, 61562306a36Sopenharmony_ci struct hns_roce_hem_mhop *mhop, 61662306a36Sopenharmony_ci struct hns_roce_hem_index *index) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci struct ib_device *ibdev = &hr_dev->ib_dev; 61962306a36Sopenharmony_ci u32 hop_num = mhop->hop_num; 62062306a36Sopenharmony_ci u32 chunk_ba_num; 62162306a36Sopenharmony_ci u32 step_idx; 62262306a36Sopenharmony_ci int ret; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci index->inited = HEM_INDEX_BUF; 62562306a36Sopenharmony_ci chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN; 62662306a36Sopenharmony_ci if (check_whether_bt_num_2(table->type, hop_num)) { 62762306a36Sopenharmony_ci if (hns_roce_check_hem_null(table->hem, index->buf, 62862306a36Sopenharmony_ci chunk_ba_num, table->num_hem)) 62962306a36Sopenharmony_ci index->inited |= HEM_INDEX_L0; 63062306a36Sopenharmony_ci } else if (check_whether_bt_num_3(table->type, hop_num)) { 63162306a36Sopenharmony_ci if (hns_roce_check_hem_null(table->hem, index->buf, 63262306a36Sopenharmony_ci chunk_ba_num, table->num_hem)) { 63362306a36Sopenharmony_ci index->inited |= HEM_INDEX_L1; 63462306a36Sopenharmony_ci if (hns_roce_check_bt_null(table->bt_l1, index->l1, 63562306a36Sopenharmony_ci chunk_ba_num)) 63662306a36Sopenharmony_ci index->inited |= HEM_INDEX_L0; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci if (table->type < HEM_TYPE_MTT) { 64162306a36Sopenharmony_ci if (hop_num == HNS_ROCE_HOP_NUM_0) 64262306a36Sopenharmony_ci step_idx = 0; 64362306a36Sopenharmony_ci else 64462306a36Sopenharmony_ci step_idx = hop_num; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci ret = hr_dev->hw->clear_hem(hr_dev, table, obj, step_idx); 64762306a36Sopenharmony_ci if (ret) 64862306a36Sopenharmony_ci ibdev_warn(ibdev, "failed to clear hop%u HEM, ret = %d.\n", 64962306a36Sopenharmony_ci hop_num, ret); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L1) { 65262306a36Sopenharmony_ci ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 1); 65362306a36Sopenharmony_ci if (ret) 65462306a36Sopenharmony_ci ibdev_warn(ibdev, "failed to clear HEM step 1, ret = %d.\n", 65562306a36Sopenharmony_ci ret); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (index->inited & HEM_INDEX_L0) { 65962306a36Sopenharmony_ci ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 0); 66062306a36Sopenharmony_ci if (ret) 66162306a36Sopenharmony_ci ibdev_warn(ibdev, "failed to clear HEM step 0, ret = %d.\n", 66262306a36Sopenharmony_ci ret); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci} 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cistatic void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev, 66862306a36Sopenharmony_ci struct hns_roce_hem_table *table, 66962306a36Sopenharmony_ci unsigned long obj, 67062306a36Sopenharmony_ci int check_refcount) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci struct ib_device *ibdev = &hr_dev->ib_dev; 67362306a36Sopenharmony_ci struct hns_roce_hem_index index = {}; 67462306a36Sopenharmony_ci struct hns_roce_hem_mhop mhop = {}; 67562306a36Sopenharmony_ci int ret; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci ret = calc_hem_config(hr_dev, table, obj, &mhop, &index); 67862306a36Sopenharmony_ci if (ret) { 67962306a36Sopenharmony_ci ibdev_err(ibdev, "calc hem config failed!\n"); 68062306a36Sopenharmony_ci return; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci if (!check_refcount) 68462306a36Sopenharmony_ci mutex_lock(&table->mutex); 68562306a36Sopenharmony_ci else if (!refcount_dec_and_mutex_lock(&table->hem[index.buf]->refcount, 68662306a36Sopenharmony_ci &table->mutex)) 68762306a36Sopenharmony_ci return; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci clear_mhop_hem(hr_dev, table, obj, &mhop, &index); 69062306a36Sopenharmony_ci free_mhop_hem(hr_dev, table, &mhop, &index); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci mutex_unlock(&table->mutex); 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_civoid hns_roce_table_put(struct hns_roce_dev *hr_dev, 69662306a36Sopenharmony_ci struct hns_roce_hem_table *table, unsigned long obj) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 69962306a36Sopenharmony_ci unsigned long i; 70062306a36Sopenharmony_ci int ret; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (hns_roce_check_whether_mhop(hr_dev, table->type)) { 70362306a36Sopenharmony_ci hns_roce_table_mhop_put(hr_dev, table, obj, 1); 70462306a36Sopenharmony_ci return; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci i = obj / (table->table_chunk_size / table->obj_size); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (!refcount_dec_and_mutex_lock(&table->hem[i]->refcount, 71062306a36Sopenharmony_ci &table->mutex)) 71162306a36Sopenharmony_ci return; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci ret = hr_dev->hw->clear_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT); 71462306a36Sopenharmony_ci if (ret) 71562306a36Sopenharmony_ci dev_warn(dev, "failed to clear HEM base address, ret = %d.\n", 71662306a36Sopenharmony_ci ret); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci hns_roce_free_hem(hr_dev, table->hem[i]); 71962306a36Sopenharmony_ci table->hem[i] = NULL; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci mutex_unlock(&table->mutex); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_civoid *hns_roce_table_find(struct hns_roce_dev *hr_dev, 72562306a36Sopenharmony_ci struct hns_roce_hem_table *table, 72662306a36Sopenharmony_ci unsigned long obj, dma_addr_t *dma_handle) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct hns_roce_hem_chunk *chunk; 72962306a36Sopenharmony_ci struct hns_roce_hem_mhop mhop; 73062306a36Sopenharmony_ci struct hns_roce_hem *hem; 73162306a36Sopenharmony_ci unsigned long mhop_obj = obj; 73262306a36Sopenharmony_ci unsigned long obj_per_chunk; 73362306a36Sopenharmony_ci unsigned long idx_offset; 73462306a36Sopenharmony_ci int offset, dma_offset; 73562306a36Sopenharmony_ci void *addr = NULL; 73662306a36Sopenharmony_ci u32 hem_idx = 0; 73762306a36Sopenharmony_ci int length; 73862306a36Sopenharmony_ci int i, j; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci mutex_lock(&table->mutex); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci if (!hns_roce_check_whether_mhop(hr_dev, table->type)) { 74362306a36Sopenharmony_ci obj_per_chunk = table->table_chunk_size / table->obj_size; 74462306a36Sopenharmony_ci hem = table->hem[obj / obj_per_chunk]; 74562306a36Sopenharmony_ci idx_offset = obj % obj_per_chunk; 74662306a36Sopenharmony_ci dma_offset = offset = idx_offset * table->obj_size; 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci u32 seg_size = 64; /* 8 bytes per BA and 8 BA per segment */ 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci if (hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, &mhop)) 75162306a36Sopenharmony_ci goto out; 75262306a36Sopenharmony_ci /* mtt mhop */ 75362306a36Sopenharmony_ci i = mhop.l0_idx; 75462306a36Sopenharmony_ci j = mhop.l1_idx; 75562306a36Sopenharmony_ci if (mhop.hop_num == 2) 75662306a36Sopenharmony_ci hem_idx = i * (mhop.bt_chunk_size / BA_BYTE_LEN) + j; 75762306a36Sopenharmony_ci else if (mhop.hop_num == 1 || 75862306a36Sopenharmony_ci mhop.hop_num == HNS_ROCE_HOP_NUM_0) 75962306a36Sopenharmony_ci hem_idx = i; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci hem = table->hem[hem_idx]; 76262306a36Sopenharmony_ci dma_offset = offset = obj * seg_size % mhop.bt_chunk_size; 76362306a36Sopenharmony_ci if (mhop.hop_num == 2) 76462306a36Sopenharmony_ci dma_offset = offset = 0; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci if (!hem) 76862306a36Sopenharmony_ci goto out; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci list_for_each_entry(chunk, &hem->chunk_list, list) { 77162306a36Sopenharmony_ci for (i = 0; i < chunk->npages; ++i) { 77262306a36Sopenharmony_ci length = sg_dma_len(&chunk->mem[i]); 77362306a36Sopenharmony_ci if (dma_handle && dma_offset >= 0) { 77462306a36Sopenharmony_ci if (length > (u32)dma_offset) 77562306a36Sopenharmony_ci *dma_handle = sg_dma_address( 77662306a36Sopenharmony_ci &chunk->mem[i]) + dma_offset; 77762306a36Sopenharmony_ci dma_offset -= length; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (length > (u32)offset) { 78162306a36Sopenharmony_ci addr = chunk->buf[i] + offset; 78262306a36Sopenharmony_ci goto out; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci offset -= length; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ciout: 78962306a36Sopenharmony_ci mutex_unlock(&table->mutex); 79062306a36Sopenharmony_ci return addr; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ciint hns_roce_init_hem_table(struct hns_roce_dev *hr_dev, 79462306a36Sopenharmony_ci struct hns_roce_hem_table *table, u32 type, 79562306a36Sopenharmony_ci unsigned long obj_size, unsigned long nobj) 79662306a36Sopenharmony_ci{ 79762306a36Sopenharmony_ci unsigned long obj_per_chunk; 79862306a36Sopenharmony_ci unsigned long num_hem; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (!hns_roce_check_whether_mhop(hr_dev, type)) { 80162306a36Sopenharmony_ci table->table_chunk_size = hr_dev->caps.chunk_sz; 80262306a36Sopenharmony_ci obj_per_chunk = table->table_chunk_size / obj_size; 80362306a36Sopenharmony_ci num_hem = DIV_ROUND_UP(nobj, obj_per_chunk); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci table->hem = kcalloc(num_hem, sizeof(*table->hem), GFP_KERNEL); 80662306a36Sopenharmony_ci if (!table->hem) 80762306a36Sopenharmony_ci return -ENOMEM; 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci struct hns_roce_hem_mhop mhop = {}; 81062306a36Sopenharmony_ci unsigned long buf_chunk_size; 81162306a36Sopenharmony_ci unsigned long bt_chunk_size; 81262306a36Sopenharmony_ci unsigned long bt_chunk_num; 81362306a36Sopenharmony_ci unsigned long num_bt_l0; 81462306a36Sopenharmony_ci u32 hop_num; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (get_hem_table_config(hr_dev, &mhop, type)) 81762306a36Sopenharmony_ci return -EINVAL; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci buf_chunk_size = mhop.buf_chunk_size; 82062306a36Sopenharmony_ci bt_chunk_size = mhop.bt_chunk_size; 82162306a36Sopenharmony_ci num_bt_l0 = mhop.ba_l0_num; 82262306a36Sopenharmony_ci hop_num = mhop.hop_num; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci obj_per_chunk = buf_chunk_size / obj_size; 82562306a36Sopenharmony_ci num_hem = DIV_ROUND_UP(nobj, obj_per_chunk); 82662306a36Sopenharmony_ci bt_chunk_num = bt_chunk_size / BA_BYTE_LEN; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (type >= HEM_TYPE_MTT) 82962306a36Sopenharmony_ci num_bt_l0 = bt_chunk_num; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci table->hem = kcalloc(num_hem, sizeof(*table->hem), 83262306a36Sopenharmony_ci GFP_KERNEL); 83362306a36Sopenharmony_ci if (!table->hem) 83462306a36Sopenharmony_ci goto err_kcalloc_hem_buf; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (check_whether_bt_num_3(type, hop_num)) { 83762306a36Sopenharmony_ci unsigned long num_bt_l1; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci num_bt_l1 = DIV_ROUND_UP(num_hem, bt_chunk_num); 84062306a36Sopenharmony_ci table->bt_l1 = kcalloc(num_bt_l1, 84162306a36Sopenharmony_ci sizeof(*table->bt_l1), 84262306a36Sopenharmony_ci GFP_KERNEL); 84362306a36Sopenharmony_ci if (!table->bt_l1) 84462306a36Sopenharmony_ci goto err_kcalloc_bt_l1; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci table->bt_l1_dma_addr = kcalloc(num_bt_l1, 84762306a36Sopenharmony_ci sizeof(*table->bt_l1_dma_addr), 84862306a36Sopenharmony_ci GFP_KERNEL); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (!table->bt_l1_dma_addr) 85162306a36Sopenharmony_ci goto err_kcalloc_l1_dma; 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (check_whether_bt_num_2(type, hop_num) || 85562306a36Sopenharmony_ci check_whether_bt_num_3(type, hop_num)) { 85662306a36Sopenharmony_ci table->bt_l0 = kcalloc(num_bt_l0, sizeof(*table->bt_l0), 85762306a36Sopenharmony_ci GFP_KERNEL); 85862306a36Sopenharmony_ci if (!table->bt_l0) 85962306a36Sopenharmony_ci goto err_kcalloc_bt_l0; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci table->bt_l0_dma_addr = kcalloc(num_bt_l0, 86262306a36Sopenharmony_ci sizeof(*table->bt_l0_dma_addr), 86362306a36Sopenharmony_ci GFP_KERNEL); 86462306a36Sopenharmony_ci if (!table->bt_l0_dma_addr) 86562306a36Sopenharmony_ci goto err_kcalloc_l0_dma; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci table->type = type; 87062306a36Sopenharmony_ci table->num_hem = num_hem; 87162306a36Sopenharmony_ci table->obj_size = obj_size; 87262306a36Sopenharmony_ci mutex_init(&table->mutex); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci return 0; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cierr_kcalloc_l0_dma: 87762306a36Sopenharmony_ci kfree(table->bt_l0); 87862306a36Sopenharmony_ci table->bt_l0 = NULL; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cierr_kcalloc_bt_l0: 88162306a36Sopenharmony_ci kfree(table->bt_l1_dma_addr); 88262306a36Sopenharmony_ci table->bt_l1_dma_addr = NULL; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cierr_kcalloc_l1_dma: 88562306a36Sopenharmony_ci kfree(table->bt_l1); 88662306a36Sopenharmony_ci table->bt_l1 = NULL; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_cierr_kcalloc_bt_l1: 88962306a36Sopenharmony_ci kfree(table->hem); 89062306a36Sopenharmony_ci table->hem = NULL; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cierr_kcalloc_hem_buf: 89362306a36Sopenharmony_ci return -ENOMEM; 89462306a36Sopenharmony_ci} 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_cistatic void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev, 89762306a36Sopenharmony_ci struct hns_roce_hem_table *table) 89862306a36Sopenharmony_ci{ 89962306a36Sopenharmony_ci struct hns_roce_hem_mhop mhop; 90062306a36Sopenharmony_ci u32 buf_chunk_size; 90162306a36Sopenharmony_ci u64 obj; 90262306a36Sopenharmony_ci int i; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (hns_roce_calc_hem_mhop(hr_dev, table, NULL, &mhop)) 90562306a36Sopenharmony_ci return; 90662306a36Sopenharmony_ci buf_chunk_size = table->type < HEM_TYPE_MTT ? mhop.buf_chunk_size : 90762306a36Sopenharmony_ci mhop.bt_chunk_size; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci for (i = 0; i < table->num_hem; ++i) { 91062306a36Sopenharmony_ci obj = i * buf_chunk_size / table->obj_size; 91162306a36Sopenharmony_ci if (table->hem[i]) 91262306a36Sopenharmony_ci hns_roce_table_mhop_put(hr_dev, table, obj, 0); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci kfree(table->hem); 91662306a36Sopenharmony_ci table->hem = NULL; 91762306a36Sopenharmony_ci kfree(table->bt_l1); 91862306a36Sopenharmony_ci table->bt_l1 = NULL; 91962306a36Sopenharmony_ci kfree(table->bt_l1_dma_addr); 92062306a36Sopenharmony_ci table->bt_l1_dma_addr = NULL; 92162306a36Sopenharmony_ci kfree(table->bt_l0); 92262306a36Sopenharmony_ci table->bt_l0 = NULL; 92362306a36Sopenharmony_ci kfree(table->bt_l0_dma_addr); 92462306a36Sopenharmony_ci table->bt_l0_dma_addr = NULL; 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_civoid hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev, 92862306a36Sopenharmony_ci struct hns_roce_hem_table *table) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct device *dev = hr_dev->dev; 93162306a36Sopenharmony_ci unsigned long i; 93262306a36Sopenharmony_ci int obj; 93362306a36Sopenharmony_ci int ret; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (hns_roce_check_whether_mhop(hr_dev, table->type)) { 93662306a36Sopenharmony_ci hns_roce_cleanup_mhop_hem_table(hr_dev, table); 93762306a36Sopenharmony_ci return; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci for (i = 0; i < table->num_hem; ++i) 94162306a36Sopenharmony_ci if (table->hem[i]) { 94262306a36Sopenharmony_ci obj = i * table->table_chunk_size / table->obj_size; 94362306a36Sopenharmony_ci ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 0); 94462306a36Sopenharmony_ci if (ret) 94562306a36Sopenharmony_ci dev_err(dev, "clear HEM base address failed, ret = %d.\n", 94662306a36Sopenharmony_ci ret); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci hns_roce_free_hem(hr_dev, table->hem[i]); 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci kfree(table->hem); 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_civoid hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev) 95562306a36Sopenharmony_ci{ 95662306a36Sopenharmony_ci if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ) 95762306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, 95862306a36Sopenharmony_ci &hr_dev->srq_table.table); 95962306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table); 96062306a36Sopenharmony_ci if (hr_dev->caps.qpc_timer_entry_sz) 96162306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, 96262306a36Sopenharmony_ci &hr_dev->qpc_timer_table); 96362306a36Sopenharmony_ci if (hr_dev->caps.cqc_timer_entry_sz) 96462306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, 96562306a36Sopenharmony_ci &hr_dev->cqc_timer_table); 96662306a36Sopenharmony_ci if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL) 96762306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, 96862306a36Sopenharmony_ci &hr_dev->qp_table.sccc_table); 96962306a36Sopenharmony_ci if (hr_dev->caps.trrl_entry_sz) 97062306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, 97162306a36Sopenharmony_ci &hr_dev->qp_table.trrl_table); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci if (hr_dev->caps.gmv_entry_sz) 97462306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, &hr_dev->gmv_table); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.irrl_table); 97762306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.qp_table); 97862306a36Sopenharmony_ci hns_roce_cleanup_hem_table(hr_dev, &hr_dev->mr_table.mtpt_table); 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistruct hns_roce_hem_item { 98262306a36Sopenharmony_ci struct list_head list; /* link all hems in the same bt level */ 98362306a36Sopenharmony_ci struct list_head sibling; /* link all hems in last hop for mtt */ 98462306a36Sopenharmony_ci void *addr; 98562306a36Sopenharmony_ci dma_addr_t dma_addr; 98662306a36Sopenharmony_ci size_t count; /* max ba numbers */ 98762306a36Sopenharmony_ci int start; /* start buf offset in this hem */ 98862306a36Sopenharmony_ci int end; /* end buf offset in this hem */ 98962306a36Sopenharmony_ci}; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci/* All HEM items are linked in a tree structure */ 99262306a36Sopenharmony_cistruct hns_roce_hem_head { 99362306a36Sopenharmony_ci struct list_head branch[HNS_ROCE_MAX_BT_REGION]; 99462306a36Sopenharmony_ci struct list_head root; 99562306a36Sopenharmony_ci struct list_head leaf; 99662306a36Sopenharmony_ci}; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic struct hns_roce_hem_item * 99962306a36Sopenharmony_cihem_list_alloc_item(struct hns_roce_dev *hr_dev, int start, int end, int count, 100062306a36Sopenharmony_ci bool exist_bt) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci struct hns_roce_hem_item *hem; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci hem = kzalloc(sizeof(*hem), GFP_KERNEL); 100562306a36Sopenharmony_ci if (!hem) 100662306a36Sopenharmony_ci return NULL; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (exist_bt) { 100962306a36Sopenharmony_ci hem->addr = dma_alloc_coherent(hr_dev->dev, count * BA_BYTE_LEN, 101062306a36Sopenharmony_ci &hem->dma_addr, GFP_KERNEL); 101162306a36Sopenharmony_ci if (!hem->addr) { 101262306a36Sopenharmony_ci kfree(hem); 101362306a36Sopenharmony_ci return NULL; 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci } 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_ci hem->count = count; 101862306a36Sopenharmony_ci hem->start = start; 101962306a36Sopenharmony_ci hem->end = end; 102062306a36Sopenharmony_ci INIT_LIST_HEAD(&hem->list); 102162306a36Sopenharmony_ci INIT_LIST_HEAD(&hem->sibling); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci return hem; 102462306a36Sopenharmony_ci} 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_cistatic void hem_list_free_item(struct hns_roce_dev *hr_dev, 102762306a36Sopenharmony_ci struct hns_roce_hem_item *hem, bool exist_bt) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci if (exist_bt) 103062306a36Sopenharmony_ci dma_free_coherent(hr_dev->dev, hem->count * BA_BYTE_LEN, 103162306a36Sopenharmony_ci hem->addr, hem->dma_addr); 103262306a36Sopenharmony_ci kfree(hem); 103362306a36Sopenharmony_ci} 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_cistatic void hem_list_free_all(struct hns_roce_dev *hr_dev, 103662306a36Sopenharmony_ci struct list_head *head, bool exist_bt) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci struct hns_roce_hem_item *hem, *temp_hem; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci list_for_each_entry_safe(hem, temp_hem, head, list) { 104162306a36Sopenharmony_ci list_del(&hem->list); 104262306a36Sopenharmony_ci hem_list_free_item(hr_dev, hem, exist_bt); 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic void hem_list_link_bt(struct hns_roce_dev *hr_dev, void *base_addr, 104762306a36Sopenharmony_ci u64 table_addr) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci *(u64 *)(base_addr) = table_addr; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci/* assign L0 table address to hem from root bt */ 105362306a36Sopenharmony_cistatic void hem_list_assign_bt(struct hns_roce_dev *hr_dev, 105462306a36Sopenharmony_ci struct hns_roce_hem_item *hem, void *cpu_addr, 105562306a36Sopenharmony_ci u64 phy_addr) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci hem->addr = cpu_addr; 105862306a36Sopenharmony_ci hem->dma_addr = (dma_addr_t)phy_addr; 105962306a36Sopenharmony_ci} 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_cistatic inline bool hem_list_page_is_in_range(struct hns_roce_hem_item *hem, 106262306a36Sopenharmony_ci int offset) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci return (hem->start <= offset && offset <= hem->end); 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic struct hns_roce_hem_item *hem_list_search_item(struct list_head *ba_list, 106862306a36Sopenharmony_ci int page_offset) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct hns_roce_hem_item *hem, *temp_hem; 107162306a36Sopenharmony_ci struct hns_roce_hem_item *found = NULL; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci list_for_each_entry_safe(hem, temp_hem, ba_list, list) { 107462306a36Sopenharmony_ci if (hem_list_page_is_in_range(hem, page_offset)) { 107562306a36Sopenharmony_ci found = hem; 107662306a36Sopenharmony_ci break; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return found; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic bool hem_list_is_bottom_bt(int hopnum, int bt_level) 108462306a36Sopenharmony_ci{ 108562306a36Sopenharmony_ci /* 108662306a36Sopenharmony_ci * hopnum base address table levels 108762306a36Sopenharmony_ci * 0 L0(buf) 108862306a36Sopenharmony_ci * 1 L0 -> buf 108962306a36Sopenharmony_ci * 2 L0 -> L1 -> buf 109062306a36Sopenharmony_ci * 3 L0 -> L1 -> L2 -> buf 109162306a36Sopenharmony_ci */ 109262306a36Sopenharmony_ci return bt_level >= (hopnum ? hopnum - 1 : hopnum); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci/* 109662306a36Sopenharmony_ci * calc base address entries num 109762306a36Sopenharmony_ci * @hopnum: num of mutihop addressing 109862306a36Sopenharmony_ci * @bt_level: base address table level 109962306a36Sopenharmony_ci * @unit: ba entries per bt page 110062306a36Sopenharmony_ci */ 110162306a36Sopenharmony_cistatic u32 hem_list_calc_ba_range(int hopnum, int bt_level, int unit) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci u32 step; 110462306a36Sopenharmony_ci int max; 110562306a36Sopenharmony_ci int i; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci if (hopnum <= bt_level) 110862306a36Sopenharmony_ci return 0; 110962306a36Sopenharmony_ci /* 111062306a36Sopenharmony_ci * hopnum bt_level range 111162306a36Sopenharmony_ci * 1 0 unit 111262306a36Sopenharmony_ci * ------------ 111362306a36Sopenharmony_ci * 2 0 unit * unit 111462306a36Sopenharmony_ci * 2 1 unit 111562306a36Sopenharmony_ci * ------------ 111662306a36Sopenharmony_ci * 3 0 unit * unit * unit 111762306a36Sopenharmony_ci * 3 1 unit * unit 111862306a36Sopenharmony_ci * 3 2 unit 111962306a36Sopenharmony_ci */ 112062306a36Sopenharmony_ci step = 1; 112162306a36Sopenharmony_ci max = hopnum - bt_level; 112262306a36Sopenharmony_ci for (i = 0; i < max; i++) 112362306a36Sopenharmony_ci step = step * unit; 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci return step; 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci/* 112962306a36Sopenharmony_ci * calc the root ba entries which could cover all regions 113062306a36Sopenharmony_ci * @regions: buf region array 113162306a36Sopenharmony_ci * @region_cnt: array size of @regions 113262306a36Sopenharmony_ci * @unit: ba entries per bt page 113362306a36Sopenharmony_ci */ 113462306a36Sopenharmony_ciint hns_roce_hem_list_calc_root_ba(const struct hns_roce_buf_region *regions, 113562306a36Sopenharmony_ci int region_cnt, int unit) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct hns_roce_buf_region *r; 113862306a36Sopenharmony_ci int total = 0; 113962306a36Sopenharmony_ci int step; 114062306a36Sopenharmony_ci int i; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci for (i = 0; i < region_cnt; i++) { 114362306a36Sopenharmony_ci r = (struct hns_roce_buf_region *)®ions[i]; 114462306a36Sopenharmony_ci if (r->hopnum > 1) { 114562306a36Sopenharmony_ci step = hem_list_calc_ba_range(r->hopnum, 1, unit); 114662306a36Sopenharmony_ci if (step > 0) 114762306a36Sopenharmony_ci total += (r->count + step - 1) / step; 114862306a36Sopenharmony_ci } else { 114962306a36Sopenharmony_ci total += r->count; 115062306a36Sopenharmony_ci } 115162306a36Sopenharmony_ci } 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci return total; 115462306a36Sopenharmony_ci} 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_cistatic int hem_list_alloc_mid_bt(struct hns_roce_dev *hr_dev, 115762306a36Sopenharmony_ci const struct hns_roce_buf_region *r, int unit, 115862306a36Sopenharmony_ci int offset, struct list_head *mid_bt, 115962306a36Sopenharmony_ci struct list_head *btm_bt) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct hns_roce_hem_item *hem_ptrs[HNS_ROCE_MAX_BT_LEVEL] = { NULL }; 116262306a36Sopenharmony_ci struct list_head temp_list[HNS_ROCE_MAX_BT_LEVEL]; 116362306a36Sopenharmony_ci struct hns_roce_hem_item *cur, *pre; 116462306a36Sopenharmony_ci const int hopnum = r->hopnum; 116562306a36Sopenharmony_ci int start_aligned; 116662306a36Sopenharmony_ci int distance; 116762306a36Sopenharmony_ci int ret = 0; 116862306a36Sopenharmony_ci int max_ofs; 116962306a36Sopenharmony_ci int level; 117062306a36Sopenharmony_ci u32 step; 117162306a36Sopenharmony_ci int end; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci if (hopnum <= 1) 117462306a36Sopenharmony_ci return 0; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci if (hopnum > HNS_ROCE_MAX_BT_LEVEL) { 117762306a36Sopenharmony_ci dev_err(hr_dev->dev, "invalid hopnum %d!\n", hopnum); 117862306a36Sopenharmony_ci return -EINVAL; 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci if (offset < r->offset) { 118262306a36Sopenharmony_ci dev_err(hr_dev->dev, "invalid offset %d, min %u!\n", 118362306a36Sopenharmony_ci offset, r->offset); 118462306a36Sopenharmony_ci return -EINVAL; 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci distance = offset - r->offset; 118862306a36Sopenharmony_ci max_ofs = r->offset + r->count - 1; 118962306a36Sopenharmony_ci for (level = 0; level < hopnum; level++) 119062306a36Sopenharmony_ci INIT_LIST_HEAD(&temp_list[level]); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* config L1 bt to last bt and link them to corresponding parent */ 119362306a36Sopenharmony_ci for (level = 1; level < hopnum; level++) { 119462306a36Sopenharmony_ci cur = hem_list_search_item(&mid_bt[level], offset); 119562306a36Sopenharmony_ci if (cur) { 119662306a36Sopenharmony_ci hem_ptrs[level] = cur; 119762306a36Sopenharmony_ci continue; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci step = hem_list_calc_ba_range(hopnum, level, unit); 120162306a36Sopenharmony_ci if (step < 1) { 120262306a36Sopenharmony_ci ret = -EINVAL; 120362306a36Sopenharmony_ci goto err_exit; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci start_aligned = (distance / step) * step + r->offset; 120762306a36Sopenharmony_ci end = min_t(int, start_aligned + step - 1, max_ofs); 120862306a36Sopenharmony_ci cur = hem_list_alloc_item(hr_dev, start_aligned, end, unit, 120962306a36Sopenharmony_ci true); 121062306a36Sopenharmony_ci if (!cur) { 121162306a36Sopenharmony_ci ret = -ENOMEM; 121262306a36Sopenharmony_ci goto err_exit; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci hem_ptrs[level] = cur; 121562306a36Sopenharmony_ci list_add(&cur->list, &temp_list[level]); 121662306a36Sopenharmony_ci if (hem_list_is_bottom_bt(hopnum, level)) 121762306a36Sopenharmony_ci list_add(&cur->sibling, &temp_list[0]); 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* link bt to parent bt */ 122062306a36Sopenharmony_ci if (level > 1) { 122162306a36Sopenharmony_ci pre = hem_ptrs[level - 1]; 122262306a36Sopenharmony_ci step = (cur->start - pre->start) / step * BA_BYTE_LEN; 122362306a36Sopenharmony_ci hem_list_link_bt(hr_dev, pre->addr + step, 122462306a36Sopenharmony_ci cur->dma_addr); 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci list_splice(&temp_list[0], btm_bt); 122962306a36Sopenharmony_ci for (level = 1; level < hopnum; level++) 123062306a36Sopenharmony_ci list_splice(&temp_list[level], &mid_bt[level]); 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_cierr_exit: 123562306a36Sopenharmony_ci for (level = 1; level < hopnum; level++) 123662306a36Sopenharmony_ci hem_list_free_all(hr_dev, &temp_list[level], true); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci return ret; 123962306a36Sopenharmony_ci} 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_cistatic struct hns_roce_hem_item * 124262306a36Sopenharmony_cialloc_root_hem(struct hns_roce_dev *hr_dev, int unit, int *max_ba_num, 124362306a36Sopenharmony_ci const struct hns_roce_buf_region *regions, int region_cnt) 124462306a36Sopenharmony_ci{ 124562306a36Sopenharmony_ci const struct hns_roce_buf_region *r; 124662306a36Sopenharmony_ci struct hns_roce_hem_item *hem; 124762306a36Sopenharmony_ci int ba_num; 124862306a36Sopenharmony_ci int offset; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci ba_num = hns_roce_hem_list_calc_root_ba(regions, region_cnt, unit); 125162306a36Sopenharmony_ci if (ba_num < 1) 125262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if (ba_num > unit) 125562306a36Sopenharmony_ci return ERR_PTR(-ENOBUFS); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci offset = regions[0].offset; 125862306a36Sopenharmony_ci /* indicate to last region */ 125962306a36Sopenharmony_ci r = ®ions[region_cnt - 1]; 126062306a36Sopenharmony_ci hem = hem_list_alloc_item(hr_dev, offset, r->offset + r->count - 1, 126162306a36Sopenharmony_ci ba_num, true); 126262306a36Sopenharmony_ci if (!hem) 126362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci *max_ba_num = ba_num; 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci return hem; 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_cistatic int alloc_fake_root_bt(struct hns_roce_dev *hr_dev, void *cpu_base, 127162306a36Sopenharmony_ci u64 phy_base, const struct hns_roce_buf_region *r, 127262306a36Sopenharmony_ci struct list_head *branch_head, 127362306a36Sopenharmony_ci struct list_head *leaf_head) 127462306a36Sopenharmony_ci{ 127562306a36Sopenharmony_ci struct hns_roce_hem_item *hem; 127662306a36Sopenharmony_ci 127762306a36Sopenharmony_ci hem = hem_list_alloc_item(hr_dev, r->offset, r->offset + r->count - 1, 127862306a36Sopenharmony_ci r->count, false); 127962306a36Sopenharmony_ci if (!hem) 128062306a36Sopenharmony_ci return -ENOMEM; 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci hem_list_assign_bt(hr_dev, hem, cpu_base, phy_base); 128362306a36Sopenharmony_ci list_add(&hem->list, branch_head); 128462306a36Sopenharmony_ci list_add(&hem->sibling, leaf_head); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci return r->count; 128762306a36Sopenharmony_ci} 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_cistatic int setup_middle_bt(struct hns_roce_dev *hr_dev, void *cpu_base, 129062306a36Sopenharmony_ci int unit, const struct hns_roce_buf_region *r, 129162306a36Sopenharmony_ci const struct list_head *branch_head) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct hns_roce_hem_item *hem, *temp_hem; 129462306a36Sopenharmony_ci int total = 0; 129562306a36Sopenharmony_ci int offset; 129662306a36Sopenharmony_ci int step; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci step = hem_list_calc_ba_range(r->hopnum, 1, unit); 129962306a36Sopenharmony_ci if (step < 1) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* if exist mid bt, link L1 to L0 */ 130362306a36Sopenharmony_ci list_for_each_entry_safe(hem, temp_hem, branch_head, list) { 130462306a36Sopenharmony_ci offset = (hem->start - r->offset) / step * BA_BYTE_LEN; 130562306a36Sopenharmony_ci hem_list_link_bt(hr_dev, cpu_base + offset, hem->dma_addr); 130662306a36Sopenharmony_ci total++; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci return total; 131062306a36Sopenharmony_ci} 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_cistatic int 131362306a36Sopenharmony_cisetup_root_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem_list *hem_list, 131462306a36Sopenharmony_ci int unit, int max_ba_num, struct hns_roce_hem_head *head, 131562306a36Sopenharmony_ci const struct hns_roce_buf_region *regions, int region_cnt) 131662306a36Sopenharmony_ci{ 131762306a36Sopenharmony_ci const struct hns_roce_buf_region *r; 131862306a36Sopenharmony_ci struct hns_roce_hem_item *root_hem; 131962306a36Sopenharmony_ci void *cpu_base; 132062306a36Sopenharmony_ci u64 phy_base; 132162306a36Sopenharmony_ci int i, total; 132262306a36Sopenharmony_ci int ret; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci root_hem = list_first_entry(&head->root, 132562306a36Sopenharmony_ci struct hns_roce_hem_item, list); 132662306a36Sopenharmony_ci if (!root_hem) 132762306a36Sopenharmony_ci return -ENOMEM; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci total = 0; 133062306a36Sopenharmony_ci for (i = 0; i < region_cnt && total < max_ba_num; i++) { 133162306a36Sopenharmony_ci r = ®ions[i]; 133262306a36Sopenharmony_ci if (!r->count) 133362306a36Sopenharmony_ci continue; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci /* all regions's mid[x][0] shared the root_bt's trunk */ 133662306a36Sopenharmony_ci cpu_base = root_hem->addr + total * BA_BYTE_LEN; 133762306a36Sopenharmony_ci phy_base = root_hem->dma_addr + total * BA_BYTE_LEN; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* if hopnum is 0 or 1, cut a new fake hem from the root bt 134062306a36Sopenharmony_ci * which's address share to all regions. 134162306a36Sopenharmony_ci */ 134262306a36Sopenharmony_ci if (hem_list_is_bottom_bt(r->hopnum, 0)) 134362306a36Sopenharmony_ci ret = alloc_fake_root_bt(hr_dev, cpu_base, phy_base, r, 134462306a36Sopenharmony_ci &head->branch[i], &head->leaf); 134562306a36Sopenharmony_ci else 134662306a36Sopenharmony_ci ret = setup_middle_bt(hr_dev, cpu_base, unit, r, 134762306a36Sopenharmony_ci &hem_list->mid_bt[i][1]); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci if (ret < 0) 135062306a36Sopenharmony_ci return ret; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci total += ret; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci list_splice(&head->leaf, &hem_list->btm_bt); 135662306a36Sopenharmony_ci list_splice(&head->root, &hem_list->root_bt); 135762306a36Sopenharmony_ci for (i = 0; i < region_cnt; i++) 135862306a36Sopenharmony_ci list_splice(&head->branch[i], &hem_list->mid_bt[i][0]); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_cistatic int hem_list_alloc_root_bt(struct hns_roce_dev *hr_dev, 136462306a36Sopenharmony_ci struct hns_roce_hem_list *hem_list, int unit, 136562306a36Sopenharmony_ci const struct hns_roce_buf_region *regions, 136662306a36Sopenharmony_ci int region_cnt) 136762306a36Sopenharmony_ci{ 136862306a36Sopenharmony_ci struct hns_roce_hem_item *root_hem; 136962306a36Sopenharmony_ci struct hns_roce_hem_head head; 137062306a36Sopenharmony_ci int max_ba_num; 137162306a36Sopenharmony_ci int ret; 137262306a36Sopenharmony_ci int i; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci root_hem = hem_list_search_item(&hem_list->root_bt, regions[0].offset); 137562306a36Sopenharmony_ci if (root_hem) 137662306a36Sopenharmony_ci return 0; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci max_ba_num = 0; 137962306a36Sopenharmony_ci root_hem = alloc_root_hem(hr_dev, unit, &max_ba_num, regions, 138062306a36Sopenharmony_ci region_cnt); 138162306a36Sopenharmony_ci if (IS_ERR(root_hem)) 138262306a36Sopenharmony_ci return PTR_ERR(root_hem); 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci /* List head for storing all allocated HEM items */ 138562306a36Sopenharmony_ci INIT_LIST_HEAD(&head.root); 138662306a36Sopenharmony_ci INIT_LIST_HEAD(&head.leaf); 138762306a36Sopenharmony_ci for (i = 0; i < region_cnt; i++) 138862306a36Sopenharmony_ci INIT_LIST_HEAD(&head.branch[i]); 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci hem_list->root_ba = root_hem->dma_addr; 139162306a36Sopenharmony_ci list_add(&root_hem->list, &head.root); 139262306a36Sopenharmony_ci ret = setup_root_hem(hr_dev, hem_list, unit, max_ba_num, &head, regions, 139362306a36Sopenharmony_ci region_cnt); 139462306a36Sopenharmony_ci if (ret) { 139562306a36Sopenharmony_ci for (i = 0; i < region_cnt; i++) 139662306a36Sopenharmony_ci hem_list_free_all(hr_dev, &head.branch[i], false); 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci hem_list_free_all(hr_dev, &head.root, true); 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci return ret; 140262306a36Sopenharmony_ci} 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci/* construct the base address table and link them by address hop config */ 140562306a36Sopenharmony_ciint hns_roce_hem_list_request(struct hns_roce_dev *hr_dev, 140662306a36Sopenharmony_ci struct hns_roce_hem_list *hem_list, 140762306a36Sopenharmony_ci const struct hns_roce_buf_region *regions, 140862306a36Sopenharmony_ci int region_cnt, unsigned int bt_pg_shift) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci const struct hns_roce_buf_region *r; 141162306a36Sopenharmony_ci int ofs, end; 141262306a36Sopenharmony_ci int unit; 141362306a36Sopenharmony_ci int ret; 141462306a36Sopenharmony_ci int i; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci if (region_cnt > HNS_ROCE_MAX_BT_REGION) { 141762306a36Sopenharmony_ci dev_err(hr_dev->dev, "invalid region region_cnt %d!\n", 141862306a36Sopenharmony_ci region_cnt); 141962306a36Sopenharmony_ci return -EINVAL; 142062306a36Sopenharmony_ci } 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci unit = (1 << bt_pg_shift) / BA_BYTE_LEN; 142362306a36Sopenharmony_ci for (i = 0; i < region_cnt; i++) { 142462306a36Sopenharmony_ci r = ®ions[i]; 142562306a36Sopenharmony_ci if (!r->count) 142662306a36Sopenharmony_ci continue; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci end = r->offset + r->count; 142962306a36Sopenharmony_ci for (ofs = r->offset; ofs < end; ofs += unit) { 143062306a36Sopenharmony_ci ret = hem_list_alloc_mid_bt(hr_dev, r, unit, ofs, 143162306a36Sopenharmony_ci hem_list->mid_bt[i], 143262306a36Sopenharmony_ci &hem_list->btm_bt); 143362306a36Sopenharmony_ci if (ret) { 143462306a36Sopenharmony_ci dev_err(hr_dev->dev, 143562306a36Sopenharmony_ci "alloc hem trunk fail ret = %d!\n", ret); 143662306a36Sopenharmony_ci goto err_alloc; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci } 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci ret = hem_list_alloc_root_bt(hr_dev, hem_list, unit, regions, 144262306a36Sopenharmony_ci region_cnt); 144362306a36Sopenharmony_ci if (ret) 144462306a36Sopenharmony_ci dev_err(hr_dev->dev, "alloc hem root fail ret = %d!\n", ret); 144562306a36Sopenharmony_ci else 144662306a36Sopenharmony_ci return 0; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cierr_alloc: 144962306a36Sopenharmony_ci hns_roce_hem_list_release(hr_dev, hem_list); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci return ret; 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_civoid hns_roce_hem_list_release(struct hns_roce_dev *hr_dev, 145562306a36Sopenharmony_ci struct hns_roce_hem_list *hem_list) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci int i, j; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++) 146062306a36Sopenharmony_ci for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++) 146162306a36Sopenharmony_ci hem_list_free_all(hr_dev, &hem_list->mid_bt[i][j], 146262306a36Sopenharmony_ci j != 0); 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci hem_list_free_all(hr_dev, &hem_list->root_bt, true); 146562306a36Sopenharmony_ci INIT_LIST_HEAD(&hem_list->btm_bt); 146662306a36Sopenharmony_ci hem_list->root_ba = 0; 146762306a36Sopenharmony_ci} 146862306a36Sopenharmony_ci 146962306a36Sopenharmony_civoid hns_roce_hem_list_init(struct hns_roce_hem_list *hem_list) 147062306a36Sopenharmony_ci{ 147162306a36Sopenharmony_ci int i, j; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci INIT_LIST_HEAD(&hem_list->root_bt); 147462306a36Sopenharmony_ci INIT_LIST_HEAD(&hem_list->btm_bt); 147562306a36Sopenharmony_ci for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++) 147662306a36Sopenharmony_ci for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++) 147762306a36Sopenharmony_ci INIT_LIST_HEAD(&hem_list->mid_bt[i][j]); 147862306a36Sopenharmony_ci} 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_civoid *hns_roce_hem_list_find_mtt(struct hns_roce_dev *hr_dev, 148162306a36Sopenharmony_ci struct hns_roce_hem_list *hem_list, 148262306a36Sopenharmony_ci int offset, int *mtt_cnt) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci struct list_head *head = &hem_list->btm_bt; 148562306a36Sopenharmony_ci struct hns_roce_hem_item *hem, *temp_hem; 148662306a36Sopenharmony_ci void *cpu_base = NULL; 148762306a36Sopenharmony_ci int nr = 0; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci list_for_each_entry_safe(hem, temp_hem, head, sibling) { 149062306a36Sopenharmony_ci if (hem_list_page_is_in_range(hem, offset)) { 149162306a36Sopenharmony_ci nr = offset - hem->start; 149262306a36Sopenharmony_ci cpu_base = hem->addr + nr * BA_BYTE_LEN; 149362306a36Sopenharmony_ci nr = hem->end + 1 - offset; 149462306a36Sopenharmony_ci break; 149562306a36Sopenharmony_ci } 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (mtt_cnt) 149962306a36Sopenharmony_ci *mtt_cnt = nr; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return cpu_base; 150262306a36Sopenharmony_ci} 1503