xref: /linux/drivers/infiniband/hw/hns/hns_roce_hem.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
19a443537Soulijun /*
29a443537Soulijun  * Copyright (c) 2016 Hisilicon Limited.
39a443537Soulijun  * Copyright (c) 2007, 2008 Mellanox Technologies. All rights reserved.
49a443537Soulijun  *
59a443537Soulijun  * This software is available to you under a choice of one of two
69a443537Soulijun  * licenses.  You may choose to be licensed under the terms of the GNU
79a443537Soulijun  * General Public License (GPL) Version 2, available from the file
89a443537Soulijun  * COPYING in the main directory of this source tree, or the
99a443537Soulijun  * OpenIB.org BSD license below:
109a443537Soulijun  *
119a443537Soulijun  *     Redistribution and use in source and binary forms, with or
129a443537Soulijun  *     without modification, are permitted provided that the following
139a443537Soulijun  *     conditions are met:
149a443537Soulijun  *
159a443537Soulijun  *      - Redistributions of source code must retain the above
169a443537Soulijun  *        copyright notice, this list of conditions and the following
179a443537Soulijun  *        disclaimer.
189a443537Soulijun  *
199a443537Soulijun  *      - Redistributions in binary form must reproduce the above
209a443537Soulijun  *        copyright notice, this list of conditions and the following
219a443537Soulijun  *        disclaimer in the documentation and/or other materials
229a443537Soulijun  *        provided with the distribution.
239a443537Soulijun  *
249a443537Soulijun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
259a443537Soulijun  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
269a443537Soulijun  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
279a443537Soulijun  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
289a443537Soulijun  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
299a443537Soulijun  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
309a443537Soulijun  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
319a443537Soulijun  * SOFTWARE.
329a443537Soulijun  */
339a443537Soulijun 
349a443537Soulijun #include "hns_roce_device.h"
359a443537Soulijun #include "hns_roce_hem.h"
369a443537Soulijun #include "hns_roce_common.h"
379a443537Soulijun 
382f49de21SXi Wang #define HEM_INDEX_BUF			BIT(0)
392f49de21SXi Wang #define HEM_INDEX_L0			BIT(1)
402f49de21SXi Wang #define HEM_INDEX_L1			BIT(2)
412f49de21SXi Wang struct hns_roce_hem_index {
422f49de21SXi Wang 	u64 buf;
432f49de21SXi Wang 	u64 l0;
442f49de21SXi Wang 	u64 l1;
452f49de21SXi Wang 	u32 inited; /* indicate which index is available */
462f49de21SXi Wang };
472f49de21SXi Wang 
hns_roce_check_whether_mhop(struct hns_roce_dev * hr_dev,u32 type)48a25d13cbSShaobo Xu bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type)
49a25d13cbSShaobo Xu {
50d7019c0fSLijun Ou 	int hop_num = 0;
51a25d13cbSShaobo Xu 
52d7019c0fSLijun Ou 	switch (type) {
53d7019c0fSLijun Ou 	case HEM_TYPE_QPC:
54d7019c0fSLijun Ou 		hop_num = hr_dev->caps.qpc_hop_num;
55d7019c0fSLijun Ou 		break;
56d7019c0fSLijun Ou 	case HEM_TYPE_MTPT:
57d7019c0fSLijun Ou 		hop_num = hr_dev->caps.mpt_hop_num;
58d7019c0fSLijun Ou 		break;
59d7019c0fSLijun Ou 	case HEM_TYPE_CQC:
60d7019c0fSLijun Ou 		hop_num = hr_dev->caps.cqc_hop_num;
61d7019c0fSLijun Ou 		break;
62d7019c0fSLijun Ou 	case HEM_TYPE_SRQC:
63d7019c0fSLijun Ou 		hop_num = hr_dev->caps.srqc_hop_num;
64d7019c0fSLijun Ou 		break;
65d7019c0fSLijun Ou 	case HEM_TYPE_SCCC:
66d7019c0fSLijun Ou 		hop_num = hr_dev->caps.sccc_hop_num;
67d7019c0fSLijun Ou 		break;
68d7019c0fSLijun Ou 	case HEM_TYPE_QPC_TIMER:
69d7019c0fSLijun Ou 		hop_num = hr_dev->caps.qpc_timer_hop_num;
70d7019c0fSLijun Ou 		break;
71d7019c0fSLijun Ou 	case HEM_TYPE_CQC_TIMER:
72d7019c0fSLijun Ou 		hop_num = hr_dev->caps.cqc_timer_hop_num;
73d7019c0fSLijun Ou 		break;
74d6d91e46SWeihang Li 	case HEM_TYPE_GMV:
75d6d91e46SWeihang Li 		hop_num = hr_dev->caps.gmv_hop_num;
76d6d91e46SWeihang Li 		break;
77d7019c0fSLijun Ou 	default:
78a25d13cbSShaobo Xu 		return false;
79a25d13cbSShaobo Xu 	}
80a25d13cbSShaobo Xu 
81272bba19SRuan Jinjie 	return hop_num;
82d7019c0fSLijun Ou }
83d7019c0fSLijun Ou 
hns_roce_check_hem_null(struct hns_roce_hem ** hem,u64 hem_idx,u32 bt_chunk_num,u64 hem_max_num)8438dcb350SXi Wang static bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 hem_idx,
859bba3f0cSXi Wang 				    u32 bt_chunk_num, u64 hem_max_num)
86a25d13cbSShaobo Xu {
8738dcb350SXi Wang 	u64 start_idx = round_down(hem_idx, bt_chunk_num);
889bba3f0cSXi Wang 	u64 check_max_num = start_idx + bt_chunk_num;
899bba3f0cSXi Wang 	u64 i;
90a25d13cbSShaobo Xu 
919bba3f0cSXi Wang 	for (i = start_idx; (i < check_max_num) && (i < hem_max_num); i++)
9238dcb350SXi Wang 		if (i != hem_idx && hem[i])
93a25d13cbSShaobo Xu 			return false;
94a25d13cbSShaobo Xu 
95a25d13cbSShaobo Xu 	return true;
96a25d13cbSShaobo Xu }
97a25d13cbSShaobo Xu 
hns_roce_check_bt_null(u64 ** bt,u64 ba_idx,u32 bt_chunk_num)9838dcb350SXi Wang static bool hns_roce_check_bt_null(u64 **bt, u64 ba_idx, u32 bt_chunk_num)
99a25d13cbSShaobo Xu {
10038dcb350SXi Wang 	u64 start_idx = round_down(ba_idx, bt_chunk_num);
101a25d13cbSShaobo Xu 	int i;
102a25d13cbSShaobo Xu 
103a25d13cbSShaobo Xu 	for (i = 0; i < bt_chunk_num; i++)
10438dcb350SXi Wang 		if (i != ba_idx && bt[start_idx + i])
105a25d13cbSShaobo Xu 			return false;
106a25d13cbSShaobo Xu 
107a25d13cbSShaobo Xu 	return true;
108a25d13cbSShaobo Xu }
109a25d13cbSShaobo Xu 
hns_roce_get_bt_num(u32 table_type,u32 hop_num)110a25d13cbSShaobo Xu static int hns_roce_get_bt_num(u32 table_type, u32 hop_num)
111a25d13cbSShaobo Xu {
112a25d13cbSShaobo Xu 	if (check_whether_bt_num_3(table_type, hop_num))
113a25d13cbSShaobo Xu 		return 3;
114a25d13cbSShaobo Xu 	else if (check_whether_bt_num_2(table_type, hop_num))
115a25d13cbSShaobo Xu 		return 2;
116a25d13cbSShaobo Xu 	else if (check_whether_bt_num_1(table_type, hop_num))
117a25d13cbSShaobo Xu 		return 1;
118a25d13cbSShaobo Xu 	else
119a25d13cbSShaobo Xu 		return 0;
120a25d13cbSShaobo Xu }
121a25d13cbSShaobo Xu 
get_hem_table_config(struct hns_roce_dev * hr_dev,struct hns_roce_hem_mhop * mhop,u32 type)122d7019c0fSLijun Ou static int get_hem_table_config(struct hns_roce_dev *hr_dev,
123d7019c0fSLijun Ou 				struct hns_roce_hem_mhop *mhop,
124d7019c0fSLijun Ou 				u32 type)
125a25d13cbSShaobo Xu {
126a25d13cbSShaobo Xu 	struct device *dev = hr_dev->dev;
127a25d13cbSShaobo Xu 
128d7019c0fSLijun Ou 	switch (type) {
129a25d13cbSShaobo Xu 	case HEM_TYPE_QPC:
130a25d13cbSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_buf_pg_sz
131a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
132a25d13cbSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_ba_pg_sz
133a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
134a25d13cbSShaobo Xu 		mhop->ba_l0_num = hr_dev->caps.qpc_bt_num;
135a25d13cbSShaobo Xu 		mhop->hop_num = hr_dev->caps.qpc_hop_num;
136a25d13cbSShaobo Xu 		break;
137a25d13cbSShaobo Xu 	case HEM_TYPE_MTPT:
138a25d13cbSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.mpt_buf_pg_sz
139a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
140a25d13cbSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.mpt_ba_pg_sz
141a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
142a25d13cbSShaobo Xu 		mhop->ba_l0_num = hr_dev->caps.mpt_bt_num;
143a25d13cbSShaobo Xu 		mhop->hop_num = hr_dev->caps.mpt_hop_num;
144a25d13cbSShaobo Xu 		break;
145a25d13cbSShaobo Xu 	case HEM_TYPE_CQC:
146a25d13cbSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_buf_pg_sz
147a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
148a25d13cbSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_ba_pg_sz
149a25d13cbSShaobo Xu 					    + PAGE_SHIFT);
150a25d13cbSShaobo Xu 		mhop->ba_l0_num = hr_dev->caps.cqc_bt_num;
151a25d13cbSShaobo Xu 		mhop->hop_num = hr_dev->caps.cqc_hop_num;
152a25d13cbSShaobo Xu 		break;
1536a157f7dSYangyang Li 	case HEM_TYPE_SCCC:
1546a157f7dSYangyang Li 		mhop->buf_chunk_size = 1 << (hr_dev->caps.sccc_buf_pg_sz
1556a157f7dSYangyang Li 					     + PAGE_SHIFT);
1566a157f7dSYangyang Li 		mhop->bt_chunk_size = 1 << (hr_dev->caps.sccc_ba_pg_sz
1576a157f7dSYangyang Li 					    + PAGE_SHIFT);
1586a157f7dSYangyang Li 		mhop->ba_l0_num = hr_dev->caps.sccc_bt_num;
1596a157f7dSYangyang Li 		mhop->hop_num = hr_dev->caps.sccc_hop_num;
1606a157f7dSYangyang Li 		break;
1610e40dc2fSYangyang Li 	case HEM_TYPE_QPC_TIMER:
1620e40dc2fSYangyang Li 		mhop->buf_chunk_size = 1 << (hr_dev->caps.qpc_timer_buf_pg_sz
1630e40dc2fSYangyang Li 					     + PAGE_SHIFT);
1640e40dc2fSYangyang Li 		mhop->bt_chunk_size = 1 << (hr_dev->caps.qpc_timer_ba_pg_sz
1650e40dc2fSYangyang Li 					    + PAGE_SHIFT);
1660e40dc2fSYangyang Li 		mhop->ba_l0_num = hr_dev->caps.qpc_timer_bt_num;
1670e40dc2fSYangyang Li 		mhop->hop_num = hr_dev->caps.qpc_timer_hop_num;
1680e40dc2fSYangyang Li 		break;
1690e40dc2fSYangyang Li 	case HEM_TYPE_CQC_TIMER:
1700e40dc2fSYangyang Li 		mhop->buf_chunk_size = 1 << (hr_dev->caps.cqc_timer_buf_pg_sz
1710e40dc2fSYangyang Li 					     + PAGE_SHIFT);
1720e40dc2fSYangyang Li 		mhop->bt_chunk_size = 1 << (hr_dev->caps.cqc_timer_ba_pg_sz
1730e40dc2fSYangyang Li 					    + PAGE_SHIFT);
1740e40dc2fSYangyang Li 		mhop->ba_l0_num = hr_dev->caps.cqc_timer_bt_num;
1750e40dc2fSYangyang Li 		mhop->hop_num = hr_dev->caps.cqc_timer_hop_num;
1760e40dc2fSYangyang Li 		break;
177a25d13cbSShaobo Xu 	case HEM_TYPE_SRQC:
178a25d13cbSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.srqc_buf_pg_sz
179a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
180a25d13cbSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.srqc_ba_pg_sz
181a25d13cbSShaobo Xu 					     + PAGE_SHIFT);
182a25d13cbSShaobo Xu 		mhop->ba_l0_num = hr_dev->caps.srqc_bt_num;
183a25d13cbSShaobo Xu 		mhop->hop_num = hr_dev->caps.srqc_hop_num;
184a25d13cbSShaobo Xu 		break;
185d6d91e46SWeihang Li 	case HEM_TYPE_GMV:
186d6d91e46SWeihang Li 		mhop->buf_chunk_size = 1 << (hr_dev->caps.gmv_buf_pg_sz +
187d6d91e46SWeihang Li 					     PAGE_SHIFT);
188d6d91e46SWeihang Li 		mhop->bt_chunk_size = 1 << (hr_dev->caps.gmv_ba_pg_sz +
189d6d91e46SWeihang Li 					    PAGE_SHIFT);
190d6d91e46SWeihang Li 		mhop->ba_l0_num = hr_dev->caps.gmv_bt_num;
191d6d91e46SWeihang Li 		mhop->hop_num = hr_dev->caps.gmv_hop_num;
192d6d91e46SWeihang Li 		break;
193a25d13cbSShaobo Xu 	default:
19461918e9bSYixing Liu 		dev_err(dev, "table %u not support multi-hop addressing!\n",
195d7019c0fSLijun Ou 			type);
196a25d13cbSShaobo Xu 		return -EINVAL;
197a25d13cbSShaobo Xu 	}
198a25d13cbSShaobo Xu 
199d7019c0fSLijun Ou 	return 0;
200d7019c0fSLijun Ou }
201d7019c0fSLijun Ou 
hns_roce_calc_hem_mhop(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long * obj,struct hns_roce_hem_mhop * mhop)202d7019c0fSLijun Ou int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev,
203d7019c0fSLijun Ou 			   struct hns_roce_hem_table *table, unsigned long *obj,
204d7019c0fSLijun Ou 			   struct hns_roce_hem_mhop *mhop)
205d7019c0fSLijun Ou {
206d7019c0fSLijun Ou 	struct device *dev = hr_dev->dev;
207d7019c0fSLijun Ou 	u32 chunk_ba_num;
208dc93a0d9SLang Cheng 	u32 chunk_size;
209d7019c0fSLijun Ou 	u32 table_idx;
210d7019c0fSLijun Ou 	u32 bt_num;
211d7019c0fSLijun Ou 
212d7019c0fSLijun Ou 	if (get_hem_table_config(hr_dev, mhop, table->type))
213d7019c0fSLijun Ou 		return -EINVAL;
214d7019c0fSLijun Ou 
215a25d13cbSShaobo Xu 	if (!obj)
216a25d13cbSShaobo Xu 		return 0;
217a25d13cbSShaobo Xu 
2186a93c77aSShaobo Xu 	/*
2196a157f7dSYangyang Li 	 * QPC/MTPT/CQC/SRQC/SCCC alloc hem for buffer pages.
2206a93c77aSShaobo Xu 	 * MTT/CQE alloc hem for bt pages.
2216a93c77aSShaobo Xu 	 */
222a25d13cbSShaobo Xu 	bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num);
2232a3d923fSLijun Ou 	chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN;
2246a93c77aSShaobo Xu 	chunk_size = table->type < HEM_TYPE_MTT ? mhop->buf_chunk_size :
2256a93c77aSShaobo Xu 			      mhop->bt_chunk_size;
22661b460d1SXi Wang 	table_idx = *obj / (chunk_size / table->obj_size);
227a25d13cbSShaobo Xu 	switch (bt_num) {
228a25d13cbSShaobo Xu 	case 3:
229a25d13cbSShaobo Xu 		mhop->l2_idx = table_idx & (chunk_ba_num - 1);
230a25d13cbSShaobo Xu 		mhop->l1_idx = table_idx / chunk_ba_num & (chunk_ba_num - 1);
23173b4e1f4SLijun Ou 		mhop->l0_idx = (table_idx / chunk_ba_num) / chunk_ba_num;
232a25d13cbSShaobo Xu 		break;
233a25d13cbSShaobo Xu 	case 2:
234a25d13cbSShaobo Xu 		mhop->l1_idx = table_idx & (chunk_ba_num - 1);
235a25d13cbSShaobo Xu 		mhop->l0_idx = table_idx / chunk_ba_num;
236a25d13cbSShaobo Xu 		break;
237a25d13cbSShaobo Xu 	case 1:
238a25d13cbSShaobo Xu 		mhop->l0_idx = table_idx;
239a25d13cbSShaobo Xu 		break;
240a25d13cbSShaobo Xu 	default:
24161918e9bSYixing Liu 		dev_err(dev, "table %u not support hop_num = %u!\n",
242a25d13cbSShaobo Xu 			table->type, mhop->hop_num);
243a25d13cbSShaobo Xu 		return -EINVAL;
244a25d13cbSShaobo Xu 	}
245a25d13cbSShaobo Xu 	if (mhop->l0_idx >= mhop->ba_l0_num)
246a25d13cbSShaobo Xu 		mhop->l0_idx %= mhop->ba_l0_num;
247a25d13cbSShaobo Xu 
248a25d13cbSShaobo Xu 	return 0;
249a25d13cbSShaobo Xu }
250a25d13cbSShaobo Xu 
hns_roce_alloc_hem(struct hns_roce_dev * hr_dev,unsigned long hem_alloc_size,gfp_t gfp_mask)251a25d13cbSShaobo Xu static struct hns_roce_hem *hns_roce_alloc_hem(struct hns_roce_dev *hr_dev,
252a25d13cbSShaobo Xu 					       unsigned long hem_alloc_size,
2539a443537Soulijun 					       gfp_t gfp_mask)
2549a443537Soulijun {
2559a443537Soulijun 	struct hns_roce_hem *hem;
2569a443537Soulijun 	int order;
2579a443537Soulijun 	void *buf;
2589a443537Soulijun 
2599a443537Soulijun 	WARN_ON(gfp_mask & __GFP_HIGHMEM);
2609a443537Soulijun 
261c00743cbSYunsheng Lin 	order = get_order(hem_alloc_size);
262c00743cbSYunsheng Lin 	if (PAGE_SIZE << order != hem_alloc_size) {
263c00743cbSYunsheng Lin 		dev_err(hr_dev->dev, "invalid hem_alloc_size: %lu!\n",
264c00743cbSYunsheng Lin 			hem_alloc_size);
265c00743cbSYunsheng Lin 		return NULL;
266c00743cbSYunsheng Lin 	}
267c00743cbSYunsheng Lin 
2689a443537Soulijun 	hem = kmalloc(sizeof(*hem),
2699a443537Soulijun 		      gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
2709a443537Soulijun 	if (!hem)
2719a443537Soulijun 		return NULL;
2729a443537Soulijun 
273c00743cbSYunsheng Lin 	buf = dma_alloc_coherent(hr_dev->dev, hem_alloc_size,
274c00743cbSYunsheng Lin 				 &hem->dma, gfp_mask);
2759a443537Soulijun 	if (!buf)
2769a443537Soulijun 		goto fail;
2779a443537Soulijun 
278c00743cbSYunsheng Lin 	hem->buf = buf;
279c00743cbSYunsheng Lin 	hem->size = hem_alloc_size;
2809a443537Soulijun 
2819a443537Soulijun 	return hem;
2829a443537Soulijun 
2839a443537Soulijun fail:
284dc3bda6eSwenglianfa 	kfree(hem);
2859a443537Soulijun 	return NULL;
2869a443537Soulijun }
2879a443537Soulijun 
hns_roce_free_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem * hem)2889a443537Soulijun void hns_roce_free_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem *hem)
2899a443537Soulijun {
2909a443537Soulijun 	if (!hem)
2919a443537Soulijun 		return;
2929a443537Soulijun 
293c00743cbSYunsheng Lin 	dma_free_coherent(hr_dev->dev, hem->size, hem->buf, hem->dma);
2949a443537Soulijun 
2959a443537Soulijun 	kfree(hem);
2969a443537Soulijun }
2979a443537Soulijun 
calc_hem_config(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj,struct hns_roce_hem_mhop * mhop,struct hns_roce_hem_index * index)2982f49de21SXi Wang static int calc_hem_config(struct hns_roce_dev *hr_dev,
2992f49de21SXi Wang 			   struct hns_roce_hem_table *table, unsigned long obj,
3002f49de21SXi Wang 			   struct hns_roce_hem_mhop *mhop,
3012f49de21SXi Wang 			   struct hns_roce_hem_index *index)
302a25d13cbSShaobo Xu {
3032f49de21SXi Wang 	struct ib_device *ibdev = &hr_dev->ib_dev;
304a25d13cbSShaobo Xu 	unsigned long mhop_obj = obj;
3052f49de21SXi Wang 	u32 l0_idx, l1_idx, l2_idx;
3062f49de21SXi Wang 	u32 chunk_ba_num;
3072f49de21SXi Wang 	u32 bt_num;
308a25d13cbSShaobo Xu 	int ret;
309a25d13cbSShaobo Xu 
3102f49de21SXi Wang 	ret = hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, mhop);
311a25d13cbSShaobo Xu 	if (ret)
312a25d13cbSShaobo Xu 		return ret;
313a25d13cbSShaobo Xu 
3142f49de21SXi Wang 	l0_idx = mhop->l0_idx;
3152f49de21SXi Wang 	l1_idx = mhop->l1_idx;
3162f49de21SXi Wang 	l2_idx = mhop->l2_idx;
3172f49de21SXi Wang 	chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN;
3182f49de21SXi Wang 	bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num);
319a25d13cbSShaobo Xu 	switch (bt_num) {
320a25d13cbSShaobo Xu 	case 3:
3212f49de21SXi Wang 		index->l1 = l0_idx * chunk_ba_num + l1_idx;
3222f49de21SXi Wang 		index->l0 = l0_idx;
3232f49de21SXi Wang 		index->buf = l0_idx * chunk_ba_num * chunk_ba_num +
3242f49de21SXi Wang 			     l1_idx * chunk_ba_num + l2_idx;
325a25d13cbSShaobo Xu 		break;
326a25d13cbSShaobo Xu 	case 2:
3272f49de21SXi Wang 		index->l0 = l0_idx;
3282f49de21SXi Wang 		index->buf = l0_idx * chunk_ba_num + l1_idx;
329a25d13cbSShaobo Xu 		break;
330a25d13cbSShaobo Xu 	case 1:
3312f49de21SXi Wang 		index->buf = l0_idx;
332a25d13cbSShaobo Xu 		break;
333a25d13cbSShaobo Xu 	default:
33461918e9bSYixing Liu 		ibdev_err(ibdev, "table %u not support mhop.hop_num = %u!\n",
3352f49de21SXi Wang 			  table->type, mhop->hop_num);
336a25d13cbSShaobo Xu 		return -EINVAL;
337a25d13cbSShaobo Xu 	}
338a25d13cbSShaobo Xu 
3392f49de21SXi Wang 	if (unlikely(index->buf >= table->num_hem)) {
34061918e9bSYixing Liu 		ibdev_err(ibdev, "table %u exceed hem limt idx %llu, max %lu!\n",
3412f49de21SXi Wang 			  table->type, index->buf, table->num_hem);
3429bba3f0cSXi Wang 		return -EINVAL;
3439bba3f0cSXi Wang 	}
3449bba3f0cSXi Wang 
3452f49de21SXi Wang 	return 0;
346a25d13cbSShaobo Xu }
347a25d13cbSShaobo Xu 
free_mhop_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,struct hns_roce_hem_mhop * mhop,struct hns_roce_hem_index * index)3482f49de21SXi Wang static void free_mhop_hem(struct hns_roce_dev *hr_dev,
3492f49de21SXi Wang 			  struct hns_roce_hem_table *table,
3502f49de21SXi Wang 			  struct hns_roce_hem_mhop *mhop,
3512f49de21SXi Wang 			  struct hns_roce_hem_index *index)
3522f49de21SXi Wang {
3532f49de21SXi Wang 	u32 bt_size = mhop->bt_chunk_size;
3542f49de21SXi Wang 	struct device *dev = hr_dev->dev;
3552f49de21SXi Wang 
3562f49de21SXi Wang 	if (index->inited & HEM_INDEX_BUF) {
3572f49de21SXi Wang 		hns_roce_free_hem(hr_dev, table->hem[index->buf]);
3582f49de21SXi Wang 		table->hem[index->buf] = NULL;
3592f49de21SXi Wang 	}
3602f49de21SXi Wang 
3612f49de21SXi Wang 	if (index->inited & HEM_INDEX_L1) {
3622f49de21SXi Wang 		dma_free_coherent(dev, bt_size, table->bt_l1[index->l1],
3632f49de21SXi Wang 				  table->bt_l1_dma_addr[index->l1]);
3642f49de21SXi Wang 		table->bt_l1[index->l1] = NULL;
3652f49de21SXi Wang 	}
3662f49de21SXi Wang 
3672f49de21SXi Wang 	if (index->inited & HEM_INDEX_L0) {
3682f49de21SXi Wang 		dma_free_coherent(dev, bt_size, table->bt_l0[index->l0],
3692f49de21SXi Wang 				  table->bt_l0_dma_addr[index->l0]);
3702f49de21SXi Wang 		table->bt_l0[index->l0] = NULL;
3712f49de21SXi Wang 	}
3722f49de21SXi Wang }
3732f49de21SXi Wang 
alloc_mhop_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,struct hns_roce_hem_mhop * mhop,struct hns_roce_hem_index * index)3742f49de21SXi Wang static int alloc_mhop_hem(struct hns_roce_dev *hr_dev,
3752f49de21SXi Wang 			  struct hns_roce_hem_table *table,
3762f49de21SXi Wang 			  struct hns_roce_hem_mhop *mhop,
3772f49de21SXi Wang 			  struct hns_roce_hem_index *index)
3782f49de21SXi Wang {
3792f49de21SXi Wang 	u32 bt_size = mhop->bt_chunk_size;
3802f49de21SXi Wang 	struct device *dev = hr_dev->dev;
3812f49de21SXi Wang 	gfp_t flag;
3822f49de21SXi Wang 	u64 bt_ba;
3832f49de21SXi Wang 	u32 size;
3842f49de21SXi Wang 	int ret;
3852f49de21SXi Wang 
386a25d13cbSShaobo Xu 	/* alloc L1 BA's chunk */
3872f49de21SXi Wang 	if ((check_whether_bt_num_3(table->type, mhop->hop_num) ||
3882f49de21SXi Wang 	     check_whether_bt_num_2(table->type, mhop->hop_num)) &&
3892f49de21SXi Wang 	     !table->bt_l0[index->l0]) {
3902f49de21SXi Wang 		table->bt_l0[index->l0] = dma_alloc_coherent(dev, bt_size,
3912f49de21SXi Wang 					    &table->bt_l0_dma_addr[index->l0],
392a25d13cbSShaobo Xu 					    GFP_KERNEL);
3932f49de21SXi Wang 		if (!table->bt_l0[index->l0]) {
394a25d13cbSShaobo Xu 			ret = -ENOMEM;
395a25d13cbSShaobo Xu 			goto out;
396a25d13cbSShaobo Xu 		}
3972f49de21SXi Wang 		index->inited |= HEM_INDEX_L0;
398a25d13cbSShaobo Xu 	}
399a25d13cbSShaobo Xu 
400a25d13cbSShaobo Xu 	/* alloc L2 BA's chunk */
4012f49de21SXi Wang 	if (check_whether_bt_num_3(table->type, mhop->hop_num) &&
4022f49de21SXi Wang 	    !table->bt_l1[index->l1])  {
4032f49de21SXi Wang 		table->bt_l1[index->l1] = dma_alloc_coherent(dev, bt_size,
4042f49de21SXi Wang 					    &table->bt_l1_dma_addr[index->l1],
405a25d13cbSShaobo Xu 					    GFP_KERNEL);
4062f49de21SXi Wang 		if (!table->bt_l1[index->l1]) {
407a25d13cbSShaobo Xu 			ret = -ENOMEM;
4082f49de21SXi Wang 			goto err_alloc_hem;
409a25d13cbSShaobo Xu 		}
4102f49de21SXi Wang 		index->inited |= HEM_INDEX_L1;
4112f49de21SXi Wang 		*(table->bt_l0[index->l0] + mhop->l1_idx) =
4122f49de21SXi Wang 					       table->bt_l1_dma_addr[index->l1];
413a25d13cbSShaobo Xu 	}
414a25d13cbSShaobo Xu 
4156a93c77aSShaobo Xu 	/*
4166a157f7dSYangyang Li 	 * alloc buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC.
4176a93c77aSShaobo Xu 	 * alloc bt space chunk for MTT/CQE.
4186a93c77aSShaobo Xu 	 */
4192f49de21SXi Wang 	size = table->type < HEM_TYPE_MTT ? mhop->buf_chunk_size : bt_size;
42029dc0635SYunsheng Lin 	flag = GFP_KERNEL | __GFP_NOWARN;
421c00743cbSYunsheng Lin 	table->hem[index->buf] = hns_roce_alloc_hem(hr_dev, size, flag);
4222f49de21SXi Wang 	if (!table->hem[index->buf]) {
423a25d13cbSShaobo Xu 		ret = -ENOMEM;
4242f49de21SXi Wang 		goto err_alloc_hem;
425a25d13cbSShaobo Xu 	}
426a25d13cbSShaobo Xu 
4272f49de21SXi Wang 	index->inited |= HEM_INDEX_BUF;
428c00743cbSYunsheng Lin 	bt_ba = table->hem[index->buf]->dma;
429c00743cbSYunsheng Lin 
430a25d13cbSShaobo Xu 	if (table->type < HEM_TYPE_MTT) {
4312f49de21SXi Wang 		if (mhop->hop_num == 2)
4322f49de21SXi Wang 			*(table->bt_l1[index->l1] + mhop->l2_idx) = bt_ba;
4332f49de21SXi Wang 		else if (mhop->hop_num == 1)
4342f49de21SXi Wang 			*(table->bt_l0[index->l0] + mhop->l1_idx) = bt_ba;
4352f49de21SXi Wang 	} else if (mhop->hop_num == 2) {
4362f49de21SXi Wang 		*(table->bt_l0[index->l0] + mhop->l1_idx) = bt_ba;
4372f49de21SXi Wang 	}
4382f49de21SXi Wang 
4392f49de21SXi Wang 	return 0;
4402f49de21SXi Wang err_alloc_hem:
4412f49de21SXi Wang 	free_mhop_hem(hr_dev, table, mhop, index);
4422f49de21SXi Wang out:
4432f49de21SXi Wang 	return ret;
4442f49de21SXi Wang }
4452f49de21SXi Wang 
set_mhop_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj,struct hns_roce_hem_mhop * mhop,struct hns_roce_hem_index * index)4462f49de21SXi Wang static int set_mhop_hem(struct hns_roce_dev *hr_dev,
4472f49de21SXi Wang 			struct hns_roce_hem_table *table, unsigned long obj,
4482f49de21SXi Wang 			struct hns_roce_hem_mhop *mhop,
4492f49de21SXi Wang 			struct hns_roce_hem_index *index)
4502f49de21SXi Wang {
4512f49de21SXi Wang 	struct ib_device *ibdev = &hr_dev->ib_dev;
452e50cda2bSWenpeng Liang 	u32 step_idx;
453d35dc58dSGustavo A. R. Silva 	int ret = 0;
4542f49de21SXi Wang 
4552f49de21SXi Wang 	if (index->inited & HEM_INDEX_L0) {
4562f49de21SXi Wang 		ret = hr_dev->hw->set_hem(hr_dev, table, obj, 0);
4572f49de21SXi Wang 		if (ret) {
4582f49de21SXi Wang 			ibdev_err(ibdev, "set HEM step 0 failed!\n");
4592f49de21SXi Wang 			goto out;
4602f49de21SXi Wang 		}
4612f49de21SXi Wang 	}
4622f49de21SXi Wang 
4632f49de21SXi Wang 	if (index->inited & HEM_INDEX_L1) {
4642f49de21SXi Wang 		ret = hr_dev->hw->set_hem(hr_dev, table, obj, 1);
4652f49de21SXi Wang 		if (ret) {
4662f49de21SXi Wang 			ibdev_err(ibdev, "set HEM step 1 failed!\n");
4672f49de21SXi Wang 			goto out;
4682f49de21SXi Wang 		}
4692f49de21SXi Wang 	}
4702f49de21SXi Wang 
4712f49de21SXi Wang 	if (index->inited & HEM_INDEX_BUF) {
4722f49de21SXi Wang 		if (mhop->hop_num == HNS_ROCE_HOP_NUM_0)
473a25d13cbSShaobo Xu 			step_idx = 0;
4742f49de21SXi Wang 		else
4752f49de21SXi Wang 			step_idx = mhop->hop_num;
4762f49de21SXi Wang 		ret = hr_dev->hw->set_hem(hr_dev, table, obj, step_idx);
4772f49de21SXi Wang 		if (ret)
4782f49de21SXi Wang 			ibdev_err(ibdev, "set HEM step last failed!\n");
4792f49de21SXi Wang 	}
4802f49de21SXi Wang out:
4812f49de21SXi Wang 	return ret;
4822f49de21SXi Wang }
4832f49de21SXi Wang 
hns_roce_table_mhop_get(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj)4842f49de21SXi Wang static int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev,
4852f49de21SXi Wang 				   struct hns_roce_hem_table *table,
4862f49de21SXi Wang 				   unsigned long obj)
4872f49de21SXi Wang {
4882f49de21SXi Wang 	struct ib_device *ibdev = &hr_dev->ib_dev;
4892f49de21SXi Wang 	struct hns_roce_hem_index index = {};
4902f49de21SXi Wang 	struct hns_roce_hem_mhop mhop = {};
4912f49de21SXi Wang 	int ret;
4922f49de21SXi Wang 
4932f49de21SXi Wang 	ret = calc_hem_config(hr_dev, table, obj, &mhop, &index);
4942f49de21SXi Wang 	if (ret) {
4952f49de21SXi Wang 		ibdev_err(ibdev, "calc hem config failed!\n");
4962f49de21SXi Wang 		return ret;
4972f49de21SXi Wang 	}
4982f49de21SXi Wang 
4992f49de21SXi Wang 	mutex_lock(&table->mutex);
5002f49de21SXi Wang 	if (table->hem[index.buf]) {
50182eb481dSWeihang Li 		refcount_inc(&table->hem[index.buf]->refcount);
5022f49de21SXi Wang 		goto out;
5032f49de21SXi Wang 	}
5042f49de21SXi Wang 
5052f49de21SXi Wang 	ret = alloc_mhop_hem(hr_dev, table, &mhop, &index);
5062f49de21SXi Wang 	if (ret) {
5072f49de21SXi Wang 		ibdev_err(ibdev, "alloc mhop hem failed!\n");
5082f49de21SXi Wang 		goto out;
509a25d13cbSShaobo Xu 	}
510a25d13cbSShaobo Xu 
511a25d13cbSShaobo Xu 	/* set HEM base address to hardware */
5122f49de21SXi Wang 	if (table->type < HEM_TYPE_MTT) {
5132f49de21SXi Wang 		ret = set_mhop_hem(hr_dev, table, obj, &mhop, &index);
5142f49de21SXi Wang 		if (ret) {
5152f49de21SXi Wang 			ibdev_err(ibdev, "set HEM address to HW failed!\n");
5162f49de21SXi Wang 			goto err_alloc;
517a25d13cbSShaobo Xu 		}
518a25d13cbSShaobo Xu 	}
519a25d13cbSShaobo Xu 
52082eb481dSWeihang Li 	refcount_set(&table->hem[index.buf]->refcount, 1);
521a25d13cbSShaobo Xu 	goto out;
522a25d13cbSShaobo Xu 
5232f49de21SXi Wang err_alloc:
5242f49de21SXi Wang 	free_mhop_hem(hr_dev, table, &mhop, &index);
525a25d13cbSShaobo Xu out:
526a25d13cbSShaobo Xu 	mutex_unlock(&table->mutex);
527a25d13cbSShaobo Xu 	return ret;
528a25d13cbSShaobo Xu }
529a25d13cbSShaobo Xu 
hns_roce_table_get(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj)5309a443537Soulijun int hns_roce_table_get(struct hns_roce_dev *hr_dev,
5319a443537Soulijun 		       struct hns_roce_hem_table *table, unsigned long obj)
5329a443537Soulijun {
53313ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
5349a443537Soulijun 	unsigned long i;
535dc93a0d9SLang Cheng 	int ret = 0;
5369a443537Soulijun 
537a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type))
538a25d13cbSShaobo Xu 		return hns_roce_table_mhop_get(hr_dev, table, obj);
539a25d13cbSShaobo Xu 
54061b460d1SXi Wang 	i = obj / (table->table_chunk_size / table->obj_size);
5419a443537Soulijun 
5429a443537Soulijun 	mutex_lock(&table->mutex);
5439a443537Soulijun 
5449a443537Soulijun 	if (table->hem[i]) {
54582eb481dSWeihang Li 		refcount_inc(&table->hem[i]->refcount);
5469a443537Soulijun 		goto out;
5479a443537Soulijun 	}
5489a443537Soulijun 
5499a443537Soulijun 	table->hem[i] = hns_roce_alloc_hem(hr_dev,
55029a1fe5dSWei Hu(Xavier) 				       table->table_chunk_size,
55129dc0635SYunsheng Lin 				       GFP_KERNEL | __GFP_NOWARN);
5529a443537Soulijun 	if (!table->hem[i]) {
5539a443537Soulijun 		ret = -ENOMEM;
5549a443537Soulijun 		goto out;
5559a443537Soulijun 	}
5569a443537Soulijun 
5579a443537Soulijun 	/* Set HEM base address(128K/page, pa) to Hardware */
558cf5b608fSChengchang Tang 	ret = hr_dev->hw->set_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT);
559cf5b608fSChengchang Tang 	if (ret) {
56008eb3018SWei Hu(Xavier) 		hns_roce_free_hem(hr_dev, table->hem[i]);
56108eb3018SWei Hu(Xavier) 		table->hem[i] = NULL;
562cf5b608fSChengchang Tang 		dev_err(dev, "set HEM base address to HW failed, ret = %d.\n",
563cf5b608fSChengchang Tang 			ret);
5649a443537Soulijun 		goto out;
5659a443537Soulijun 	}
5669a443537Soulijun 
56782eb481dSWeihang Li 	refcount_set(&table->hem[i]->refcount, 1);
5689a443537Soulijun out:
5699a443537Soulijun 	mutex_unlock(&table->mutex);
5709a443537Soulijun 	return ret;
5719a443537Soulijun }
5729a443537Soulijun 
clear_mhop_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj,struct hns_roce_hem_mhop * mhop,struct hns_roce_hem_index * index)57338dcb350SXi Wang static void clear_mhop_hem(struct hns_roce_dev *hr_dev,
57438dcb350SXi Wang 			   struct hns_roce_hem_table *table, unsigned long obj,
57538dcb350SXi Wang 			   struct hns_roce_hem_mhop *mhop,
57638dcb350SXi Wang 			   struct hns_roce_hem_index *index)
57738dcb350SXi Wang {
57838dcb350SXi Wang 	struct ib_device *ibdev = &hr_dev->ib_dev;
57938dcb350SXi Wang 	u32 hop_num = mhop->hop_num;
58038dcb350SXi Wang 	u32 chunk_ba_num;
581e50cda2bSWenpeng Liang 	u32 step_idx;
582a519a612SChengchang Tang 	int ret;
58338dcb350SXi Wang 
58438dcb350SXi Wang 	index->inited = HEM_INDEX_BUF;
58538dcb350SXi Wang 	chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN;
58638dcb350SXi Wang 	if (check_whether_bt_num_2(table->type, hop_num)) {
58738dcb350SXi Wang 		if (hns_roce_check_hem_null(table->hem, index->buf,
58838dcb350SXi Wang 					    chunk_ba_num, table->num_hem))
58938dcb350SXi Wang 			index->inited |= HEM_INDEX_L0;
59038dcb350SXi Wang 	} else if (check_whether_bt_num_3(table->type, hop_num)) {
59138dcb350SXi Wang 		if (hns_roce_check_hem_null(table->hem, index->buf,
59238dcb350SXi Wang 					    chunk_ba_num, table->num_hem)) {
59338dcb350SXi Wang 			index->inited |= HEM_INDEX_L1;
59438dcb350SXi Wang 			if (hns_roce_check_bt_null(table->bt_l1, index->l1,
59538dcb350SXi Wang 						   chunk_ba_num))
59638dcb350SXi Wang 				index->inited |= HEM_INDEX_L0;
59738dcb350SXi Wang 		}
59838dcb350SXi Wang 	}
59938dcb350SXi Wang 
60038dcb350SXi Wang 	if (table->type < HEM_TYPE_MTT) {
60138dcb350SXi Wang 		if (hop_num == HNS_ROCE_HOP_NUM_0)
60238dcb350SXi Wang 			step_idx = 0;
60338dcb350SXi Wang 		else
60438dcb350SXi Wang 			step_idx = hop_num;
60538dcb350SXi Wang 
606a519a612SChengchang Tang 		ret = hr_dev->hw->clear_hem(hr_dev, table, obj, step_idx);
607a519a612SChengchang Tang 		if (ret)
608a519a612SChengchang Tang 			ibdev_warn(ibdev, "failed to clear hop%u HEM, ret = %d.\n",
609a519a612SChengchang Tang 				   hop_num, ret);
61038dcb350SXi Wang 
611a519a612SChengchang Tang 		if (index->inited & HEM_INDEX_L1) {
612a519a612SChengchang Tang 			ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 1);
613a519a612SChengchang Tang 			if (ret)
614a519a612SChengchang Tang 				ibdev_warn(ibdev, "failed to clear HEM step 1, ret = %d.\n",
615a519a612SChengchang Tang 					   ret);
616a519a612SChengchang Tang 		}
61738dcb350SXi Wang 
618a519a612SChengchang Tang 		if (index->inited & HEM_INDEX_L0) {
619a519a612SChengchang Tang 			ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 0);
620a519a612SChengchang Tang 			if (ret)
621a519a612SChengchang Tang 				ibdev_warn(ibdev, "failed to clear HEM step 0, ret = %d.\n",
622a519a612SChengchang Tang 					   ret);
623a519a612SChengchang Tang 		}
62438dcb350SXi Wang 	}
62538dcb350SXi Wang }
62638dcb350SXi Wang 
hns_roce_table_mhop_put(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj,int check_refcount)627281d0ccfSColin Ian King static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev,
628a25d13cbSShaobo Xu 				    struct hns_roce_hem_table *table,
629a25d13cbSShaobo Xu 				    unsigned long obj,
630a25d13cbSShaobo Xu 				    int check_refcount)
631a25d13cbSShaobo Xu {
63238dcb350SXi Wang 	struct ib_device *ibdev = &hr_dev->ib_dev;
63338dcb350SXi Wang 	struct hns_roce_hem_index index = {};
63438dcb350SXi Wang 	struct hns_roce_hem_mhop mhop = {};
635a25d13cbSShaobo Xu 	int ret;
636a25d13cbSShaobo Xu 
63738dcb350SXi Wang 	ret = calc_hem_config(hr_dev, table, obj, &mhop, &index);
63838dcb350SXi Wang 	if (ret) {
63938dcb350SXi Wang 		ibdev_err(ibdev, "calc hem config failed!\n");
640a25d13cbSShaobo Xu 		return;
641a25d13cbSShaobo Xu 	}
642a25d13cbSShaobo Xu 
64382eb481dSWeihang Li 	if (!check_refcount)
644a25d13cbSShaobo Xu 		mutex_lock(&table->mutex);
64582eb481dSWeihang Li 	else if (!refcount_dec_and_mutex_lock(&table->hem[index.buf]->refcount,
64682eb481dSWeihang Li 					      &table->mutex))
647a25d13cbSShaobo Xu 		return;
648a25d13cbSShaobo Xu 
64938dcb350SXi Wang 	clear_mhop_hem(hr_dev, table, obj, &mhop, &index);
65038dcb350SXi Wang 	free_mhop_hem(hr_dev, table, &mhop, &index);
651a25d13cbSShaobo Xu 
652a25d13cbSShaobo Xu 	mutex_unlock(&table->mutex);
653a25d13cbSShaobo Xu }
654a25d13cbSShaobo Xu 
hns_roce_table_put(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj)6559a443537Soulijun void hns_roce_table_put(struct hns_roce_dev *hr_dev,
6569a443537Soulijun 			struct hns_roce_hem_table *table, unsigned long obj)
6579a443537Soulijun {
65813ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
6599a443537Soulijun 	unsigned long i;
660a519a612SChengchang Tang 	int ret;
6619a443537Soulijun 
662a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
663a25d13cbSShaobo Xu 		hns_roce_table_mhop_put(hr_dev, table, obj, 1);
664a25d13cbSShaobo Xu 		return;
665a25d13cbSShaobo Xu 	}
666a25d13cbSShaobo Xu 
66761b460d1SXi Wang 	i = obj / (table->table_chunk_size / table->obj_size);
6689a443537Soulijun 
66982eb481dSWeihang Li 	if (!refcount_dec_and_mutex_lock(&table->hem[i]->refcount,
67082eb481dSWeihang Li 					 &table->mutex))
67182eb481dSWeihang Li 		return;
6729a443537Soulijun 
673a519a612SChengchang Tang 	ret = hr_dev->hw->clear_hem(hr_dev, table, obj, HEM_HOP_STEP_DIRECT);
674a519a612SChengchang Tang 	if (ret)
675a519a612SChengchang Tang 		dev_warn(dev, "failed to clear HEM base address, ret = %d.\n",
676a519a612SChengchang Tang 			 ret);
6779a443537Soulijun 
6789a443537Soulijun 	hns_roce_free_hem(hr_dev, table->hem[i]);
6799a443537Soulijun 	table->hem[i] = NULL;
6809a443537Soulijun 
6819a443537Soulijun 	mutex_unlock(&table->mutex);
6829a443537Soulijun }
6839a443537Soulijun 
hns_roce_table_find(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,unsigned long obj,dma_addr_t * dma_handle)6846a93c77aSShaobo Xu void *hns_roce_table_find(struct hns_roce_dev *hr_dev,
6856a93c77aSShaobo Xu 			  struct hns_roce_hem_table *table,
6866a93c77aSShaobo Xu 			  unsigned long obj, dma_addr_t *dma_handle)
6879a443537Soulijun {
6886a93c77aSShaobo Xu 	struct hns_roce_hem_mhop mhop;
6899a443537Soulijun 	struct hns_roce_hem *hem;
6906a93c77aSShaobo Xu 	unsigned long mhop_obj = obj;
6910203b14cSoulijun 	unsigned long obj_per_chunk;
6920203b14cSoulijun 	unsigned long idx_offset;
6936a93c77aSShaobo Xu 	int offset, dma_offset;
694dc93a0d9SLang Cheng 	void *addr = NULL;
695dc93a0d9SLang Cheng 	u32 hem_idx = 0;
6966a93c77aSShaobo Xu 	int i, j;
6979a443537Soulijun 
6989a443537Soulijun 	mutex_lock(&table->mutex);
6996a93c77aSShaobo Xu 
7006a93c77aSShaobo Xu 	if (!hns_roce_check_whether_mhop(hr_dev, table->type)) {
7010203b14cSoulijun 		obj_per_chunk = table->table_chunk_size / table->obj_size;
70261b460d1SXi Wang 		hem = table->hem[obj / obj_per_chunk];
70361b460d1SXi Wang 		idx_offset = obj % obj_per_chunk;
7040203b14cSoulijun 		dma_offset = offset = idx_offset * table->obj_size;
7056a93c77aSShaobo Xu 	} else {
7064772e03dSLijun Ou 		u32 seg_size = 64; /* 8 bytes per BA and 8 BA per segment */
7074772e03dSLijun Ou 
7080e20ebf8SLang Cheng 		if (hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, &mhop))
7090e20ebf8SLang Cheng 			goto out;
7106a93c77aSShaobo Xu 		/* mtt mhop */
7116a93c77aSShaobo Xu 		i = mhop.l0_idx;
7126a93c77aSShaobo Xu 		j = mhop.l1_idx;
7136a93c77aSShaobo Xu 		if (mhop.hop_num == 2)
7142a3d923fSLijun Ou 			hem_idx = i * (mhop.bt_chunk_size / BA_BYTE_LEN) + j;
7156a93c77aSShaobo Xu 		else if (mhop.hop_num == 1 ||
7166a93c77aSShaobo Xu 			 mhop.hop_num == HNS_ROCE_HOP_NUM_0)
7176a93c77aSShaobo Xu 			hem_idx = i;
7186a93c77aSShaobo Xu 
7196a93c77aSShaobo Xu 		hem = table->hem[hem_idx];
72061b460d1SXi Wang 		dma_offset = offset = obj * seg_size % mhop.bt_chunk_size;
7216a93c77aSShaobo Xu 		if (mhop.hop_num == 2)
7226a93c77aSShaobo Xu 			dma_offset = offset = 0;
7236a93c77aSShaobo Xu 	}
7249a443537Soulijun 
7259a443537Soulijun 	if (!hem)
7269a443537Soulijun 		goto out;
7279a443537Soulijun 
728c00743cbSYunsheng Lin 	*dma_handle = hem->dma + dma_offset;
729c00743cbSYunsheng Lin 	addr = hem->buf + offset;
7309a443537Soulijun 
7319a443537Soulijun out:
7329a443537Soulijun 	mutex_unlock(&table->mutex);
733378efe79SWei Hu\(Xavier\) 	return addr;
7349a443537Soulijun }
7359a443537Soulijun 
hns_roce_init_hem_table(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table,u32 type,unsigned long obj_size,unsigned long nobj)7369a443537Soulijun int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev,
7379a443537Soulijun 			    struct hns_roce_hem_table *table, u32 type,
73829dc0635SYunsheng Lin 			    unsigned long obj_size, unsigned long nobj)
7399a443537Soulijun {
7409a443537Soulijun 	unsigned long obj_per_chunk;
7419a443537Soulijun 	unsigned long num_hem;
7429a443537Soulijun 
743a25d13cbSShaobo Xu 	if (!hns_roce_check_whether_mhop(hr_dev, type)) {
74429a1fe5dSWei Hu(Xavier) 		table->table_chunk_size = hr_dev->caps.chunk_sz;
74529a1fe5dSWei Hu(Xavier) 		obj_per_chunk = table->table_chunk_size / obj_size;
74661b460d1SXi Wang 		num_hem = DIV_ROUND_UP(nobj, obj_per_chunk);
7479a443537Soulijun 
7489a443537Soulijun 		table->hem = kcalloc(num_hem, sizeof(*table->hem), GFP_KERNEL);
7499a443537Soulijun 		if (!table->hem)
7509a443537Soulijun 			return -ENOMEM;
751a25d13cbSShaobo Xu 	} else {
752d7019c0fSLijun Ou 		struct hns_roce_hem_mhop mhop = {};
753a25d13cbSShaobo Xu 		unsigned long buf_chunk_size;
754a25d13cbSShaobo Xu 		unsigned long bt_chunk_size;
755a25d13cbSShaobo Xu 		unsigned long bt_chunk_num;
75662f3b70eSXinhao Liu 		unsigned long num_bt_l0;
757a25d13cbSShaobo Xu 		u32 hop_num;
758a25d13cbSShaobo Xu 
759d7019c0fSLijun Ou 		if (get_hem_table_config(hr_dev, &mhop, type))
760a25d13cbSShaobo Xu 			return -EINVAL;
761d7019c0fSLijun Ou 
762d7019c0fSLijun Ou 		buf_chunk_size = mhop.buf_chunk_size;
763d7019c0fSLijun Ou 		bt_chunk_size = mhop.bt_chunk_size;
764d7019c0fSLijun Ou 		num_bt_l0 = mhop.ba_l0_num;
765d7019c0fSLijun Ou 		hop_num = mhop.hop_num;
766d7019c0fSLijun Ou 
767a25d13cbSShaobo Xu 		obj_per_chunk = buf_chunk_size / obj_size;
76861b460d1SXi Wang 		num_hem = DIV_ROUND_UP(nobj, obj_per_chunk);
7692a3d923fSLijun Ou 		bt_chunk_num = bt_chunk_size / BA_BYTE_LEN;
77061b460d1SXi Wang 
771215a8c09Soulijun 		if (type >= HEM_TYPE_MTT)
7726a93c77aSShaobo Xu 			num_bt_l0 = bt_chunk_num;
773a25d13cbSShaobo Xu 
774a25d13cbSShaobo Xu 		table->hem = kcalloc(num_hem, sizeof(*table->hem),
775a25d13cbSShaobo Xu 					 GFP_KERNEL);
776a25d13cbSShaobo Xu 		if (!table->hem)
777a25d13cbSShaobo Xu 			goto err_kcalloc_hem_buf;
778a25d13cbSShaobo Xu 
779215a8c09Soulijun 		if (check_whether_bt_num_3(type, hop_num)) {
780a25d13cbSShaobo Xu 			unsigned long num_bt_l1;
781a25d13cbSShaobo Xu 
78261b460d1SXi Wang 			num_bt_l1 = DIV_ROUND_UP(num_hem, bt_chunk_num);
783a25d13cbSShaobo Xu 			table->bt_l1 = kcalloc(num_bt_l1,
784a25d13cbSShaobo Xu 					       sizeof(*table->bt_l1),
785a25d13cbSShaobo Xu 					       GFP_KERNEL);
786a25d13cbSShaobo Xu 			if (!table->bt_l1)
787a25d13cbSShaobo Xu 				goto err_kcalloc_bt_l1;
788a25d13cbSShaobo Xu 
789a25d13cbSShaobo Xu 			table->bt_l1_dma_addr = kcalloc(num_bt_l1,
790a25d13cbSShaobo Xu 						 sizeof(*table->bt_l1_dma_addr),
791a25d13cbSShaobo Xu 						 GFP_KERNEL);
792a25d13cbSShaobo Xu 
793a25d13cbSShaobo Xu 			if (!table->bt_l1_dma_addr)
794a25d13cbSShaobo Xu 				goto err_kcalloc_l1_dma;
795a25d13cbSShaobo Xu 		}
796a25d13cbSShaobo Xu 
797215a8c09Soulijun 		if (check_whether_bt_num_2(type, hop_num) ||
798215a8c09Soulijun 			check_whether_bt_num_3(type, hop_num)) {
799a25d13cbSShaobo Xu 			table->bt_l0 = kcalloc(num_bt_l0, sizeof(*table->bt_l0),
800a25d13cbSShaobo Xu 					       GFP_KERNEL);
801a25d13cbSShaobo Xu 			if (!table->bt_l0)
802a25d13cbSShaobo Xu 				goto err_kcalloc_bt_l0;
803a25d13cbSShaobo Xu 
804a25d13cbSShaobo Xu 			table->bt_l0_dma_addr = kcalloc(num_bt_l0,
805a25d13cbSShaobo Xu 						 sizeof(*table->bt_l0_dma_addr),
806a25d13cbSShaobo Xu 						 GFP_KERNEL);
807a25d13cbSShaobo Xu 			if (!table->bt_l0_dma_addr)
808a25d13cbSShaobo Xu 				goto err_kcalloc_l0_dma;
809a25d13cbSShaobo Xu 		}
810a25d13cbSShaobo Xu 	}
8119a443537Soulijun 
8129a443537Soulijun 	table->type = type;
8139a443537Soulijun 	table->num_hem = num_hem;
8149a443537Soulijun 	table->obj_size = obj_size;
8159a443537Soulijun 	mutex_init(&table->mutex);
8169a443537Soulijun 
8179a443537Soulijun 	return 0;
818a25d13cbSShaobo Xu 
819a25d13cbSShaobo Xu err_kcalloc_l0_dma:
820a25d13cbSShaobo Xu 	kfree(table->bt_l0);
821a25d13cbSShaobo Xu 	table->bt_l0 = NULL;
822a25d13cbSShaobo Xu 
823a25d13cbSShaobo Xu err_kcalloc_bt_l0:
824a25d13cbSShaobo Xu 	kfree(table->bt_l1_dma_addr);
825a25d13cbSShaobo Xu 	table->bt_l1_dma_addr = NULL;
826a25d13cbSShaobo Xu 
827a25d13cbSShaobo Xu err_kcalloc_l1_dma:
828a25d13cbSShaobo Xu 	kfree(table->bt_l1);
829a25d13cbSShaobo Xu 	table->bt_l1 = NULL;
830a25d13cbSShaobo Xu 
831a25d13cbSShaobo Xu err_kcalloc_bt_l1:
832a25d13cbSShaobo Xu 	kfree(table->hem);
833a25d13cbSShaobo Xu 	table->hem = NULL;
834a25d13cbSShaobo Xu 
835a25d13cbSShaobo Xu err_kcalloc_hem_buf:
836a25d13cbSShaobo Xu 	return -ENOMEM;
837a25d13cbSShaobo Xu }
838a25d13cbSShaobo Xu 
hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table)839281d0ccfSColin Ian King static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev,
840a25d13cbSShaobo Xu 					    struct hns_roce_hem_table *table)
841a25d13cbSShaobo Xu {
842a25d13cbSShaobo Xu 	struct hns_roce_hem_mhop mhop;
843a25d13cbSShaobo Xu 	u32 buf_chunk_size;
844a25d13cbSShaobo Xu 	u64 obj;
845dc93a0d9SLang Cheng 	int i;
846a25d13cbSShaobo Xu 
8470e20ebf8SLang Cheng 	if (hns_roce_calc_hem_mhop(hr_dev, table, NULL, &mhop))
8480e20ebf8SLang Cheng 		return;
8496a93c77aSShaobo Xu 	buf_chunk_size = table->type < HEM_TYPE_MTT ? mhop.buf_chunk_size :
8506a93c77aSShaobo Xu 					mhop.bt_chunk_size;
851a25d13cbSShaobo Xu 
852a25d13cbSShaobo Xu 	for (i = 0; i < table->num_hem; ++i) {
853a25d13cbSShaobo Xu 		obj = i * buf_chunk_size / table->obj_size;
854a25d13cbSShaobo Xu 		if (table->hem[i])
855a25d13cbSShaobo Xu 			hns_roce_table_mhop_put(hr_dev, table, obj, 0);
856a25d13cbSShaobo Xu 	}
857a25d13cbSShaobo Xu 
858a25d13cbSShaobo Xu 	kfree(table->hem);
859a25d13cbSShaobo Xu 	table->hem = NULL;
860a25d13cbSShaobo Xu 	kfree(table->bt_l1);
861a25d13cbSShaobo Xu 	table->bt_l1 = NULL;
862a25d13cbSShaobo Xu 	kfree(table->bt_l1_dma_addr);
863a25d13cbSShaobo Xu 	table->bt_l1_dma_addr = NULL;
864a25d13cbSShaobo Xu 	kfree(table->bt_l0);
865a25d13cbSShaobo Xu 	table->bt_l0 = NULL;
866a25d13cbSShaobo Xu 	kfree(table->bt_l0_dma_addr);
867a25d13cbSShaobo Xu 	table->bt_l0_dma_addr = NULL;
8689a443537Soulijun }
8699a443537Soulijun 
hns_roce_cleanup_hem_table(struct hns_roce_dev * hr_dev,struct hns_roce_hem_table * table)8709a443537Soulijun void hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev,
8719a443537Soulijun 				struct hns_roce_hem_table *table)
8729a443537Soulijun {
87313ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
8749a443537Soulijun 	unsigned long i;
875a519a612SChengchang Tang 	int obj;
876a519a612SChengchang Tang 	int ret;
8779a443537Soulijun 
878a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
879a25d13cbSShaobo Xu 		hns_roce_cleanup_mhop_hem_table(hr_dev, table);
8809a84848dSwenglianfa 		mutex_destroy(&table->mutex);
881a25d13cbSShaobo Xu 		return;
882a25d13cbSShaobo Xu 	}
883a25d13cbSShaobo Xu 
8849a443537Soulijun 	for (i = 0; i < table->num_hem; ++i)
8859a443537Soulijun 		if (table->hem[i]) {
886a519a612SChengchang Tang 			obj = i * table->table_chunk_size / table->obj_size;
887a519a612SChengchang Tang 			ret = hr_dev->hw->clear_hem(hr_dev, table, obj, 0);
888a519a612SChengchang Tang 			if (ret)
889a519a612SChengchang Tang 				dev_err(dev, "clear HEM base address failed, ret = %d.\n",
890a519a612SChengchang Tang 					ret);
8919a443537Soulijun 
8929a443537Soulijun 			hns_roce_free_hem(hr_dev, table->hem[i]);
8939a443537Soulijun 		}
8949a443537Soulijun 
8959a84848dSwenglianfa 	mutex_destroy(&table->mutex);
8969a443537Soulijun 	kfree(table->hem);
8979a443537Soulijun }
8989a443537Soulijun 
hns_roce_cleanup_hem(struct hns_roce_dev * hr_dev)8999a443537Soulijun void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev)
9009a443537Soulijun {
9014ddeacf6SWenpeng Liang 	if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_SRQ)
9025c1f167aSLijun Ou 		hns_roce_cleanup_hem_table(hr_dev,
9035c1f167aSLijun Ou 					   &hr_dev->srq_table.table);
9049a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table);
9050e40dc2fSYangyang Li 	if (hr_dev->caps.qpc_timer_entry_sz)
9060e40dc2fSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
9070e40dc2fSYangyang Li 					   &hr_dev->qpc_timer_table);
9080e40dc2fSYangyang Li 	if (hr_dev->caps.cqc_timer_entry_sz)
9090e40dc2fSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
9100e40dc2fSYangyang Li 					   &hr_dev->cqc_timer_table);
9114ddeacf6SWenpeng Liang 	if (hr_dev->caps.flags & HNS_ROCE_CAP_FLAG_QP_FLOW_CTRL)
9126a157f7dSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
9136a157f7dSYangyang Li 					   &hr_dev->qp_table.sccc_table);
914e92f2c18Soulijun 	if (hr_dev->caps.trrl_entry_sz)
915e92f2c18Soulijun 		hns_roce_cleanup_hem_table(hr_dev,
916e92f2c18Soulijun 					   &hr_dev->qp_table.trrl_table);
917d6d91e46SWeihang Li 
918d6d91e46SWeihang Li 	if (hr_dev->caps.gmv_entry_sz)
919d6d91e46SWeihang Li 		hns_roce_cleanup_hem_table(hr_dev, &hr_dev->gmv_table);
920d6d91e46SWeihang Li 
921ae25db00Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.irrl_table);
9229a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.qp_table);
9239a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->mr_table.mtpt_table);
9249a443537Soulijun }
92538389eaaSLijun Ou 
9261f704d8cSXi Wang struct hns_roce_hem_item {
92738389eaaSLijun Ou 	struct list_head list; /* link all hems in the same bt level */
92838389eaaSLijun Ou 	struct list_head sibling; /* link all hems in last hop for mtt */
92938389eaaSLijun Ou 	void *addr;
93038389eaaSLijun Ou 	dma_addr_t dma_addr;
93138389eaaSLijun Ou 	size_t count; /* max ba numbers */
93238389eaaSLijun Ou 	int start; /* start buf offset in this hem */
93338389eaaSLijun Ou 	int end; /* end buf offset in this hem */
93438389eaaSLijun Ou };
93538389eaaSLijun Ou 
9361f704d8cSXi Wang /* All HEM items are linked in a tree structure */
9371f704d8cSXi Wang struct hns_roce_hem_head {
9381f704d8cSXi Wang 	struct list_head branch[HNS_ROCE_MAX_BT_REGION];
9391f704d8cSXi Wang 	struct list_head root;
9401f704d8cSXi Wang 	struct list_head leaf;
9411f704d8cSXi Wang };
9421f704d8cSXi Wang 
9431f704d8cSXi Wang static struct hns_roce_hem_item *
hem_list_alloc_item(struct hns_roce_dev * hr_dev,int start,int end,int count,bool exist_bt)9441f704d8cSXi Wang hem_list_alloc_item(struct hns_roce_dev *hr_dev, int start, int end, int count,
945be1eeb66SYunsheng Lin 		    bool exist_bt)
94638389eaaSLijun Ou {
9471f704d8cSXi Wang 	struct hns_roce_hem_item *hem;
94838389eaaSLijun Ou 
94938389eaaSLijun Ou 	hem = kzalloc(sizeof(*hem), GFP_KERNEL);
95038389eaaSLijun Ou 	if (!hem)
95138389eaaSLijun Ou 		return NULL;
95238389eaaSLijun Ou 
95338389eaaSLijun Ou 	if (exist_bt) {
9549ea9a53eSXi Wang 		hem->addr = dma_alloc_coherent(hr_dev->dev, count * BA_BYTE_LEN,
95538389eaaSLijun Ou 					       &hem->dma_addr, GFP_KERNEL);
95638389eaaSLijun Ou 		if (!hem->addr) {
95738389eaaSLijun Ou 			kfree(hem);
95838389eaaSLijun Ou 			return NULL;
95938389eaaSLijun Ou 		}
96038389eaaSLijun Ou 	}
96138389eaaSLijun Ou 
96238389eaaSLijun Ou 	hem->count = count;
96338389eaaSLijun Ou 	hem->start = start;
96438389eaaSLijun Ou 	hem->end = end;
96538389eaaSLijun Ou 	INIT_LIST_HEAD(&hem->list);
96638389eaaSLijun Ou 	INIT_LIST_HEAD(&hem->sibling);
96738389eaaSLijun Ou 
96838389eaaSLijun Ou 	return hem;
96938389eaaSLijun Ou }
97038389eaaSLijun Ou 
hem_list_free_item(struct hns_roce_dev * hr_dev,struct hns_roce_hem_item * hem,bool exist_bt)97138389eaaSLijun Ou static void hem_list_free_item(struct hns_roce_dev *hr_dev,
9721f704d8cSXi Wang 			       struct hns_roce_hem_item *hem, bool exist_bt)
97338389eaaSLijun Ou {
97438389eaaSLijun Ou 	if (exist_bt)
97538389eaaSLijun Ou 		dma_free_coherent(hr_dev->dev, hem->count * BA_BYTE_LEN,
97638389eaaSLijun Ou 				  hem->addr, hem->dma_addr);
97738389eaaSLijun Ou 	kfree(hem);
97838389eaaSLijun Ou }
97938389eaaSLijun Ou 
hem_list_free_all(struct hns_roce_dev * hr_dev,struct list_head * head,bool exist_bt)98038389eaaSLijun Ou static void hem_list_free_all(struct hns_roce_dev *hr_dev,
98138389eaaSLijun Ou 			      struct list_head *head, bool exist_bt)
98238389eaaSLijun Ou {
9831f704d8cSXi Wang 	struct hns_roce_hem_item *hem, *temp_hem;
98438389eaaSLijun Ou 
98538389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, head, list) {
98638389eaaSLijun Ou 		list_del(&hem->list);
98738389eaaSLijun Ou 		hem_list_free_item(hr_dev, hem, exist_bt);
98838389eaaSLijun Ou 	}
98938389eaaSLijun Ou }
99038389eaaSLijun Ou 
hem_list_link_bt(void * base_addr,u64 table_addr)991f4caa864SChengchang Tang static void hem_list_link_bt(void *base_addr, u64 table_addr)
99238389eaaSLijun Ou {
99338389eaaSLijun Ou 	*(u64 *)(base_addr) = table_addr;
99438389eaaSLijun Ou }
99538389eaaSLijun Ou 
99638389eaaSLijun Ou /* assign L0 table address to hem from root bt */
hem_list_assign_bt(struct hns_roce_hem_item * hem,void * cpu_addr,u64 phy_addr)997f4caa864SChengchang Tang static void hem_list_assign_bt(struct hns_roce_hem_item *hem, void *cpu_addr,
99838389eaaSLijun Ou 			       u64 phy_addr)
99938389eaaSLijun Ou {
100038389eaaSLijun Ou 	hem->addr = cpu_addr;
100138389eaaSLijun Ou 	hem->dma_addr = (dma_addr_t)phy_addr;
100238389eaaSLijun Ou }
100338389eaaSLijun Ou 
hem_list_page_is_in_range(struct hns_roce_hem_item * hem,int offset)10041f704d8cSXi Wang static inline bool hem_list_page_is_in_range(struct hns_roce_hem_item *hem,
100538389eaaSLijun Ou 					     int offset)
100638389eaaSLijun Ou {
100738389eaaSLijun Ou 	return (hem->start <= offset && offset <= hem->end);
100838389eaaSLijun Ou }
100938389eaaSLijun Ou 
hem_list_search_item(struct list_head * ba_list,int page_offset)10101f704d8cSXi Wang static struct hns_roce_hem_item *hem_list_search_item(struct list_head *ba_list,
101138389eaaSLijun Ou 						      int page_offset)
101238389eaaSLijun Ou {
10131f704d8cSXi Wang 	struct hns_roce_hem_item *hem, *temp_hem;
10141f704d8cSXi Wang 	struct hns_roce_hem_item *found = NULL;
101538389eaaSLijun Ou 
101638389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, ba_list, list) {
101738389eaaSLijun Ou 		if (hem_list_page_is_in_range(hem, page_offset)) {
101838389eaaSLijun Ou 			found = hem;
101938389eaaSLijun Ou 			break;
102038389eaaSLijun Ou 		}
102138389eaaSLijun Ou 	}
102238389eaaSLijun Ou 
102338389eaaSLijun Ou 	return found;
102438389eaaSLijun Ou }
102538389eaaSLijun Ou 
hem_list_is_bottom_bt(int hopnum,int bt_level)102638389eaaSLijun Ou static bool hem_list_is_bottom_bt(int hopnum, int bt_level)
102738389eaaSLijun Ou {
102838389eaaSLijun Ou 	/*
102938389eaaSLijun Ou 	 * hopnum    base address table levels
103038389eaaSLijun Ou 	 * 0		L0(buf)
103138389eaaSLijun Ou 	 * 1		L0 -> buf
103238389eaaSLijun Ou 	 * 2		L0 -> L1 -> buf
103338389eaaSLijun Ou 	 * 3		L0 -> L1 -> L2 -> buf
103438389eaaSLijun Ou 	 */
103538389eaaSLijun Ou 	return bt_level >= (hopnum ? hopnum - 1 : hopnum);
103638389eaaSLijun Ou }
103738389eaaSLijun Ou 
1038bf194997SLeon Romanovsky /*
103938389eaaSLijun Ou  * calc base address entries num
104038389eaaSLijun Ou  * @hopnum: num of mutihop addressing
104138389eaaSLijun Ou  * @bt_level: base address table level
104238389eaaSLijun Ou  * @unit: ba entries per bt page
104338389eaaSLijun Ou  */
hem_list_calc_ba_range(int hopnum,int bt_level,int unit)1044d586628bSwenglianfa static u64 hem_list_calc_ba_range(int hopnum, int bt_level, int unit)
104538389eaaSLijun Ou {
1046d586628bSwenglianfa 	u64 step;
104738389eaaSLijun Ou 	int max;
104838389eaaSLijun Ou 	int i;
104938389eaaSLijun Ou 
105038389eaaSLijun Ou 	if (hopnum <= bt_level)
105138389eaaSLijun Ou 		return 0;
105238389eaaSLijun Ou 	/*
105338389eaaSLijun Ou 	 * hopnum  bt_level   range
105438389eaaSLijun Ou 	 * 1	      0       unit
105538389eaaSLijun Ou 	 * ------------
105638389eaaSLijun Ou 	 * 2	      0       unit * unit
105738389eaaSLijun Ou 	 * 2	      1       unit
105838389eaaSLijun Ou 	 * ------------
105938389eaaSLijun Ou 	 * 3	      0       unit * unit * unit
106038389eaaSLijun Ou 	 * 3	      1       unit * unit
106138389eaaSLijun Ou 	 * 3	      2       unit
106238389eaaSLijun Ou 	 */
106338389eaaSLijun Ou 	step = 1;
106438389eaaSLijun Ou 	max = hopnum - bt_level;
106538389eaaSLijun Ou 	for (i = 0; i < max; i++)
106638389eaaSLijun Ou 		step = step * unit;
106738389eaaSLijun Ou 
106838389eaaSLijun Ou 	return step;
106938389eaaSLijun Ou }
107038389eaaSLijun Ou 
1071bf194997SLeon Romanovsky /*
107238389eaaSLijun Ou  * calc the root ba entries which could cover all regions
107338389eaaSLijun Ou  * @regions: buf region array
107438389eaaSLijun Ou  * @region_cnt: array size of @regions
107538389eaaSLijun Ou  * @unit: ba entries per bt page
107638389eaaSLijun Ou  */
hns_roce_hem_list_calc_root_ba(const struct hns_roce_buf_region * regions,int region_cnt,int unit)107738389eaaSLijun Ou int hns_roce_hem_list_calc_root_ba(const struct hns_roce_buf_region *regions,
107838389eaaSLijun Ou 				   int region_cnt, int unit)
107938389eaaSLijun Ou {
108038389eaaSLijun Ou 	struct hns_roce_buf_region *r;
108138389eaaSLijun Ou 	int total = 0;
1082d586628bSwenglianfa 	u64 step;
108338389eaaSLijun Ou 	int i;
108438389eaaSLijun Ou 
108538389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++) {
108638389eaaSLijun Ou 		r = (struct hns_roce_buf_region *)&regions[i];
108738389eaaSLijun Ou 		if (r->hopnum > 1) {
108838389eaaSLijun Ou 			step = hem_list_calc_ba_range(r->hopnum, 1, unit);
108938389eaaSLijun Ou 			if (step > 0)
109038389eaaSLijun Ou 				total += (r->count + step - 1) / step;
109138389eaaSLijun Ou 		} else {
109238389eaaSLijun Ou 			total += r->count;
109338389eaaSLijun Ou 		}
109438389eaaSLijun Ou 	}
109538389eaaSLijun Ou 
109638389eaaSLijun Ou 	return total;
109738389eaaSLijun Ou }
109838389eaaSLijun Ou 
hem_list_alloc_mid_bt(struct hns_roce_dev * hr_dev,const struct hns_roce_buf_region * r,int unit,int offset,struct list_head * mid_bt,struct list_head * btm_bt)109938389eaaSLijun Ou static int hem_list_alloc_mid_bt(struct hns_roce_dev *hr_dev,
110038389eaaSLijun Ou 				 const struct hns_roce_buf_region *r, int unit,
110138389eaaSLijun Ou 				 int offset, struct list_head *mid_bt,
110238389eaaSLijun Ou 				 struct list_head *btm_bt)
110338389eaaSLijun Ou {
11041f704d8cSXi Wang 	struct hns_roce_hem_item *hem_ptrs[HNS_ROCE_MAX_BT_LEVEL] = { NULL };
110538389eaaSLijun Ou 	struct list_head temp_list[HNS_ROCE_MAX_BT_LEVEL];
11061f704d8cSXi Wang 	struct hns_roce_hem_item *cur, *pre;
110738389eaaSLijun Ou 	const int hopnum = r->hopnum;
110838389eaaSLijun Ou 	int start_aligned;
110938389eaaSLijun Ou 	int distance;
111038389eaaSLijun Ou 	int ret = 0;
111138389eaaSLijun Ou 	int max_ofs;
111238389eaaSLijun Ou 	int level;
1113d586628bSwenglianfa 	u64 step;
111438389eaaSLijun Ou 	int end;
111538389eaaSLijun Ou 
111638389eaaSLijun Ou 	if (hopnum <= 1)
111738389eaaSLijun Ou 		return 0;
111838389eaaSLijun Ou 
111938389eaaSLijun Ou 	if (hopnum > HNS_ROCE_MAX_BT_LEVEL) {
112038389eaaSLijun Ou 		dev_err(hr_dev->dev, "invalid hopnum %d!\n", hopnum);
112138389eaaSLijun Ou 		return -EINVAL;
112238389eaaSLijun Ou 	}
112338389eaaSLijun Ou 
112438389eaaSLijun Ou 	if (offset < r->offset) {
112561918e9bSYixing Liu 		dev_err(hr_dev->dev, "invalid offset %d, min %u!\n",
112638389eaaSLijun Ou 			offset, r->offset);
112738389eaaSLijun Ou 		return -EINVAL;
112838389eaaSLijun Ou 	}
112938389eaaSLijun Ou 
113038389eaaSLijun Ou 	distance = offset - r->offset;
113138389eaaSLijun Ou 	max_ofs = r->offset + r->count - 1;
113238389eaaSLijun Ou 	for (level = 0; level < hopnum; level++)
113338389eaaSLijun Ou 		INIT_LIST_HEAD(&temp_list[level]);
113438389eaaSLijun Ou 
113538389eaaSLijun Ou 	/* config L1 bt to last bt and link them to corresponding parent */
113638389eaaSLijun Ou 	for (level = 1; level < hopnum; level++) {
1137*fe51f625SJunxian Huang 		if (!hem_list_is_bottom_bt(hopnum, level)) {
113838389eaaSLijun Ou 			cur = hem_list_search_item(&mid_bt[level], offset);
113938389eaaSLijun Ou 			if (cur) {
114038389eaaSLijun Ou 				hem_ptrs[level] = cur;
114138389eaaSLijun Ou 				continue;
114238389eaaSLijun Ou 			}
1143*fe51f625SJunxian Huang 		}
114438389eaaSLijun Ou 
114538389eaaSLijun Ou 		step = hem_list_calc_ba_range(hopnum, level, unit);
114638389eaaSLijun Ou 		if (step < 1) {
114738389eaaSLijun Ou 			ret = -EINVAL;
114838389eaaSLijun Ou 			goto err_exit;
114938389eaaSLijun Ou 		}
115038389eaaSLijun Ou 
115138389eaaSLijun Ou 		start_aligned = (distance / step) * step + r->offset;
1152d586628bSwenglianfa 		end = min_t(u64, start_aligned + step - 1, max_ofs);
115338389eaaSLijun Ou 		cur = hem_list_alloc_item(hr_dev, start_aligned, end, unit,
1154be1eeb66SYunsheng Lin 					  true);
115538389eaaSLijun Ou 		if (!cur) {
115638389eaaSLijun Ou 			ret = -ENOMEM;
115738389eaaSLijun Ou 			goto err_exit;
115838389eaaSLijun Ou 		}
115938389eaaSLijun Ou 		hem_ptrs[level] = cur;
116038389eaaSLijun Ou 		list_add(&cur->list, &temp_list[level]);
116138389eaaSLijun Ou 		if (hem_list_is_bottom_bt(hopnum, level))
116238389eaaSLijun Ou 			list_add(&cur->sibling, &temp_list[0]);
116338389eaaSLijun Ou 
116438389eaaSLijun Ou 		/* link bt to parent bt */
116538389eaaSLijun Ou 		if (level > 1) {
116638389eaaSLijun Ou 			pre = hem_ptrs[level - 1];
116738389eaaSLijun Ou 			step = (cur->start - pre->start) / step * BA_BYTE_LEN;
1168f4caa864SChengchang Tang 			hem_list_link_bt(pre->addr + step, cur->dma_addr);
116938389eaaSLijun Ou 		}
117038389eaaSLijun Ou 	}
117138389eaaSLijun Ou 
117238389eaaSLijun Ou 	list_splice(&temp_list[0], btm_bt);
117338389eaaSLijun Ou 	for (level = 1; level < hopnum; level++)
117438389eaaSLijun Ou 		list_splice(&temp_list[level], &mid_bt[level]);
117538389eaaSLijun Ou 
117638389eaaSLijun Ou 	return 0;
117738389eaaSLijun Ou 
117838389eaaSLijun Ou err_exit:
117938389eaaSLijun Ou 	for (level = 1; level < hopnum; level++)
118038389eaaSLijun Ou 		hem_list_free_all(hr_dev, &temp_list[level], true);
118138389eaaSLijun Ou 
118238389eaaSLijun Ou 	return ret;
118338389eaaSLijun Ou }
118438389eaaSLijun Ou 
11851f704d8cSXi Wang static struct hns_roce_hem_item *
alloc_root_hem(struct hns_roce_dev * hr_dev,int unit,int * max_ba_num,const struct hns_roce_buf_region * regions,int region_cnt)11861f704d8cSXi Wang alloc_root_hem(struct hns_roce_dev *hr_dev, int unit, int *max_ba_num,
11871f704d8cSXi Wang 	       const struct hns_roce_buf_region *regions, int region_cnt)
118838389eaaSLijun Ou {
118938389eaaSLijun Ou 	const struct hns_roce_buf_region *r;
11901f704d8cSXi Wang 	struct hns_roce_hem_item *hem;
119111334014SXi Wang 	int ba_num;
119238389eaaSLijun Ou 	int offset;
119338389eaaSLijun Ou 
119411334014SXi Wang 	ba_num = hns_roce_hem_list_calc_root_ba(regions, region_cnt, unit);
119511334014SXi Wang 	if (ba_num < 1)
11961f704d8cSXi Wang 		return ERR_PTR(-ENOMEM);
119711334014SXi Wang 
11989ea9a53eSXi Wang 	if (ba_num > unit)
11991f704d8cSXi Wang 		return ERR_PTR(-ENOBUFS);
12009ea9a53eSXi Wang 
12011f704d8cSXi Wang 	offset = regions[0].offset;
120238389eaaSLijun Ou 	/* indicate to last region */
120338389eaaSLijun Ou 	r = &regions[region_cnt - 1];
12041f704d8cSXi Wang 	hem = hem_list_alloc_item(hr_dev, offset, r->offset + r->count - 1,
1205be1eeb66SYunsheng Lin 				  ba_num, true);
12061f704d8cSXi Wang 	if (!hem)
12071f704d8cSXi Wang 		return ERR_PTR(-ENOMEM);
12081f704d8cSXi Wang 
12091f704d8cSXi Wang 	*max_ba_num = ba_num;
12101f704d8cSXi Wang 
12111f704d8cSXi Wang 	return hem;
12121f704d8cSXi Wang }
12131f704d8cSXi Wang 
alloc_fake_root_bt(struct hns_roce_dev * hr_dev,void * cpu_base,u64 phy_base,const struct hns_roce_buf_region * r,struct list_head * branch_head,struct list_head * leaf_head)12141f704d8cSXi Wang static int alloc_fake_root_bt(struct hns_roce_dev *hr_dev, void *cpu_base,
12151f704d8cSXi Wang 			      u64 phy_base, const struct hns_roce_buf_region *r,
12161f704d8cSXi Wang 			      struct list_head *branch_head,
12171f704d8cSXi Wang 			      struct list_head *leaf_head)
12181f704d8cSXi Wang {
12191f704d8cSXi Wang 	struct hns_roce_hem_item *hem;
12201f704d8cSXi Wang 
12211f704d8cSXi Wang 	hem = hem_list_alloc_item(hr_dev, r->offset, r->offset + r->count - 1,
1222be1eeb66SYunsheng Lin 				  r->count, false);
12231f704d8cSXi Wang 	if (!hem)
12241f704d8cSXi Wang 		return -ENOMEM;
12251f704d8cSXi Wang 
1226f4caa864SChengchang Tang 	hem_list_assign_bt(hem, cpu_base, phy_base);
12271f704d8cSXi Wang 	list_add(&hem->list, branch_head);
12281f704d8cSXi Wang 	list_add(&hem->sibling, leaf_head);
12291f704d8cSXi Wang 
12301f704d8cSXi Wang 	return r->count;
12311f704d8cSXi Wang }
12321f704d8cSXi Wang 
setup_middle_bt(struct hns_roce_dev * hr_dev,void * cpu_base,int unit,const struct hns_roce_buf_region * r,const struct list_head * branch_head)12331f704d8cSXi Wang static int setup_middle_bt(struct hns_roce_dev *hr_dev, void *cpu_base,
12341f704d8cSXi Wang 			   int unit, const struct hns_roce_buf_region *r,
12351f704d8cSXi Wang 			   const struct list_head *branch_head)
12361f704d8cSXi Wang {
12371f704d8cSXi Wang 	struct hns_roce_hem_item *hem, *temp_hem;
12381f704d8cSXi Wang 	int total = 0;
12391f704d8cSXi Wang 	int offset;
1240d586628bSwenglianfa 	u64 step;
12411f704d8cSXi Wang 
12421f704d8cSXi Wang 	step = hem_list_calc_ba_range(r->hopnum, 1, unit);
12431f704d8cSXi Wang 	if (step < 1)
12441f704d8cSXi Wang 		return -EINVAL;
12451f704d8cSXi Wang 
12461f704d8cSXi Wang 	/* if exist mid bt, link L1 to L0 */
12471f704d8cSXi Wang 	list_for_each_entry_safe(hem, temp_hem, branch_head, list) {
12481f704d8cSXi Wang 		offset = (hem->start - r->offset) / step * BA_BYTE_LEN;
1249f4caa864SChengchang Tang 		hem_list_link_bt(cpu_base + offset, hem->dma_addr);
12501f704d8cSXi Wang 		total++;
12511f704d8cSXi Wang 	}
12521f704d8cSXi Wang 
12531f704d8cSXi Wang 	return total;
12541f704d8cSXi Wang }
12551f704d8cSXi Wang 
12561f704d8cSXi Wang static int
setup_root_hem(struct hns_roce_dev * hr_dev,struct hns_roce_hem_list * hem_list,int unit,int max_ba_num,struct hns_roce_hem_head * head,const struct hns_roce_buf_region * regions,int region_cnt)12571f704d8cSXi Wang setup_root_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem_list *hem_list,
12581f704d8cSXi Wang 	       int unit, int max_ba_num, struct hns_roce_hem_head *head,
12591f704d8cSXi Wang 	       const struct hns_roce_buf_region *regions, int region_cnt)
12601f704d8cSXi Wang {
12611f704d8cSXi Wang 	const struct hns_roce_buf_region *r;
12621f704d8cSXi Wang 	struct hns_roce_hem_item *root_hem;
12631f704d8cSXi Wang 	void *cpu_base;
12641f704d8cSXi Wang 	u64 phy_base;
12651f704d8cSXi Wang 	int i, total;
12661f704d8cSXi Wang 	int ret;
12671f704d8cSXi Wang 
12681f704d8cSXi Wang 	root_hem = list_first_entry(&head->root,
12691f704d8cSXi Wang 				    struct hns_roce_hem_item, list);
127038389eaaSLijun Ou 	if (!root_hem)
127138389eaaSLijun Ou 		return -ENOMEM;
127238389eaaSLijun Ou 
127338389eaaSLijun Ou 	total = 0;
12741f704d8cSXi Wang 	for (i = 0; i < region_cnt && total < max_ba_num; i++) {
127538389eaaSLijun Ou 		r = &regions[i];
127638389eaaSLijun Ou 		if (!r->count)
127738389eaaSLijun Ou 			continue;
127838389eaaSLijun Ou 
127938389eaaSLijun Ou 		/* all regions's mid[x][0] shared the root_bt's trunk */
128038389eaaSLijun Ou 		cpu_base = root_hem->addr + total * BA_BYTE_LEN;
128138389eaaSLijun Ou 		phy_base = root_hem->dma_addr + total * BA_BYTE_LEN;
128238389eaaSLijun Ou 
128338389eaaSLijun Ou 		/* if hopnum is 0 or 1, cut a new fake hem from the root bt
128438389eaaSLijun Ou 		 * which's address share to all regions.
128538389eaaSLijun Ou 		 */
12861f704d8cSXi Wang 		if (hem_list_is_bottom_bt(r->hopnum, 0))
12871f704d8cSXi Wang 			ret = alloc_fake_root_bt(hr_dev, cpu_base, phy_base, r,
12881f704d8cSXi Wang 						 &head->branch[i], &head->leaf);
12891f704d8cSXi Wang 		else
12901f704d8cSXi Wang 			ret = setup_middle_bt(hr_dev, cpu_base, unit, r,
12911f704d8cSXi Wang 					      &hem_list->mid_bt[i][1]);
12921f704d8cSXi Wang 
12931f704d8cSXi Wang 		if (ret < 0)
12941f704d8cSXi Wang 			return ret;
12951f704d8cSXi Wang 
12961f704d8cSXi Wang 		total += ret;
129738389eaaSLijun Ou 	}
129838389eaaSLijun Ou 
12991f704d8cSXi Wang 	list_splice(&head->leaf, &hem_list->btm_bt);
13001f704d8cSXi Wang 	list_splice(&head->root, &hem_list->root_bt);
130138389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++)
13021f704d8cSXi Wang 		list_splice(&head->branch[i], &hem_list->mid_bt[i][0]);
130338389eaaSLijun Ou 
130438389eaaSLijun Ou 	return 0;
13051f704d8cSXi Wang }
130638389eaaSLijun Ou 
hem_list_alloc_root_bt(struct hns_roce_dev * hr_dev,struct hns_roce_hem_list * hem_list,int unit,const struct hns_roce_buf_region * regions,int region_cnt)13071f704d8cSXi Wang static int hem_list_alloc_root_bt(struct hns_roce_dev *hr_dev,
13081f704d8cSXi Wang 				  struct hns_roce_hem_list *hem_list, int unit,
13091f704d8cSXi Wang 				  const struct hns_roce_buf_region *regions,
13101f704d8cSXi Wang 				  int region_cnt)
13111f704d8cSXi Wang {
13121f704d8cSXi Wang 	struct hns_roce_hem_item *root_hem;
13131f704d8cSXi Wang 	struct hns_roce_hem_head head;
13141f704d8cSXi Wang 	int max_ba_num;
13151f704d8cSXi Wang 	int ret;
13161f704d8cSXi Wang 	int i;
13171f704d8cSXi Wang 
13181f704d8cSXi Wang 	root_hem = hem_list_search_item(&hem_list->root_bt, regions[0].offset);
13191f704d8cSXi Wang 	if (root_hem)
13201f704d8cSXi Wang 		return 0;
13211f704d8cSXi Wang 
13221f704d8cSXi Wang 	max_ba_num = 0;
13231f704d8cSXi Wang 	root_hem = alloc_root_hem(hr_dev, unit, &max_ba_num, regions,
13241f704d8cSXi Wang 				  region_cnt);
13251f704d8cSXi Wang 	if (IS_ERR(root_hem))
13261f704d8cSXi Wang 		return PTR_ERR(root_hem);
13271f704d8cSXi Wang 
13281f704d8cSXi Wang 	/* List head for storing all allocated HEM items */
13291f704d8cSXi Wang 	INIT_LIST_HEAD(&head.root);
13301f704d8cSXi Wang 	INIT_LIST_HEAD(&head.leaf);
133138389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++)
13321f704d8cSXi Wang 		INIT_LIST_HEAD(&head.branch[i]);
133338389eaaSLijun Ou 
13341f704d8cSXi Wang 	hem_list->root_ba = root_hem->dma_addr;
13351f704d8cSXi Wang 	list_add(&root_hem->list, &head.root);
13361f704d8cSXi Wang 	ret = setup_root_hem(hr_dev, hem_list, unit, max_ba_num, &head, regions,
13371f704d8cSXi Wang 			     region_cnt);
13381f704d8cSXi Wang 	if (ret) {
13391f704d8cSXi Wang 		for (i = 0; i < region_cnt; i++)
13401f704d8cSXi Wang 			hem_list_free_all(hr_dev, &head.branch[i], false);
13411f704d8cSXi Wang 
13421f704d8cSXi Wang 		hem_list_free_all(hr_dev, &head.root, true);
13431f704d8cSXi Wang 	}
134438389eaaSLijun Ou 
134538389eaaSLijun Ou 	return ret;
134638389eaaSLijun Ou }
134738389eaaSLijun Ou 
134838389eaaSLijun Ou /* construct the base address table and link them by address hop config */
hns_roce_hem_list_request(struct hns_roce_dev * hr_dev,struct hns_roce_hem_list * hem_list,const struct hns_roce_buf_region * regions,int region_cnt,unsigned int bt_pg_shift)134938389eaaSLijun Ou int hns_roce_hem_list_request(struct hns_roce_dev *hr_dev,
135038389eaaSLijun Ou 			      struct hns_roce_hem_list *hem_list,
135138389eaaSLijun Ou 			      const struct hns_roce_buf_region *regions,
135282d07a4eSWeihang Li 			      int region_cnt, unsigned int bt_pg_shift)
135338389eaaSLijun Ou {
135438389eaaSLijun Ou 	const struct hns_roce_buf_region *r;
135538389eaaSLijun Ou 	int ofs, end;
135638389eaaSLijun Ou 	int unit;
1357dc93a0d9SLang Cheng 	int ret;
135838389eaaSLijun Ou 	int i;
135938389eaaSLijun Ou 
136038389eaaSLijun Ou 	if (region_cnt > HNS_ROCE_MAX_BT_REGION) {
136138389eaaSLijun Ou 		dev_err(hr_dev->dev, "invalid region region_cnt %d!\n",
136238389eaaSLijun Ou 			region_cnt);
136338389eaaSLijun Ou 		return -EINVAL;
136438389eaaSLijun Ou 	}
136538389eaaSLijun Ou 
13663c873161SXi Wang 	unit = (1 << bt_pg_shift) / BA_BYTE_LEN;
136738389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++) {
136838389eaaSLijun Ou 		r = &regions[i];
136938389eaaSLijun Ou 		if (!r->count)
137038389eaaSLijun Ou 			continue;
137138389eaaSLijun Ou 
137238389eaaSLijun Ou 		end = r->offset + r->count;
137338389eaaSLijun Ou 		for (ofs = r->offset; ofs < end; ofs += unit) {
137438389eaaSLijun Ou 			ret = hem_list_alloc_mid_bt(hr_dev, r, unit, ofs,
137538389eaaSLijun Ou 						    hem_list->mid_bt[i],
137638389eaaSLijun Ou 						    &hem_list->btm_bt);
137738389eaaSLijun Ou 			if (ret) {
137838389eaaSLijun Ou 				dev_err(hr_dev->dev,
137938389eaaSLijun Ou 					"alloc hem trunk fail ret = %d!\n", ret);
138038389eaaSLijun Ou 				goto err_alloc;
138138389eaaSLijun Ou 			}
138238389eaaSLijun Ou 		}
138338389eaaSLijun Ou 	}
138438389eaaSLijun Ou 
138538389eaaSLijun Ou 	ret = hem_list_alloc_root_bt(hr_dev, hem_list, unit, regions,
138638389eaaSLijun Ou 				     region_cnt);
138738389eaaSLijun Ou 	if (ret)
138838389eaaSLijun Ou 		dev_err(hr_dev->dev, "alloc hem root fail ret = %d!\n", ret);
138938389eaaSLijun Ou 	else
139038389eaaSLijun Ou 		return 0;
139138389eaaSLijun Ou 
139238389eaaSLijun Ou err_alloc:
139338389eaaSLijun Ou 	hns_roce_hem_list_release(hr_dev, hem_list);
139438389eaaSLijun Ou 
139538389eaaSLijun Ou 	return ret;
139638389eaaSLijun Ou }
139738389eaaSLijun Ou 
hns_roce_hem_list_release(struct hns_roce_dev * hr_dev,struct hns_roce_hem_list * hem_list)139838389eaaSLijun Ou void hns_roce_hem_list_release(struct hns_roce_dev *hr_dev,
139938389eaaSLijun Ou 			       struct hns_roce_hem_list *hem_list)
140038389eaaSLijun Ou {
140138389eaaSLijun Ou 	int i, j;
140238389eaaSLijun Ou 
140338389eaaSLijun Ou 	for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++)
140438389eaaSLijun Ou 		for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++)
140538389eaaSLijun Ou 			hem_list_free_all(hr_dev, &hem_list->mid_bt[i][j],
140638389eaaSLijun Ou 					  j != 0);
140738389eaaSLijun Ou 
140838389eaaSLijun Ou 	hem_list_free_all(hr_dev, &hem_list->root_bt, true);
140938389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->btm_bt);
141038389eaaSLijun Ou 	hem_list->root_ba = 0;
141138389eaaSLijun Ou }
141238389eaaSLijun Ou 
hns_roce_hem_list_init(struct hns_roce_hem_list * hem_list)14133c873161SXi Wang void hns_roce_hem_list_init(struct hns_roce_hem_list *hem_list)
141438389eaaSLijun Ou {
141538389eaaSLijun Ou 	int i, j;
141638389eaaSLijun Ou 
141738389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->root_bt);
141838389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->btm_bt);
141938389eaaSLijun Ou 	for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++)
142038389eaaSLijun Ou 		for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++)
142138389eaaSLijun Ou 			INIT_LIST_HEAD(&hem_list->mid_bt[i][j]);
142238389eaaSLijun Ou }
142338389eaaSLijun Ou 
hns_roce_hem_list_find_mtt(struct hns_roce_dev * hr_dev,struct hns_roce_hem_list * hem_list,int offset,int * mtt_cnt)142438389eaaSLijun Ou void *hns_roce_hem_list_find_mtt(struct hns_roce_dev *hr_dev,
142538389eaaSLijun Ou 				 struct hns_roce_hem_list *hem_list,
14265f652387SChengchang Tang 				 int offset, int *mtt_cnt)
142738389eaaSLijun Ou {
142838389eaaSLijun Ou 	struct list_head *head = &hem_list->btm_bt;
14291f704d8cSXi Wang 	struct hns_roce_hem_item *hem, *temp_hem;
143038389eaaSLijun Ou 	void *cpu_base = NULL;
143138389eaaSLijun Ou 	int nr = 0;
143238389eaaSLijun Ou 
143338389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, head, sibling) {
143438389eaaSLijun Ou 		if (hem_list_page_is_in_range(hem, offset)) {
143538389eaaSLijun Ou 			nr = offset - hem->start;
143638389eaaSLijun Ou 			cpu_base = hem->addr + nr * BA_BYTE_LEN;
143738389eaaSLijun Ou 			nr = hem->end + 1 - offset;
143838389eaaSLijun Ou 			break;
143938389eaaSLijun Ou 		}
144038389eaaSLijun Ou 	}
144138389eaaSLijun Ou 
144238389eaaSLijun Ou 	if (mtt_cnt)
144338389eaaSLijun Ou 		*mtt_cnt = nr;
144438389eaaSLijun Ou 
144538389eaaSLijun Ou 	return cpu_base;
144638389eaaSLijun Ou }
1447