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 *)&regions[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 = &regions[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 = &regions[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 = &regions[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