xref: /linux/drivers/infiniband/hw/hns/hns_roce_hem.c (revision d7019c0f47ae497bae27be5397287ca72c704bf4)
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 <linux/platform_device.h>
359a443537Soulijun #include "hns_roce_device.h"
369a443537Soulijun #include "hns_roce_hem.h"
379a443537Soulijun #include "hns_roce_common.h"
389a443537Soulijun 
399a443537Soulijun #define DMA_ADDR_T_SHIFT		12
409a443537Soulijun #define BT_BA_SHIFT			32
419a443537Soulijun 
42a25d13cbSShaobo Xu bool hns_roce_check_whether_mhop(struct hns_roce_dev *hr_dev, u32 type)
43a25d13cbSShaobo Xu {
44*d7019c0fSLijun Ou 	int hop_num = 0;
45a25d13cbSShaobo Xu 
46*d7019c0fSLijun Ou 	switch (type) {
47*d7019c0fSLijun Ou 	case HEM_TYPE_QPC:
48*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.qpc_hop_num;
49*d7019c0fSLijun Ou 		break;
50*d7019c0fSLijun Ou 	case HEM_TYPE_MTPT:
51*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.mpt_hop_num;
52*d7019c0fSLijun Ou 		break;
53*d7019c0fSLijun Ou 	case HEM_TYPE_CQC:
54*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.cqc_hop_num;
55*d7019c0fSLijun Ou 		break;
56*d7019c0fSLijun Ou 	case HEM_TYPE_SRQC:
57*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.srqc_hop_num;
58*d7019c0fSLijun Ou 		break;
59*d7019c0fSLijun Ou 	case HEM_TYPE_SCCC:
60*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.sccc_hop_num;
61*d7019c0fSLijun Ou 		break;
62*d7019c0fSLijun Ou 	case HEM_TYPE_QPC_TIMER:
63*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.qpc_timer_hop_num;
64*d7019c0fSLijun Ou 		break;
65*d7019c0fSLijun Ou 	case HEM_TYPE_CQC_TIMER:
66*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.cqc_timer_hop_num;
67*d7019c0fSLijun Ou 		break;
68*d7019c0fSLijun Ou 	case HEM_TYPE_CQE:
69*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.cqe_hop_num;
70*d7019c0fSLijun Ou 		break;
71*d7019c0fSLijun Ou 	case HEM_TYPE_MTT:
72*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.mtt_hop_num;
73*d7019c0fSLijun Ou 		break;
74*d7019c0fSLijun Ou 	case HEM_TYPE_SRQWQE:
75*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.srqwqe_hop_num;
76*d7019c0fSLijun Ou 		break;
77*d7019c0fSLijun Ou 	case HEM_TYPE_IDX:
78*d7019c0fSLijun Ou 		hop_num = hr_dev->caps.idx_hop_num;
79*d7019c0fSLijun Ou 		break;
80*d7019c0fSLijun Ou 	default:
81a25d13cbSShaobo Xu 		return false;
82a25d13cbSShaobo Xu 	}
83a25d13cbSShaobo Xu 
84*d7019c0fSLijun Ou 	return hop_num ? true : false;
85*d7019c0fSLijun Ou }
86*d7019c0fSLijun Ou 
87a25d13cbSShaobo Xu static bool hns_roce_check_hem_null(struct hns_roce_hem **hem, u64 start_idx,
88a25d13cbSShaobo Xu 			    u32 bt_chunk_num)
89a25d13cbSShaobo Xu {
90a25d13cbSShaobo Xu 	int i;
91a25d13cbSShaobo Xu 
92a25d13cbSShaobo Xu 	for (i = 0; i < bt_chunk_num; i++)
93a25d13cbSShaobo Xu 		if (hem[start_idx + i])
94a25d13cbSShaobo Xu 			return false;
95a25d13cbSShaobo Xu 
96a25d13cbSShaobo Xu 	return true;
97a25d13cbSShaobo Xu }
98a25d13cbSShaobo Xu 
99a25d13cbSShaobo Xu static bool hns_roce_check_bt_null(u64 **bt, u64 start_idx, u32 bt_chunk_num)
100a25d13cbSShaobo Xu {
101a25d13cbSShaobo Xu 	int i;
102a25d13cbSShaobo Xu 
103a25d13cbSShaobo Xu 	for (i = 0; i < bt_chunk_num; i++)
104a25d13cbSShaobo Xu 		if (bt[start_idx + i])
105a25d13cbSShaobo Xu 			return false;
106a25d13cbSShaobo Xu 
107a25d13cbSShaobo Xu 	return true;
108a25d13cbSShaobo Xu }
109a25d13cbSShaobo Xu 
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 
122*d7019c0fSLijun Ou static int get_hem_table_config(struct hns_roce_dev *hr_dev,
123*d7019c0fSLijun Ou 				struct hns_roce_hem_mhop *mhop,
124*d7019c0fSLijun Ou 				u32 type)
125a25d13cbSShaobo Xu {
126a25d13cbSShaobo Xu 	struct device *dev = hr_dev->dev;
127a25d13cbSShaobo Xu 
128*d7019c0fSLijun 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;
1856a93c77aSShaobo Xu 	case HEM_TYPE_MTT:
1866a93c77aSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.mtt_buf_pg_sz
1876a93c77aSShaobo Xu 					     + PAGE_SHIFT);
1886a93c77aSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.mtt_ba_pg_sz
1896a93c77aSShaobo Xu 					     + PAGE_SHIFT);
1902a3d923fSLijun Ou 		mhop->ba_l0_num = mhop->bt_chunk_size / BA_BYTE_LEN;
1916a93c77aSShaobo Xu 		mhop->hop_num = hr_dev->caps.mtt_hop_num;
1926a93c77aSShaobo Xu 		break;
1936a93c77aSShaobo Xu 	case HEM_TYPE_CQE:
1946a93c77aSShaobo Xu 		mhop->buf_chunk_size = 1 << (hr_dev->caps.cqe_buf_pg_sz
1956a93c77aSShaobo Xu 					     + PAGE_SHIFT);
1966a93c77aSShaobo Xu 		mhop->bt_chunk_size = 1 << (hr_dev->caps.cqe_ba_pg_sz
1976a93c77aSShaobo Xu 					     + PAGE_SHIFT);
1982a3d923fSLijun Ou 		mhop->ba_l0_num = mhop->bt_chunk_size / BA_BYTE_LEN;
1996a93c77aSShaobo Xu 		mhop->hop_num = hr_dev->caps.cqe_hop_num;
2006a93c77aSShaobo Xu 		break;
201c7bcb134SLijun Ou 	case HEM_TYPE_SRQWQE:
202c7bcb134SLijun Ou 		mhop->buf_chunk_size = 1 << (hr_dev->caps.srqwqe_buf_pg_sz
203c7bcb134SLijun Ou 					    + PAGE_SHIFT);
204c7bcb134SLijun Ou 		mhop->bt_chunk_size = 1 << (hr_dev->caps.srqwqe_ba_pg_sz
205c7bcb134SLijun Ou 					    + PAGE_SHIFT);
2062a3d923fSLijun Ou 		mhop->ba_l0_num = mhop->bt_chunk_size / BA_BYTE_LEN;
207c7bcb134SLijun Ou 		mhop->hop_num = hr_dev->caps.srqwqe_hop_num;
208c7bcb134SLijun Ou 		break;
209c7bcb134SLijun Ou 	case HEM_TYPE_IDX:
210c7bcb134SLijun Ou 		mhop->buf_chunk_size = 1 << (hr_dev->caps.idx_buf_pg_sz
211c7bcb134SLijun Ou 				       + PAGE_SHIFT);
212c7bcb134SLijun Ou 		mhop->bt_chunk_size = 1 << (hr_dev->caps.idx_ba_pg_sz
213c7bcb134SLijun Ou 				       + PAGE_SHIFT);
2142a3d923fSLijun Ou 		mhop->ba_l0_num = mhop->bt_chunk_size / BA_BYTE_LEN;
215c7bcb134SLijun Ou 		mhop->hop_num = hr_dev->caps.idx_hop_num;
216c7bcb134SLijun Ou 		break;
217a25d13cbSShaobo Xu 	default:
218a25d13cbSShaobo Xu 		dev_err(dev, "Table %d not support multi-hop addressing!\n",
219*d7019c0fSLijun Ou 			type);
220a25d13cbSShaobo Xu 		return -EINVAL;
221a25d13cbSShaobo Xu 	}
222a25d13cbSShaobo Xu 
223*d7019c0fSLijun Ou 	return 0;
224*d7019c0fSLijun Ou }
225*d7019c0fSLijun Ou 
226*d7019c0fSLijun Ou int hns_roce_calc_hem_mhop(struct hns_roce_dev *hr_dev,
227*d7019c0fSLijun Ou 			   struct hns_roce_hem_table *table, unsigned long *obj,
228*d7019c0fSLijun Ou 			   struct hns_roce_hem_mhop *mhop)
229*d7019c0fSLijun Ou {
230*d7019c0fSLijun Ou 	struct device *dev = hr_dev->dev;
231*d7019c0fSLijun Ou 	u32 chunk_ba_num;
232*d7019c0fSLijun Ou 	u32 table_idx;
233*d7019c0fSLijun Ou 	u32 bt_num;
234*d7019c0fSLijun Ou 	u32 chunk_size;
235*d7019c0fSLijun Ou 
236*d7019c0fSLijun Ou 	if (get_hem_table_config(hr_dev, mhop, table->type))
237*d7019c0fSLijun Ou 		return -EINVAL;
238*d7019c0fSLijun Ou 
239a25d13cbSShaobo Xu 	if (!obj)
240a25d13cbSShaobo Xu 		return 0;
241a25d13cbSShaobo Xu 
2426a93c77aSShaobo Xu 	/*
2436a157f7dSYangyang Li 	 * QPC/MTPT/CQC/SRQC/SCCC alloc hem for buffer pages.
2446a93c77aSShaobo Xu 	 * MTT/CQE alloc hem for bt pages.
2456a93c77aSShaobo Xu 	 */
246a25d13cbSShaobo Xu 	bt_num = hns_roce_get_bt_num(table->type, mhop->hop_num);
2472a3d923fSLijun Ou 	chunk_ba_num = mhop->bt_chunk_size / BA_BYTE_LEN;
2486a93c77aSShaobo Xu 	chunk_size = table->type < HEM_TYPE_MTT ? mhop->buf_chunk_size :
2496a93c77aSShaobo Xu 			      mhop->bt_chunk_size;
250a25d13cbSShaobo Xu 	table_idx = (*obj & (table->num_obj - 1)) /
251a25d13cbSShaobo Xu 		     (chunk_size / table->obj_size);
252a25d13cbSShaobo Xu 	switch (bt_num) {
253a25d13cbSShaobo Xu 	case 3:
254a25d13cbSShaobo Xu 		mhop->l2_idx = table_idx & (chunk_ba_num - 1);
255a25d13cbSShaobo Xu 		mhop->l1_idx = table_idx / chunk_ba_num & (chunk_ba_num - 1);
25673b4e1f4SLijun Ou 		mhop->l0_idx = (table_idx / chunk_ba_num) / chunk_ba_num;
257a25d13cbSShaobo Xu 		break;
258a25d13cbSShaobo Xu 	case 2:
259a25d13cbSShaobo Xu 		mhop->l1_idx = table_idx & (chunk_ba_num - 1);
260a25d13cbSShaobo Xu 		mhop->l0_idx = table_idx / chunk_ba_num;
261a25d13cbSShaobo Xu 		break;
262a25d13cbSShaobo Xu 	case 1:
263a25d13cbSShaobo Xu 		mhop->l0_idx = table_idx;
264a25d13cbSShaobo Xu 		break;
265a25d13cbSShaobo Xu 	default:
266a25d13cbSShaobo Xu 		dev_err(dev, "Table %d not support hop_num = %d!\n",
267a25d13cbSShaobo Xu 			     table->type, mhop->hop_num);
268a25d13cbSShaobo Xu 		return -EINVAL;
269a25d13cbSShaobo Xu 	}
270a25d13cbSShaobo Xu 	if (mhop->l0_idx >= mhop->ba_l0_num)
271a25d13cbSShaobo Xu 		mhop->l0_idx %= mhop->ba_l0_num;
272a25d13cbSShaobo Xu 
273a25d13cbSShaobo Xu 	return 0;
274a25d13cbSShaobo Xu }
275a25d13cbSShaobo Xu 
276a25d13cbSShaobo Xu static struct hns_roce_hem *hns_roce_alloc_hem(struct hns_roce_dev *hr_dev,
277a25d13cbSShaobo Xu 					       int npages,
278a25d13cbSShaobo Xu 					       unsigned long hem_alloc_size,
2799a443537Soulijun 					       gfp_t gfp_mask)
2809a443537Soulijun {
2819a443537Soulijun 	struct hns_roce_hem_chunk *chunk = NULL;
2829a443537Soulijun 	struct hns_roce_hem *hem;
2839a443537Soulijun 	struct scatterlist *mem;
2849a443537Soulijun 	int order;
2859a443537Soulijun 	void *buf;
2869a443537Soulijun 
2879a443537Soulijun 	WARN_ON(gfp_mask & __GFP_HIGHMEM);
2889a443537Soulijun 
2899a443537Soulijun 	hem = kmalloc(sizeof(*hem),
2909a443537Soulijun 		      gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
2919a443537Soulijun 	if (!hem)
2929a443537Soulijun 		return NULL;
2939a443537Soulijun 
2949a443537Soulijun 	hem->refcount = 0;
2959a443537Soulijun 	INIT_LIST_HEAD(&hem->chunk_list);
2969a443537Soulijun 
297a25d13cbSShaobo Xu 	order = get_order(hem_alloc_size);
2989a443537Soulijun 
2999a443537Soulijun 	while (npages > 0) {
3009a443537Soulijun 		if (!chunk) {
3019a443537Soulijun 			chunk = kmalloc(sizeof(*chunk),
3029a443537Soulijun 				gfp_mask & ~(__GFP_HIGHMEM | __GFP_NOWARN));
3039a443537Soulijun 			if (!chunk)
3049a443537Soulijun 				goto fail;
3059a443537Soulijun 
3069a443537Soulijun 			sg_init_table(chunk->mem, HNS_ROCE_HEM_CHUNK_LEN);
3079a443537Soulijun 			chunk->npages = 0;
3089a443537Soulijun 			chunk->nsg = 0;
309378efe79SWei Hu\(Xavier\) 			memset(chunk->buf, 0, sizeof(chunk->buf));
3109a443537Soulijun 			list_add_tail(&chunk->list, &hem->chunk_list);
3119a443537Soulijun 		}
3129a443537Soulijun 
3139a443537Soulijun 		while (1 << order > npages)
3149a443537Soulijun 			--order;
3159a443537Soulijun 
3169a443537Soulijun 		/*
3179a443537Soulijun 		 * Alloc memory one time. If failed, don't alloc small block
3189a443537Soulijun 		 * memory, directly return fail.
3199a443537Soulijun 		 */
3209a443537Soulijun 		mem = &chunk->mem[chunk->npages];
32113ca970eSWei Hu(Xavier) 		buf = dma_alloc_coherent(hr_dev->dev, PAGE_SIZE << order,
3229a443537Soulijun 				&sg_dma_address(mem), gfp_mask);
3239a443537Soulijun 		if (!buf)
3249a443537Soulijun 			goto fail;
3259a443537Soulijun 
326378efe79SWei Hu\(Xavier\) 		chunk->buf[chunk->npages] = buf;
3279a443537Soulijun 		sg_dma_len(mem) = PAGE_SIZE << order;
3289a443537Soulijun 
3299a443537Soulijun 		++chunk->npages;
3309a443537Soulijun 		++chunk->nsg;
3319a443537Soulijun 		npages -= 1 << order;
3329a443537Soulijun 	}
3339a443537Soulijun 
3349a443537Soulijun 	return hem;
3359a443537Soulijun 
3369a443537Soulijun fail:
3379a443537Soulijun 	hns_roce_free_hem(hr_dev, hem);
3389a443537Soulijun 	return NULL;
3399a443537Soulijun }
3409a443537Soulijun 
3419a443537Soulijun void hns_roce_free_hem(struct hns_roce_dev *hr_dev, struct hns_roce_hem *hem)
3429a443537Soulijun {
3439a443537Soulijun 	struct hns_roce_hem_chunk *chunk, *tmp;
3449a443537Soulijun 	int i;
3459a443537Soulijun 
3469a443537Soulijun 	if (!hem)
3479a443537Soulijun 		return;
3489a443537Soulijun 
3499a443537Soulijun 	list_for_each_entry_safe(chunk, tmp, &hem->chunk_list, list) {
3509a443537Soulijun 		for (i = 0; i < chunk->npages; ++i)
35113ca970eSWei Hu(Xavier) 			dma_free_coherent(hr_dev->dev,
352378efe79SWei Hu\(Xavier\) 				   sg_dma_len(&chunk->mem[i]),
353378efe79SWei Hu\(Xavier\) 				   chunk->buf[i],
3549a443537Soulijun 				   sg_dma_address(&chunk->mem[i]));
3559a443537Soulijun 		kfree(chunk);
3569a443537Soulijun 	}
3579a443537Soulijun 
3589a443537Soulijun 	kfree(hem);
3599a443537Soulijun }
3609a443537Soulijun 
3619a443537Soulijun static int hns_roce_set_hem(struct hns_roce_dev *hr_dev,
3629a443537Soulijun 			    struct hns_roce_hem_table *table, unsigned long obj)
3639a443537Soulijun {
3649a443537Soulijun 	spinlock_t *lock = &hr_dev->bt_cmd_lock;
36513ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
366a511f822SColin Ian King 	long end;
3679a443537Soulijun 	unsigned long flags;
3689a443537Soulijun 	struct hns_roce_hem_iter iter;
3699a443537Soulijun 	void __iomem *bt_cmd;
3709a443537Soulijun 	u32 bt_cmd_h_val = 0;
3719a443537Soulijun 	u32 bt_cmd_val[2];
3729a443537Soulijun 	u32 bt_cmd_l = 0;
3739a443537Soulijun 	u64 bt_ba = 0;
3749a443537Soulijun 	int ret = 0;
3759a443537Soulijun 
3769a443537Soulijun 	/* Find the HEM(Hardware Entry Memory) entry */
3779a443537Soulijun 	unsigned long i = (obj & (table->num_obj - 1)) /
37829a1fe5dSWei Hu(Xavier) 			  (table->table_chunk_size / table->obj_size);
3799a443537Soulijun 
3809a443537Soulijun 	switch (table->type) {
3819a443537Soulijun 	case HEM_TYPE_QPC:
3829a443537Soulijun 		roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
3839a443537Soulijun 			       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_QPC);
3849a443537Soulijun 		break;
3859a443537Soulijun 	case HEM_TYPE_MTPT:
3869a443537Soulijun 		roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
3879a443537Soulijun 			       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
3889a443537Soulijun 			       HEM_TYPE_MTPT);
3899a443537Soulijun 		break;
3909a443537Soulijun 	case HEM_TYPE_CQC:
3919a443537Soulijun 		roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
3929a443537Soulijun 			       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S, HEM_TYPE_CQC);
3939a443537Soulijun 		break;
3949a443537Soulijun 	case HEM_TYPE_SRQC:
3959a443537Soulijun 		roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_M,
3969a443537Soulijun 			       ROCEE_BT_CMD_H_ROCEE_BT_CMD_MDF_S,
3979a443537Soulijun 			       HEM_TYPE_SRQC);
3989a443537Soulijun 		break;
3999a443537Soulijun 	default:
4009a443537Soulijun 		return ret;
4019a443537Soulijun 	}
4029a443537Soulijun 	roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_M,
4039a443537Soulijun 		       ROCEE_BT_CMD_H_ROCEE_BT_CMD_IN_MDF_S, obj);
4049a443537Soulijun 	roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_S, 0);
4059a443537Soulijun 	roce_set_bit(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_HW_SYNS_S, 1);
4069a443537Soulijun 
4079a443537Soulijun 	/* Currently iter only a chunk */
4089a443537Soulijun 	for (hns_roce_hem_first(table->hem[i], &iter);
4099a443537Soulijun 	     !hns_roce_hem_last(&iter); hns_roce_hem_next(&iter)) {
4109a443537Soulijun 		bt_ba = hns_roce_hem_addr(&iter) >> DMA_ADDR_T_SHIFT;
4119a443537Soulijun 
4129a443537Soulijun 		spin_lock_irqsave(lock, flags);
4139a443537Soulijun 
4149a443537Soulijun 		bt_cmd = hr_dev->reg_base + ROCEE_BT_CMD_H_REG;
4159a443537Soulijun 
416669cefb6SLang Cheng 		end = HW_SYNC_TIMEOUT_MSECS;
417a511f822SColin Ian King 		while (end > 0) {
418fa027328SColin Ian King 			if (!(readl(bt_cmd) >> BT_CMD_SYNC_SHIFT))
419669cefb6SLang Cheng 				break;
420669cefb6SLang Cheng 
421669cefb6SLang Cheng 			mdelay(HW_SYNC_SLEEP_TIME_INTERVAL);
422669cefb6SLang Cheng 			end -= HW_SYNC_SLEEP_TIME_INTERVAL;
423669cefb6SLang Cheng 		}
424669cefb6SLang Cheng 
425669cefb6SLang Cheng 		if (end <= 0) {
4269a443537Soulijun 			dev_err(dev, "Write bt_cmd err,hw_sync is not zero.\n");
4279a443537Soulijun 			spin_unlock_irqrestore(lock, flags);
4289a443537Soulijun 			return -EBUSY;
4299a443537Soulijun 		}
4309a443537Soulijun 
4319a443537Soulijun 		bt_cmd_l = (u32)bt_ba;
4329a443537Soulijun 		roce_set_field(bt_cmd_h_val, ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_M,
4339a443537Soulijun 			       ROCEE_BT_CMD_H_ROCEE_BT_CMD_BA_H_S,
4349a443537Soulijun 			       bt_ba >> BT_BA_SHIFT);
4359a443537Soulijun 
4369a443537Soulijun 		bt_cmd_val[0] = bt_cmd_l;
4379a443537Soulijun 		bt_cmd_val[1] = bt_cmd_h_val;
4389a443537Soulijun 		hns_roce_write64_k(bt_cmd_val,
4399a443537Soulijun 				   hr_dev->reg_base + ROCEE_BT_CMD_L_REG);
4409a443537Soulijun 		spin_unlock_irqrestore(lock, flags);
4419a443537Soulijun 	}
4429a443537Soulijun 
4439a443537Soulijun 	return ret;
4449a443537Soulijun }
4459a443537Soulijun 
446281d0ccfSColin Ian King static int hns_roce_table_mhop_get(struct hns_roce_dev *hr_dev,
447a25d13cbSShaobo Xu 				   struct hns_roce_hem_table *table,
448a25d13cbSShaobo Xu 				   unsigned long obj)
449a25d13cbSShaobo Xu {
450a25d13cbSShaobo Xu 	struct device *dev = hr_dev->dev;
451a25d13cbSShaobo Xu 	struct hns_roce_hem_mhop mhop;
452a25d13cbSShaobo Xu 	struct hns_roce_hem_iter iter;
453a25d13cbSShaobo Xu 	u32 buf_chunk_size;
454a25d13cbSShaobo Xu 	u32 bt_chunk_size;
455a25d13cbSShaobo Xu 	u32 chunk_ba_num;
456a25d13cbSShaobo Xu 	u32 hop_num;
457a25d13cbSShaobo Xu 	u32 size;
458a25d13cbSShaobo Xu 	u32 bt_num;
459a25d13cbSShaobo Xu 	u64 hem_idx;
460a25d13cbSShaobo Xu 	u64 bt_l1_idx = 0;
461a25d13cbSShaobo Xu 	u64 bt_l0_idx = 0;
462a25d13cbSShaobo Xu 	u64 bt_ba;
463a25d13cbSShaobo Xu 	unsigned long mhop_obj = obj;
464a25d13cbSShaobo Xu 	int bt_l1_allocated = 0;
465a25d13cbSShaobo Xu 	int bt_l0_allocated = 0;
466a25d13cbSShaobo Xu 	int step_idx;
467a25d13cbSShaobo Xu 	int ret;
468a25d13cbSShaobo Xu 
469a25d13cbSShaobo Xu 	ret = hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, &mhop);
470a25d13cbSShaobo Xu 	if (ret)
471a25d13cbSShaobo Xu 		return ret;
472a25d13cbSShaobo Xu 
473a25d13cbSShaobo Xu 	buf_chunk_size = mhop.buf_chunk_size;
474a25d13cbSShaobo Xu 	bt_chunk_size = mhop.bt_chunk_size;
475a25d13cbSShaobo Xu 	hop_num = mhop.hop_num;
4762a3d923fSLijun Ou 	chunk_ba_num = bt_chunk_size / BA_BYTE_LEN;
477a25d13cbSShaobo Xu 
478a25d13cbSShaobo Xu 	bt_num = hns_roce_get_bt_num(table->type, hop_num);
479a25d13cbSShaobo Xu 	switch (bt_num) {
480a25d13cbSShaobo Xu 	case 3:
481a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx * chunk_ba_num * chunk_ba_num +
482a25d13cbSShaobo Xu 			  mhop.l1_idx * chunk_ba_num + mhop.l2_idx;
483a25d13cbSShaobo Xu 		bt_l1_idx = mhop.l0_idx * chunk_ba_num + mhop.l1_idx;
484a25d13cbSShaobo Xu 		bt_l0_idx = mhop.l0_idx;
485a25d13cbSShaobo Xu 		break;
486a25d13cbSShaobo Xu 	case 2:
487a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx * chunk_ba_num + mhop.l1_idx;
488a25d13cbSShaobo Xu 		bt_l0_idx = mhop.l0_idx;
489a25d13cbSShaobo Xu 		break;
490a25d13cbSShaobo Xu 	case 1:
491a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx;
492a25d13cbSShaobo Xu 		break;
493a25d13cbSShaobo Xu 	default:
494a25d13cbSShaobo Xu 		dev_err(dev, "Table %d not support hop_num = %d!\n",
495a25d13cbSShaobo Xu 			     table->type, hop_num);
496a25d13cbSShaobo Xu 		return -EINVAL;
497a25d13cbSShaobo Xu 	}
498a25d13cbSShaobo Xu 
499a25d13cbSShaobo Xu 	mutex_lock(&table->mutex);
500a25d13cbSShaobo Xu 
501a25d13cbSShaobo Xu 	if (table->hem[hem_idx]) {
502a25d13cbSShaobo Xu 		++table->hem[hem_idx]->refcount;
503a25d13cbSShaobo Xu 		goto out;
504a25d13cbSShaobo Xu 	}
505a25d13cbSShaobo Xu 
506a25d13cbSShaobo Xu 	/* alloc L1 BA's chunk */
507a25d13cbSShaobo Xu 	if ((check_whether_bt_num_3(table->type, hop_num) ||
508a25d13cbSShaobo Xu 		check_whether_bt_num_2(table->type, hop_num)) &&
509a25d13cbSShaobo Xu 		!table->bt_l0[bt_l0_idx]) {
510a25d13cbSShaobo Xu 		table->bt_l0[bt_l0_idx] = dma_alloc_coherent(dev, bt_chunk_size,
511a25d13cbSShaobo Xu 					    &(table->bt_l0_dma_addr[bt_l0_idx]),
512a25d13cbSShaobo Xu 					    GFP_KERNEL);
513a25d13cbSShaobo Xu 		if (!table->bt_l0[bt_l0_idx]) {
514a25d13cbSShaobo Xu 			ret = -ENOMEM;
515a25d13cbSShaobo Xu 			goto out;
516a25d13cbSShaobo Xu 		}
517a25d13cbSShaobo Xu 		bt_l0_allocated = 1;
518a25d13cbSShaobo Xu 
519a25d13cbSShaobo Xu 		/* set base address to hardware */
520a25d13cbSShaobo Xu 		if (table->type < HEM_TYPE_MTT) {
521a25d13cbSShaobo Xu 			step_idx = 0;
522a25d13cbSShaobo Xu 			if (hr_dev->hw->set_hem(hr_dev, table, obj, step_idx)) {
523a25d13cbSShaobo Xu 				ret = -ENODEV;
524a25d13cbSShaobo Xu 				dev_err(dev, "set HEM base address to HW failed!\n");
525a25d13cbSShaobo Xu 				goto err_dma_alloc_l1;
526a25d13cbSShaobo Xu 			}
527a25d13cbSShaobo Xu 		}
528a25d13cbSShaobo Xu 	}
529a25d13cbSShaobo Xu 
530a25d13cbSShaobo Xu 	/* alloc L2 BA's chunk */
531a25d13cbSShaobo Xu 	if (check_whether_bt_num_3(table->type, hop_num) &&
532a25d13cbSShaobo Xu 	    !table->bt_l1[bt_l1_idx])  {
533a25d13cbSShaobo Xu 		table->bt_l1[bt_l1_idx] = dma_alloc_coherent(dev, bt_chunk_size,
534a25d13cbSShaobo Xu 					    &(table->bt_l1_dma_addr[bt_l1_idx]),
535a25d13cbSShaobo Xu 					    GFP_KERNEL);
536a25d13cbSShaobo Xu 		if (!table->bt_l1[bt_l1_idx]) {
537a25d13cbSShaobo Xu 			ret = -ENOMEM;
538a25d13cbSShaobo Xu 			goto err_dma_alloc_l1;
539a25d13cbSShaobo Xu 		}
540a25d13cbSShaobo Xu 		bt_l1_allocated = 1;
541a25d13cbSShaobo Xu 		*(table->bt_l0[bt_l0_idx] + mhop.l1_idx) =
542a25d13cbSShaobo Xu 					       table->bt_l1_dma_addr[bt_l1_idx];
543a25d13cbSShaobo Xu 
544a25d13cbSShaobo Xu 		/* set base address to hardware */
545a25d13cbSShaobo Xu 		step_idx = 1;
546a25d13cbSShaobo Xu 		if (hr_dev->hw->set_hem(hr_dev, table, obj, step_idx)) {
547a25d13cbSShaobo Xu 			ret = -ENODEV;
548a25d13cbSShaobo Xu 			dev_err(dev, "set HEM base address to HW failed!\n");
549a25d13cbSShaobo Xu 			goto err_alloc_hem_buf;
550a25d13cbSShaobo Xu 		}
551a25d13cbSShaobo Xu 	}
552a25d13cbSShaobo Xu 
5536a93c77aSShaobo Xu 	/*
5546a157f7dSYangyang Li 	 * alloc buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC.
5556a93c77aSShaobo Xu 	 * alloc bt space chunk for MTT/CQE.
5566a93c77aSShaobo Xu 	 */
5576a93c77aSShaobo Xu 	size = table->type < HEM_TYPE_MTT ? buf_chunk_size : bt_chunk_size;
558a25d13cbSShaobo Xu 	table->hem[hem_idx] = hns_roce_alloc_hem(hr_dev,
559a25d13cbSShaobo Xu 						size >> PAGE_SHIFT,
560a25d13cbSShaobo Xu 						size,
561a25d13cbSShaobo Xu 						(table->lowmem ? GFP_KERNEL :
562a25d13cbSShaobo Xu 						GFP_HIGHUSER) | __GFP_NOWARN);
563a25d13cbSShaobo Xu 	if (!table->hem[hem_idx]) {
564a25d13cbSShaobo Xu 		ret = -ENOMEM;
565a25d13cbSShaobo Xu 		goto err_alloc_hem_buf;
566a25d13cbSShaobo Xu 	}
567a25d13cbSShaobo Xu 
568a25d13cbSShaobo Xu 	hns_roce_hem_first(table->hem[hem_idx], &iter);
569a25d13cbSShaobo Xu 	bt_ba = hns_roce_hem_addr(&iter);
570a25d13cbSShaobo Xu 
571a25d13cbSShaobo Xu 	if (table->type < HEM_TYPE_MTT) {
572a25d13cbSShaobo Xu 		if (hop_num == 2) {
573a25d13cbSShaobo Xu 			*(table->bt_l1[bt_l1_idx] + mhop.l2_idx) = bt_ba;
574a25d13cbSShaobo Xu 			step_idx = 2;
575a25d13cbSShaobo Xu 		} else if (hop_num == 1) {
576a25d13cbSShaobo Xu 			*(table->bt_l0[bt_l0_idx] + mhop.l1_idx) = bt_ba;
577a25d13cbSShaobo Xu 			step_idx = 1;
578a25d13cbSShaobo Xu 		} else if (hop_num == HNS_ROCE_HOP_NUM_0) {
579a25d13cbSShaobo Xu 			step_idx = 0;
58026f63b9cSLijun Ou 		} else {
58126f63b9cSLijun Ou 			ret = -EINVAL;
58226f63b9cSLijun Ou 			goto err_dma_alloc_l1;
583a25d13cbSShaobo Xu 		}
584a25d13cbSShaobo Xu 
585a25d13cbSShaobo Xu 		/* set HEM base address to hardware */
586a25d13cbSShaobo Xu 		if (hr_dev->hw->set_hem(hr_dev, table, obj, step_idx)) {
587a25d13cbSShaobo Xu 			ret = -ENODEV;
588a25d13cbSShaobo Xu 			dev_err(dev, "set HEM base address to HW failed!\n");
589a25d13cbSShaobo Xu 			goto err_alloc_hem_buf;
590a25d13cbSShaobo Xu 		}
5916a93c77aSShaobo Xu 	} else if (hop_num == 2) {
5926a93c77aSShaobo Xu 		*(table->bt_l0[bt_l0_idx] + mhop.l1_idx) = bt_ba;
593a25d13cbSShaobo Xu 	}
594a25d13cbSShaobo Xu 
595a25d13cbSShaobo Xu 	++table->hem[hem_idx]->refcount;
596a25d13cbSShaobo Xu 	goto out;
597a25d13cbSShaobo Xu 
598a25d13cbSShaobo Xu err_alloc_hem_buf:
599a25d13cbSShaobo Xu 	if (bt_l1_allocated) {
600a25d13cbSShaobo Xu 		dma_free_coherent(dev, bt_chunk_size, table->bt_l1[bt_l1_idx],
601a25d13cbSShaobo Xu 				  table->bt_l1_dma_addr[bt_l1_idx]);
602a25d13cbSShaobo Xu 		table->bt_l1[bt_l1_idx] = NULL;
603a25d13cbSShaobo Xu 	}
604a25d13cbSShaobo Xu 
605a25d13cbSShaobo Xu err_dma_alloc_l1:
606a25d13cbSShaobo Xu 	if (bt_l0_allocated) {
607a25d13cbSShaobo Xu 		dma_free_coherent(dev, bt_chunk_size, table->bt_l0[bt_l0_idx],
608a25d13cbSShaobo Xu 				  table->bt_l0_dma_addr[bt_l0_idx]);
609a25d13cbSShaobo Xu 		table->bt_l0[bt_l0_idx] = NULL;
610a25d13cbSShaobo Xu 	}
611a25d13cbSShaobo Xu 
612a25d13cbSShaobo Xu out:
613a25d13cbSShaobo Xu 	mutex_unlock(&table->mutex);
614a25d13cbSShaobo Xu 	return ret;
615a25d13cbSShaobo Xu }
616a25d13cbSShaobo Xu 
6179a443537Soulijun int hns_roce_table_get(struct hns_roce_dev *hr_dev,
6189a443537Soulijun 		       struct hns_roce_hem_table *table, unsigned long obj)
6199a443537Soulijun {
62013ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
6219a443537Soulijun 	int ret = 0;
6229a443537Soulijun 	unsigned long i;
6239a443537Soulijun 
624a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type))
625a25d13cbSShaobo Xu 		return hns_roce_table_mhop_get(hr_dev, table, obj);
626a25d13cbSShaobo Xu 
62729a1fe5dSWei Hu(Xavier) 	i = (obj & (table->num_obj - 1)) / (table->table_chunk_size /
6289a443537Soulijun 	     table->obj_size);
6299a443537Soulijun 
6309a443537Soulijun 	mutex_lock(&table->mutex);
6319a443537Soulijun 
6329a443537Soulijun 	if (table->hem[i]) {
6339a443537Soulijun 		++table->hem[i]->refcount;
6349a443537Soulijun 		goto out;
6359a443537Soulijun 	}
6369a443537Soulijun 
6379a443537Soulijun 	table->hem[i] = hns_roce_alloc_hem(hr_dev,
63829a1fe5dSWei Hu(Xavier) 				       table->table_chunk_size >> PAGE_SHIFT,
63929a1fe5dSWei Hu(Xavier) 				       table->table_chunk_size,
6409a443537Soulijun 				       (table->lowmem ? GFP_KERNEL :
6419a443537Soulijun 					GFP_HIGHUSER) | __GFP_NOWARN);
6429a443537Soulijun 	if (!table->hem[i]) {
6439a443537Soulijun 		ret = -ENOMEM;
6449a443537Soulijun 		goto out;
6459a443537Soulijun 	}
6469a443537Soulijun 
6479a443537Soulijun 	/* Set HEM base address(128K/page, pa) to Hardware */
6489a443537Soulijun 	if (hns_roce_set_hem(hr_dev, table, obj)) {
64908eb3018SWei Hu(Xavier) 		hns_roce_free_hem(hr_dev, table->hem[i]);
65008eb3018SWei Hu(Xavier) 		table->hem[i] = NULL;
6519a443537Soulijun 		ret = -ENODEV;
6529a443537Soulijun 		dev_err(dev, "set HEM base address to HW failed.\n");
6539a443537Soulijun 		goto out;
6549a443537Soulijun 	}
6559a443537Soulijun 
6569a443537Soulijun 	++table->hem[i]->refcount;
6579a443537Soulijun out:
6589a443537Soulijun 	mutex_unlock(&table->mutex);
6599a443537Soulijun 	return ret;
6609a443537Soulijun }
6619a443537Soulijun 
662281d0ccfSColin Ian King static void hns_roce_table_mhop_put(struct hns_roce_dev *hr_dev,
663a25d13cbSShaobo Xu 				    struct hns_roce_hem_table *table,
664a25d13cbSShaobo Xu 				    unsigned long obj,
665a25d13cbSShaobo Xu 				    int check_refcount)
666a25d13cbSShaobo Xu {
667a25d13cbSShaobo Xu 	struct device *dev = hr_dev->dev;
668a25d13cbSShaobo Xu 	struct hns_roce_hem_mhop mhop;
669a25d13cbSShaobo Xu 	unsigned long mhop_obj = obj;
670a25d13cbSShaobo Xu 	u32 bt_chunk_size;
671a25d13cbSShaobo Xu 	u32 chunk_ba_num;
672a25d13cbSShaobo Xu 	u32 hop_num;
673a25d13cbSShaobo Xu 	u32 start_idx;
674a25d13cbSShaobo Xu 	u32 bt_num;
675a25d13cbSShaobo Xu 	u64 hem_idx;
676a25d13cbSShaobo Xu 	u64 bt_l1_idx = 0;
677a25d13cbSShaobo Xu 	int ret;
678a25d13cbSShaobo Xu 
679a25d13cbSShaobo Xu 	ret = hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, &mhop);
680a25d13cbSShaobo Xu 	if (ret)
681a25d13cbSShaobo Xu 		return;
682a25d13cbSShaobo Xu 
683a25d13cbSShaobo Xu 	bt_chunk_size = mhop.bt_chunk_size;
684a25d13cbSShaobo Xu 	hop_num = mhop.hop_num;
6852a3d923fSLijun Ou 	chunk_ba_num = bt_chunk_size / BA_BYTE_LEN;
686a25d13cbSShaobo Xu 
687a25d13cbSShaobo Xu 	bt_num = hns_roce_get_bt_num(table->type, hop_num);
688a25d13cbSShaobo Xu 	switch (bt_num) {
689a25d13cbSShaobo Xu 	case 3:
690a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx * chunk_ba_num * chunk_ba_num +
691a25d13cbSShaobo Xu 			  mhop.l1_idx * chunk_ba_num + mhop.l2_idx;
692a25d13cbSShaobo Xu 		bt_l1_idx = mhop.l0_idx * chunk_ba_num + mhop.l1_idx;
693a25d13cbSShaobo Xu 		break;
694a25d13cbSShaobo Xu 	case 2:
695a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx * chunk_ba_num + mhop.l1_idx;
696a25d13cbSShaobo Xu 		break;
697a25d13cbSShaobo Xu 	case 1:
698a25d13cbSShaobo Xu 		hem_idx = mhop.l0_idx;
699a25d13cbSShaobo Xu 		break;
700a25d13cbSShaobo Xu 	default:
701a25d13cbSShaobo Xu 		dev_err(dev, "Table %d not support hop_num = %d!\n",
702a25d13cbSShaobo Xu 			     table->type, hop_num);
703a25d13cbSShaobo Xu 		return;
704a25d13cbSShaobo Xu 	}
705a25d13cbSShaobo Xu 
706a25d13cbSShaobo Xu 	mutex_lock(&table->mutex);
707a25d13cbSShaobo Xu 
708a25d13cbSShaobo Xu 	if (check_refcount && (--table->hem[hem_idx]->refcount > 0)) {
709a25d13cbSShaobo Xu 		mutex_unlock(&table->mutex);
710a25d13cbSShaobo Xu 		return;
711a25d13cbSShaobo Xu 	}
712a25d13cbSShaobo Xu 
713a25d13cbSShaobo Xu 	if (table->type < HEM_TYPE_MTT && hop_num == 1) {
714a25d13cbSShaobo Xu 		if (hr_dev->hw->clear_hem(hr_dev, table, obj, 1))
715a25d13cbSShaobo Xu 			dev_warn(dev, "Clear HEM base address failed.\n");
716a25d13cbSShaobo Xu 	} else if (table->type < HEM_TYPE_MTT && hop_num == 2) {
717a25d13cbSShaobo Xu 		if (hr_dev->hw->clear_hem(hr_dev, table, obj, 2))
718a25d13cbSShaobo Xu 			dev_warn(dev, "Clear HEM base address failed.\n");
719a25d13cbSShaobo Xu 	} else if (table->type < HEM_TYPE_MTT &&
720a25d13cbSShaobo Xu 		   hop_num == HNS_ROCE_HOP_NUM_0) {
721a25d13cbSShaobo Xu 		if (hr_dev->hw->clear_hem(hr_dev, table, obj, 0))
722a25d13cbSShaobo Xu 			dev_warn(dev, "Clear HEM base address failed.\n");
723a25d13cbSShaobo Xu 	}
724a25d13cbSShaobo Xu 
7256a93c77aSShaobo Xu 	/*
7266a157f7dSYangyang Li 	 * free buffer space chunk for QPC/MTPT/CQC/SRQC/SCCC.
7276a93c77aSShaobo Xu 	 * free bt space chunk for MTT/CQE.
7286a93c77aSShaobo Xu 	 */
729a25d13cbSShaobo Xu 	hns_roce_free_hem(hr_dev, table->hem[hem_idx]);
730a25d13cbSShaobo Xu 	table->hem[hem_idx] = NULL;
731a25d13cbSShaobo Xu 
732a25d13cbSShaobo Xu 	if (check_whether_bt_num_2(table->type, hop_num)) {
733a25d13cbSShaobo Xu 		start_idx = mhop.l0_idx * chunk_ba_num;
734a25d13cbSShaobo Xu 		if (hns_roce_check_hem_null(table->hem, start_idx,
735a25d13cbSShaobo Xu 					    chunk_ba_num)) {
736a25d13cbSShaobo Xu 			if (table->type < HEM_TYPE_MTT &&
737a25d13cbSShaobo Xu 			    hr_dev->hw->clear_hem(hr_dev, table, obj, 0))
738a25d13cbSShaobo Xu 				dev_warn(dev, "Clear HEM base address failed.\n");
739a25d13cbSShaobo Xu 
740a25d13cbSShaobo Xu 			dma_free_coherent(dev, bt_chunk_size,
741a25d13cbSShaobo Xu 					  table->bt_l0[mhop.l0_idx],
742a25d13cbSShaobo Xu 					  table->bt_l0_dma_addr[mhop.l0_idx]);
743a25d13cbSShaobo Xu 			table->bt_l0[mhop.l0_idx] = NULL;
744a25d13cbSShaobo Xu 		}
745a25d13cbSShaobo Xu 	} else if (check_whether_bt_num_3(table->type, hop_num)) {
746a25d13cbSShaobo Xu 		start_idx = mhop.l0_idx * chunk_ba_num * chunk_ba_num +
747a25d13cbSShaobo Xu 			    mhop.l1_idx * chunk_ba_num;
748a25d13cbSShaobo Xu 		if (hns_roce_check_hem_null(table->hem, start_idx,
749a25d13cbSShaobo Xu 					    chunk_ba_num)) {
750a25d13cbSShaobo Xu 			if (hr_dev->hw->clear_hem(hr_dev, table, obj, 1))
751a25d13cbSShaobo Xu 				dev_warn(dev, "Clear HEM base address failed.\n");
752a25d13cbSShaobo Xu 
753a25d13cbSShaobo Xu 			dma_free_coherent(dev, bt_chunk_size,
754a25d13cbSShaobo Xu 					  table->bt_l1[bt_l1_idx],
755a25d13cbSShaobo Xu 					  table->bt_l1_dma_addr[bt_l1_idx]);
756a25d13cbSShaobo Xu 			table->bt_l1[bt_l1_idx] = NULL;
757a25d13cbSShaobo Xu 
758a25d13cbSShaobo Xu 			start_idx = mhop.l0_idx * chunk_ba_num;
759a25d13cbSShaobo Xu 			if (hns_roce_check_bt_null(table->bt_l1, start_idx,
760a25d13cbSShaobo Xu 						   chunk_ba_num)) {
761a25d13cbSShaobo Xu 				if (hr_dev->hw->clear_hem(hr_dev, table, obj,
762a25d13cbSShaobo Xu 							  0))
763a25d13cbSShaobo Xu 					dev_warn(dev, "Clear HEM base address failed.\n");
764a25d13cbSShaobo Xu 
765a25d13cbSShaobo Xu 				dma_free_coherent(dev, bt_chunk_size,
766a25d13cbSShaobo Xu 					    table->bt_l0[mhop.l0_idx],
767a25d13cbSShaobo Xu 					    table->bt_l0_dma_addr[mhop.l0_idx]);
768a25d13cbSShaobo Xu 				table->bt_l0[mhop.l0_idx] = NULL;
769a25d13cbSShaobo Xu 			}
770a25d13cbSShaobo Xu 		}
771a25d13cbSShaobo Xu 	}
772a25d13cbSShaobo Xu 
773a25d13cbSShaobo Xu 	mutex_unlock(&table->mutex);
774a25d13cbSShaobo Xu }
775a25d13cbSShaobo Xu 
7769a443537Soulijun void hns_roce_table_put(struct hns_roce_dev *hr_dev,
7779a443537Soulijun 			struct hns_roce_hem_table *table, unsigned long obj)
7789a443537Soulijun {
77913ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
7809a443537Soulijun 	unsigned long i;
7819a443537Soulijun 
782a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
783a25d13cbSShaobo Xu 		hns_roce_table_mhop_put(hr_dev, table, obj, 1);
784a25d13cbSShaobo Xu 		return;
785a25d13cbSShaobo Xu 	}
786a25d13cbSShaobo Xu 
7879a443537Soulijun 	i = (obj & (table->num_obj - 1)) /
78829a1fe5dSWei Hu(Xavier) 	    (table->table_chunk_size / table->obj_size);
7899a443537Soulijun 
7909a443537Soulijun 	mutex_lock(&table->mutex);
7919a443537Soulijun 
7929a443537Soulijun 	if (--table->hem[i]->refcount == 0) {
7939a443537Soulijun 		/* Clear HEM base address */
794a25d13cbSShaobo Xu 		if (hr_dev->hw->clear_hem(hr_dev, table, obj, 0))
7959a443537Soulijun 			dev_warn(dev, "Clear HEM base address failed.\n");
7969a443537Soulijun 
7979a443537Soulijun 		hns_roce_free_hem(hr_dev, table->hem[i]);
7989a443537Soulijun 		table->hem[i] = NULL;
7999a443537Soulijun 	}
8009a443537Soulijun 
8019a443537Soulijun 	mutex_unlock(&table->mutex);
8029a443537Soulijun }
8039a443537Soulijun 
8046a93c77aSShaobo Xu void *hns_roce_table_find(struct hns_roce_dev *hr_dev,
8056a93c77aSShaobo Xu 			  struct hns_roce_hem_table *table,
8066a93c77aSShaobo Xu 			  unsigned long obj, dma_addr_t *dma_handle)
8079a443537Soulijun {
8089a443537Soulijun 	struct hns_roce_hem_chunk *chunk;
8096a93c77aSShaobo Xu 	struct hns_roce_hem_mhop mhop;
8109a443537Soulijun 	struct hns_roce_hem *hem;
811378efe79SWei Hu\(Xavier\) 	void *addr = NULL;
8126a93c77aSShaobo Xu 	unsigned long mhop_obj = obj;
8130203b14cSoulijun 	unsigned long obj_per_chunk;
8140203b14cSoulijun 	unsigned long idx_offset;
8156a93c77aSShaobo Xu 	int offset, dma_offset;
816378efe79SWei Hu\(Xavier\) 	int length;
8176a93c77aSShaobo Xu 	int i, j;
8186a93c77aSShaobo Xu 	u32 hem_idx = 0;
8199a443537Soulijun 
8209a443537Soulijun 	if (!table->lowmem)
8219a443537Soulijun 		return NULL;
8229a443537Soulijun 
8239a443537Soulijun 	mutex_lock(&table->mutex);
8246a93c77aSShaobo Xu 
8256a93c77aSShaobo Xu 	if (!hns_roce_check_whether_mhop(hr_dev, table->type)) {
8260203b14cSoulijun 		obj_per_chunk = table->table_chunk_size / table->obj_size;
8270203b14cSoulijun 		hem = table->hem[(obj & (table->num_obj - 1)) / obj_per_chunk];
8280203b14cSoulijun 		idx_offset = (obj & (table->num_obj - 1)) % obj_per_chunk;
8290203b14cSoulijun 		dma_offset = offset = idx_offset * table->obj_size;
8306a93c77aSShaobo Xu 	} else {
8314772e03dSLijun Ou 		u32 seg_size = 64; /* 8 bytes per BA and 8 BA per segment */
8324772e03dSLijun Ou 
8336a93c77aSShaobo Xu 		hns_roce_calc_hem_mhop(hr_dev, table, &mhop_obj, &mhop);
8346a93c77aSShaobo Xu 		/* mtt mhop */
8356a93c77aSShaobo Xu 		i = mhop.l0_idx;
8366a93c77aSShaobo Xu 		j = mhop.l1_idx;
8376a93c77aSShaobo Xu 		if (mhop.hop_num == 2)
8382a3d923fSLijun Ou 			hem_idx = i * (mhop.bt_chunk_size / BA_BYTE_LEN) + j;
8396a93c77aSShaobo Xu 		else if (mhop.hop_num == 1 ||
8406a93c77aSShaobo Xu 			 mhop.hop_num == HNS_ROCE_HOP_NUM_0)
8416a93c77aSShaobo Xu 			hem_idx = i;
8426a93c77aSShaobo Xu 
8436a93c77aSShaobo Xu 		hem = table->hem[hem_idx];
8444772e03dSLijun Ou 		dma_offset = offset = (obj & (table->num_obj - 1)) * seg_size %
8454772e03dSLijun Ou 				       mhop.bt_chunk_size;
8466a93c77aSShaobo Xu 		if (mhop.hop_num == 2)
8476a93c77aSShaobo Xu 			dma_offset = offset = 0;
8486a93c77aSShaobo Xu 	}
8499a443537Soulijun 
8509a443537Soulijun 	if (!hem)
8519a443537Soulijun 		goto out;
8529a443537Soulijun 
8539a443537Soulijun 	list_for_each_entry(chunk, &hem->chunk_list, list) {
8549a443537Soulijun 		for (i = 0; i < chunk->npages; ++i) {
855378efe79SWei Hu\(Xavier\) 			length = sg_dma_len(&chunk->mem[i]);
8569a443537Soulijun 			if (dma_handle && dma_offset >= 0) {
857378efe79SWei Hu\(Xavier\) 				if (length > (u32)dma_offset)
8589a443537Soulijun 					*dma_handle = sg_dma_address(
8599a443537Soulijun 						&chunk->mem[i]) + dma_offset;
860378efe79SWei Hu\(Xavier\) 				dma_offset -= length;
8619a443537Soulijun 			}
8629a443537Soulijun 
863378efe79SWei Hu\(Xavier\) 			if (length > (u32)offset) {
864378efe79SWei Hu\(Xavier\) 				addr = chunk->buf[i] + offset;
8659a443537Soulijun 				goto out;
8669a443537Soulijun 			}
867378efe79SWei Hu\(Xavier\) 			offset -= length;
8689a443537Soulijun 		}
8699a443537Soulijun 	}
8709a443537Soulijun 
8719a443537Soulijun out:
8729a443537Soulijun 	mutex_unlock(&table->mutex);
873378efe79SWei Hu\(Xavier\) 	return addr;
8749a443537Soulijun }
8759a443537Soulijun 
8769a443537Soulijun int hns_roce_table_get_range(struct hns_roce_dev *hr_dev,
8779a443537Soulijun 			     struct hns_roce_hem_table *table,
8789a443537Soulijun 			     unsigned long start, unsigned long end)
8799a443537Soulijun {
8806a93c77aSShaobo Xu 	struct hns_roce_hem_mhop mhop;
88129a1fe5dSWei Hu(Xavier) 	unsigned long inc = table->table_chunk_size / table->obj_size;
8826a93c77aSShaobo Xu 	unsigned long i;
8836a93c77aSShaobo Xu 	int ret;
8846a93c77aSShaobo Xu 
8856a93c77aSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
8866a93c77aSShaobo Xu 		hns_roce_calc_hem_mhop(hr_dev, table, NULL, &mhop);
8876a93c77aSShaobo Xu 		inc = mhop.bt_chunk_size / table->obj_size;
8886a93c77aSShaobo Xu 	}
8899a443537Soulijun 
8909a443537Soulijun 	/* Allocate MTT entry memory according to chunk(128K) */
8919a443537Soulijun 	for (i = start; i <= end; i += inc) {
8929a443537Soulijun 		ret = hns_roce_table_get(hr_dev, table, i);
8939a443537Soulijun 		if (ret)
8949a443537Soulijun 			goto fail;
8959a443537Soulijun 	}
8969a443537Soulijun 
8979a443537Soulijun 	return 0;
8989a443537Soulijun 
8999a443537Soulijun fail:
9009a443537Soulijun 	while (i > start) {
9019a443537Soulijun 		i -= inc;
9029a443537Soulijun 		hns_roce_table_put(hr_dev, table, i);
9039a443537Soulijun 	}
9049a443537Soulijun 	return ret;
9059a443537Soulijun }
9069a443537Soulijun 
9079a443537Soulijun void hns_roce_table_put_range(struct hns_roce_dev *hr_dev,
9089a443537Soulijun 			      struct hns_roce_hem_table *table,
9099a443537Soulijun 			      unsigned long start, unsigned long end)
9109a443537Soulijun {
9116a93c77aSShaobo Xu 	struct hns_roce_hem_mhop mhop;
91229a1fe5dSWei Hu(Xavier) 	unsigned long inc = table->table_chunk_size / table->obj_size;
9139a443537Soulijun 	unsigned long i;
9149a443537Soulijun 
9156a93c77aSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
9166a93c77aSShaobo Xu 		hns_roce_calc_hem_mhop(hr_dev, table, NULL, &mhop);
9176a93c77aSShaobo Xu 		inc = mhop.bt_chunk_size / table->obj_size;
9186a93c77aSShaobo Xu 	}
9196a93c77aSShaobo Xu 
92029a1fe5dSWei Hu(Xavier) 	for (i = start; i <= end; i += inc)
9219a443537Soulijun 		hns_roce_table_put(hr_dev, table, i);
9229a443537Soulijun }
9239a443537Soulijun 
9249a443537Soulijun int hns_roce_init_hem_table(struct hns_roce_dev *hr_dev,
9259a443537Soulijun 			    struct hns_roce_hem_table *table, u32 type,
9269a443537Soulijun 			    unsigned long obj_size, unsigned long nobj,
9279a443537Soulijun 			    int use_lowmem)
9289a443537Soulijun {
9299a443537Soulijun 	unsigned long obj_per_chunk;
9309a443537Soulijun 	unsigned long num_hem;
9319a443537Soulijun 
932a25d13cbSShaobo Xu 	if (!hns_roce_check_whether_mhop(hr_dev, type)) {
93329a1fe5dSWei Hu(Xavier) 		table->table_chunk_size = hr_dev->caps.chunk_sz;
93429a1fe5dSWei Hu(Xavier) 		obj_per_chunk = table->table_chunk_size / obj_size;
9359a443537Soulijun 		num_hem = (nobj + obj_per_chunk - 1) / obj_per_chunk;
9369a443537Soulijun 
9379a443537Soulijun 		table->hem = kcalloc(num_hem, sizeof(*table->hem), GFP_KERNEL);
9389a443537Soulijun 		if (!table->hem)
9399a443537Soulijun 			return -ENOMEM;
940a25d13cbSShaobo Xu 	} else {
941*d7019c0fSLijun Ou 		struct hns_roce_hem_mhop mhop = {};
942a25d13cbSShaobo Xu 		unsigned long buf_chunk_size;
943a25d13cbSShaobo Xu 		unsigned long bt_chunk_size;
944a25d13cbSShaobo Xu 		unsigned long bt_chunk_num;
9456a93c77aSShaobo Xu 		unsigned long num_bt_l0 = 0;
946a25d13cbSShaobo Xu 		u32 hop_num;
947a25d13cbSShaobo Xu 
948*d7019c0fSLijun Ou 		if (get_hem_table_config(hr_dev, &mhop, type))
949a25d13cbSShaobo Xu 			return -EINVAL;
950*d7019c0fSLijun Ou 
951*d7019c0fSLijun Ou 		buf_chunk_size = mhop.buf_chunk_size;
952*d7019c0fSLijun Ou 		bt_chunk_size = mhop.bt_chunk_size;
953*d7019c0fSLijun Ou 		num_bt_l0 = mhop.ba_l0_num;
954*d7019c0fSLijun Ou 		hop_num = mhop.hop_num;
955*d7019c0fSLijun Ou 
956a25d13cbSShaobo Xu 		obj_per_chunk = buf_chunk_size / obj_size;
957a25d13cbSShaobo Xu 		num_hem = (nobj + obj_per_chunk - 1) / obj_per_chunk;
9582a3d923fSLijun Ou 		bt_chunk_num = bt_chunk_size / BA_BYTE_LEN;
959215a8c09Soulijun 		if (type >= HEM_TYPE_MTT)
9606a93c77aSShaobo Xu 			num_bt_l0 = bt_chunk_num;
961a25d13cbSShaobo Xu 
962a25d13cbSShaobo Xu 		table->hem = kcalloc(num_hem, sizeof(*table->hem),
963a25d13cbSShaobo Xu 					 GFP_KERNEL);
964a25d13cbSShaobo Xu 		if (!table->hem)
965a25d13cbSShaobo Xu 			goto err_kcalloc_hem_buf;
966a25d13cbSShaobo Xu 
967215a8c09Soulijun 		if (check_whether_bt_num_3(type, hop_num)) {
968a25d13cbSShaobo Xu 			unsigned long num_bt_l1;
969a25d13cbSShaobo Xu 
970a25d13cbSShaobo Xu 			num_bt_l1 = (num_hem + bt_chunk_num - 1) /
971a25d13cbSShaobo Xu 					     bt_chunk_num;
972a25d13cbSShaobo Xu 			table->bt_l1 = kcalloc(num_bt_l1,
973a25d13cbSShaobo Xu 					       sizeof(*table->bt_l1),
974a25d13cbSShaobo Xu 					       GFP_KERNEL);
975a25d13cbSShaobo Xu 			if (!table->bt_l1)
976a25d13cbSShaobo Xu 				goto err_kcalloc_bt_l1;
977a25d13cbSShaobo Xu 
978a25d13cbSShaobo Xu 			table->bt_l1_dma_addr = kcalloc(num_bt_l1,
979a25d13cbSShaobo Xu 						 sizeof(*table->bt_l1_dma_addr),
980a25d13cbSShaobo Xu 						 GFP_KERNEL);
981a25d13cbSShaobo Xu 
982a25d13cbSShaobo Xu 			if (!table->bt_l1_dma_addr)
983a25d13cbSShaobo Xu 				goto err_kcalloc_l1_dma;
984a25d13cbSShaobo Xu 		}
985a25d13cbSShaobo Xu 
986215a8c09Soulijun 		if (check_whether_bt_num_2(type, hop_num) ||
987215a8c09Soulijun 			check_whether_bt_num_3(type, hop_num)) {
988a25d13cbSShaobo Xu 			table->bt_l0 = kcalloc(num_bt_l0, sizeof(*table->bt_l0),
989a25d13cbSShaobo Xu 					       GFP_KERNEL);
990a25d13cbSShaobo Xu 			if (!table->bt_l0)
991a25d13cbSShaobo Xu 				goto err_kcalloc_bt_l0;
992a25d13cbSShaobo Xu 
993a25d13cbSShaobo Xu 			table->bt_l0_dma_addr = kcalloc(num_bt_l0,
994a25d13cbSShaobo Xu 						 sizeof(*table->bt_l0_dma_addr),
995a25d13cbSShaobo Xu 						 GFP_KERNEL);
996a25d13cbSShaobo Xu 			if (!table->bt_l0_dma_addr)
997a25d13cbSShaobo Xu 				goto err_kcalloc_l0_dma;
998a25d13cbSShaobo Xu 		}
999a25d13cbSShaobo Xu 	}
10009a443537Soulijun 
10019a443537Soulijun 	table->type = type;
10029a443537Soulijun 	table->num_hem = num_hem;
10039a443537Soulijun 	table->num_obj = nobj;
10049a443537Soulijun 	table->obj_size = obj_size;
10059a443537Soulijun 	table->lowmem = use_lowmem;
10069a443537Soulijun 	mutex_init(&table->mutex);
10079a443537Soulijun 
10089a443537Soulijun 	return 0;
1009a25d13cbSShaobo Xu 
1010a25d13cbSShaobo Xu err_kcalloc_l0_dma:
1011a25d13cbSShaobo Xu 	kfree(table->bt_l0);
1012a25d13cbSShaobo Xu 	table->bt_l0 = NULL;
1013a25d13cbSShaobo Xu 
1014a25d13cbSShaobo Xu err_kcalloc_bt_l0:
1015a25d13cbSShaobo Xu 	kfree(table->bt_l1_dma_addr);
1016a25d13cbSShaobo Xu 	table->bt_l1_dma_addr = NULL;
1017a25d13cbSShaobo Xu 
1018a25d13cbSShaobo Xu err_kcalloc_l1_dma:
1019a25d13cbSShaobo Xu 	kfree(table->bt_l1);
1020a25d13cbSShaobo Xu 	table->bt_l1 = NULL;
1021a25d13cbSShaobo Xu 
1022a25d13cbSShaobo Xu err_kcalloc_bt_l1:
1023a25d13cbSShaobo Xu 	kfree(table->hem);
1024a25d13cbSShaobo Xu 	table->hem = NULL;
1025a25d13cbSShaobo Xu 
1026a25d13cbSShaobo Xu err_kcalloc_hem_buf:
1027a25d13cbSShaobo Xu 	return -ENOMEM;
1028a25d13cbSShaobo Xu }
1029a25d13cbSShaobo Xu 
1030281d0ccfSColin Ian King static void hns_roce_cleanup_mhop_hem_table(struct hns_roce_dev *hr_dev,
1031a25d13cbSShaobo Xu 					    struct hns_roce_hem_table *table)
1032a25d13cbSShaobo Xu {
1033a25d13cbSShaobo Xu 	struct hns_roce_hem_mhop mhop;
1034a25d13cbSShaobo Xu 	u32 buf_chunk_size;
1035a25d13cbSShaobo Xu 	int i;
1036a25d13cbSShaobo Xu 	u64 obj;
1037a25d13cbSShaobo Xu 
1038a25d13cbSShaobo Xu 	hns_roce_calc_hem_mhop(hr_dev, table, NULL, &mhop);
10396a93c77aSShaobo Xu 	buf_chunk_size = table->type < HEM_TYPE_MTT ? mhop.buf_chunk_size :
10406a93c77aSShaobo Xu 					mhop.bt_chunk_size;
1041a25d13cbSShaobo Xu 
1042a25d13cbSShaobo Xu 	for (i = 0; i < table->num_hem; ++i) {
1043a25d13cbSShaobo Xu 		obj = i * buf_chunk_size / table->obj_size;
1044a25d13cbSShaobo Xu 		if (table->hem[i])
1045a25d13cbSShaobo Xu 			hns_roce_table_mhop_put(hr_dev, table, obj, 0);
1046a25d13cbSShaobo Xu 	}
1047a25d13cbSShaobo Xu 
1048a25d13cbSShaobo Xu 	kfree(table->hem);
1049a25d13cbSShaobo Xu 	table->hem = NULL;
1050a25d13cbSShaobo Xu 	kfree(table->bt_l1);
1051a25d13cbSShaobo Xu 	table->bt_l1 = NULL;
1052a25d13cbSShaobo Xu 	kfree(table->bt_l1_dma_addr);
1053a25d13cbSShaobo Xu 	table->bt_l1_dma_addr = NULL;
1054a25d13cbSShaobo Xu 	kfree(table->bt_l0);
1055a25d13cbSShaobo Xu 	table->bt_l0 = NULL;
1056a25d13cbSShaobo Xu 	kfree(table->bt_l0_dma_addr);
1057a25d13cbSShaobo Xu 	table->bt_l0_dma_addr = NULL;
10589a443537Soulijun }
10599a443537Soulijun 
10609a443537Soulijun void hns_roce_cleanup_hem_table(struct hns_roce_dev *hr_dev,
10619a443537Soulijun 				struct hns_roce_hem_table *table)
10629a443537Soulijun {
106313ca970eSWei Hu(Xavier) 	struct device *dev = hr_dev->dev;
10649a443537Soulijun 	unsigned long i;
10659a443537Soulijun 
1066a25d13cbSShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, table->type)) {
1067a25d13cbSShaobo Xu 		hns_roce_cleanup_mhop_hem_table(hr_dev, table);
1068a25d13cbSShaobo Xu 		return;
1069a25d13cbSShaobo Xu 	}
1070a25d13cbSShaobo Xu 
10719a443537Soulijun 	for (i = 0; i < table->num_hem; ++i)
10729a443537Soulijun 		if (table->hem[i]) {
107397f0e39fSWei Hu (Xavier) 			if (hr_dev->hw->clear_hem(hr_dev, table,
107429a1fe5dSWei Hu(Xavier) 			    i * table->table_chunk_size / table->obj_size, 0))
10759a443537Soulijun 				dev_err(dev, "Clear HEM base address failed.\n");
10769a443537Soulijun 
10779a443537Soulijun 			hns_roce_free_hem(hr_dev, table->hem[i]);
10789a443537Soulijun 		}
10799a443537Soulijun 
10809a443537Soulijun 	kfree(table->hem);
10819a443537Soulijun }
10829a443537Soulijun 
10839a443537Soulijun void hns_roce_cleanup_hem(struct hns_roce_dev *hr_dev)
10849a443537Soulijun {
10855c1f167aSLijun Ou 	if ((hr_dev->caps.num_idx_segs))
10865c1f167aSLijun Ou 		hns_roce_cleanup_hem_table(hr_dev,
10875c1f167aSLijun Ou 					   &hr_dev->mr_table.mtt_idx_table);
10885c1f167aSLijun Ou 	if (hr_dev->caps.num_srqwqe_segs)
10895c1f167aSLijun Ou 		hns_roce_cleanup_hem_table(hr_dev,
10905c1f167aSLijun Ou 					   &hr_dev->mr_table.mtt_srqwqe_table);
10915c1f167aSLijun Ou 	if (hr_dev->caps.srqc_entry_sz)
10925c1f167aSLijun Ou 		hns_roce_cleanup_hem_table(hr_dev,
10935c1f167aSLijun Ou 					   &hr_dev->srq_table.table);
10949a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->cq_table.table);
10950e40dc2fSYangyang Li 	if (hr_dev->caps.qpc_timer_entry_sz)
10960e40dc2fSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
10970e40dc2fSYangyang Li 					   &hr_dev->qpc_timer_table);
10980e40dc2fSYangyang Li 	if (hr_dev->caps.cqc_timer_entry_sz)
10990e40dc2fSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
11000e40dc2fSYangyang Li 					   &hr_dev->cqc_timer_table);
11016a157f7dSYangyang Li 	if (hr_dev->caps.sccc_entry_sz)
11026a157f7dSYangyang Li 		hns_roce_cleanup_hem_table(hr_dev,
11036a157f7dSYangyang Li 					   &hr_dev->qp_table.sccc_table);
1104e92f2c18Soulijun 	if (hr_dev->caps.trrl_entry_sz)
1105e92f2c18Soulijun 		hns_roce_cleanup_hem_table(hr_dev,
1106e92f2c18Soulijun 					   &hr_dev->qp_table.trrl_table);
1107ae25db00Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.irrl_table);
11089a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->qp_table.qp_table);
11099a443537Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->mr_table.mtpt_table);
11109766edc3SShaobo Xu 	if (hns_roce_check_whether_mhop(hr_dev, HEM_TYPE_CQE))
11119766edc3SShaobo Xu 		hns_roce_cleanup_hem_table(hr_dev,
11129766edc3SShaobo Xu 					   &hr_dev->mr_table.mtt_cqe_table);
1113ae25db00Soulijun 	hns_roce_cleanup_hem_table(hr_dev, &hr_dev->mr_table.mtt_table);
11149a443537Soulijun }
111538389eaaSLijun Ou 
111638389eaaSLijun Ou struct roce_hem_item {
111738389eaaSLijun Ou 	struct list_head list; /* link all hems in the same bt level */
111838389eaaSLijun Ou 	struct list_head sibling; /* link all hems in last hop for mtt */
111938389eaaSLijun Ou 	void *addr;
112038389eaaSLijun Ou 	dma_addr_t dma_addr;
112138389eaaSLijun Ou 	size_t count; /* max ba numbers */
112238389eaaSLijun Ou 	int start; /* start buf offset in this hem */
112338389eaaSLijun Ou 	int end; /* end buf offset in this hem */
112438389eaaSLijun Ou };
112538389eaaSLijun Ou 
112638389eaaSLijun Ou static struct roce_hem_item *hem_list_alloc_item(struct hns_roce_dev *hr_dev,
112738389eaaSLijun Ou 						   int start, int end,
112838389eaaSLijun Ou 						   int count, bool exist_bt,
112938389eaaSLijun Ou 						   int bt_level)
113038389eaaSLijun Ou {
113138389eaaSLijun Ou 	struct roce_hem_item *hem;
113238389eaaSLijun Ou 
113338389eaaSLijun Ou 	hem = kzalloc(sizeof(*hem), GFP_KERNEL);
113438389eaaSLijun Ou 	if (!hem)
113538389eaaSLijun Ou 		return NULL;
113638389eaaSLijun Ou 
113738389eaaSLijun Ou 	if (exist_bt) {
113838389eaaSLijun Ou 		hem->addr = dma_alloc_coherent(hr_dev->dev,
113938389eaaSLijun Ou 						   count * BA_BYTE_LEN,
114038389eaaSLijun Ou 						   &hem->dma_addr, GFP_KERNEL);
114138389eaaSLijun Ou 		if (!hem->addr) {
114238389eaaSLijun Ou 			kfree(hem);
114338389eaaSLijun Ou 			return NULL;
114438389eaaSLijun Ou 		}
114538389eaaSLijun Ou 	}
114638389eaaSLijun Ou 
114738389eaaSLijun Ou 	hem->count = count;
114838389eaaSLijun Ou 	hem->start = start;
114938389eaaSLijun Ou 	hem->end = end;
115038389eaaSLijun Ou 	INIT_LIST_HEAD(&hem->list);
115138389eaaSLijun Ou 	INIT_LIST_HEAD(&hem->sibling);
115238389eaaSLijun Ou 
115338389eaaSLijun Ou 	return hem;
115438389eaaSLijun Ou }
115538389eaaSLijun Ou 
115638389eaaSLijun Ou static void hem_list_free_item(struct hns_roce_dev *hr_dev,
115738389eaaSLijun Ou 			       struct roce_hem_item *hem, bool exist_bt)
115838389eaaSLijun Ou {
115938389eaaSLijun Ou 	if (exist_bt)
116038389eaaSLijun Ou 		dma_free_coherent(hr_dev->dev, hem->count * BA_BYTE_LEN,
116138389eaaSLijun Ou 				  hem->addr, hem->dma_addr);
116238389eaaSLijun Ou 	kfree(hem);
116338389eaaSLijun Ou }
116438389eaaSLijun Ou 
116538389eaaSLijun Ou static void hem_list_free_all(struct hns_roce_dev *hr_dev,
116638389eaaSLijun Ou 			      struct list_head *head, bool exist_bt)
116738389eaaSLijun Ou {
116838389eaaSLijun Ou 	struct roce_hem_item *hem, *temp_hem;
116938389eaaSLijun Ou 
117038389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, head, list) {
117138389eaaSLijun Ou 		list_del(&hem->list);
117238389eaaSLijun Ou 		hem_list_free_item(hr_dev, hem, exist_bt);
117338389eaaSLijun Ou 	}
117438389eaaSLijun Ou }
117538389eaaSLijun Ou 
117638389eaaSLijun Ou static void hem_list_link_bt(struct hns_roce_dev *hr_dev, void *base_addr,
117738389eaaSLijun Ou 			     u64 table_addr)
117838389eaaSLijun Ou {
117938389eaaSLijun Ou 	*(u64 *)(base_addr) = table_addr;
118038389eaaSLijun Ou }
118138389eaaSLijun Ou 
118238389eaaSLijun Ou /* assign L0 table address to hem from root bt */
118338389eaaSLijun Ou static void hem_list_assign_bt(struct hns_roce_dev *hr_dev,
118438389eaaSLijun Ou 			       struct roce_hem_item *hem, void *cpu_addr,
118538389eaaSLijun Ou 			       u64 phy_addr)
118638389eaaSLijun Ou {
118738389eaaSLijun Ou 	hem->addr = cpu_addr;
118838389eaaSLijun Ou 	hem->dma_addr = (dma_addr_t)phy_addr;
118938389eaaSLijun Ou }
119038389eaaSLijun Ou 
119138389eaaSLijun Ou static inline bool hem_list_page_is_in_range(struct roce_hem_item *hem,
119238389eaaSLijun Ou 					     int offset)
119338389eaaSLijun Ou {
119438389eaaSLijun Ou 	return (hem->start <= offset && offset <= hem->end);
119538389eaaSLijun Ou }
119638389eaaSLijun Ou 
119738389eaaSLijun Ou static struct roce_hem_item *hem_list_search_item(struct list_head *ba_list,
119838389eaaSLijun Ou 						    int page_offset)
119938389eaaSLijun Ou {
120038389eaaSLijun Ou 	struct roce_hem_item *hem, *temp_hem;
120138389eaaSLijun Ou 	struct roce_hem_item *found = NULL;
120238389eaaSLijun Ou 
120338389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, ba_list, list) {
120438389eaaSLijun Ou 		if (hem_list_page_is_in_range(hem, page_offset)) {
120538389eaaSLijun Ou 			found = hem;
120638389eaaSLijun Ou 			break;
120738389eaaSLijun Ou 		}
120838389eaaSLijun Ou 	}
120938389eaaSLijun Ou 
121038389eaaSLijun Ou 	return found;
121138389eaaSLijun Ou }
121238389eaaSLijun Ou 
121338389eaaSLijun Ou static bool hem_list_is_bottom_bt(int hopnum, int bt_level)
121438389eaaSLijun Ou {
121538389eaaSLijun Ou 	/*
121638389eaaSLijun Ou 	 * hopnum    base address table levels
121738389eaaSLijun Ou 	 * 0		L0(buf)
121838389eaaSLijun Ou 	 * 1		L0 -> buf
121938389eaaSLijun Ou 	 * 2		L0 -> L1 -> buf
122038389eaaSLijun Ou 	 * 3		L0 -> L1 -> L2 -> buf
122138389eaaSLijun Ou 	 */
122238389eaaSLijun Ou 	return bt_level >= (hopnum ? hopnum - 1 : hopnum);
122338389eaaSLijun Ou }
122438389eaaSLijun Ou 
122538389eaaSLijun Ou /**
122638389eaaSLijun Ou  * calc base address entries num
122738389eaaSLijun Ou  * @hopnum: num of mutihop addressing
122838389eaaSLijun Ou  * @bt_level: base address table level
122938389eaaSLijun Ou  * @unit: ba entries per bt page
123038389eaaSLijun Ou  */
123138389eaaSLijun Ou static u32 hem_list_calc_ba_range(int hopnum, int bt_level, int unit)
123238389eaaSLijun Ou {
123338389eaaSLijun Ou 	u32 step;
123438389eaaSLijun Ou 	int max;
123538389eaaSLijun Ou 	int i;
123638389eaaSLijun Ou 
123738389eaaSLijun Ou 	if (hopnum <= bt_level)
123838389eaaSLijun Ou 		return 0;
123938389eaaSLijun Ou 	/*
124038389eaaSLijun Ou 	 * hopnum  bt_level   range
124138389eaaSLijun Ou 	 * 1	      0       unit
124238389eaaSLijun Ou 	 * ------------
124338389eaaSLijun Ou 	 * 2	      0       unit * unit
124438389eaaSLijun Ou 	 * 2	      1       unit
124538389eaaSLijun Ou 	 * ------------
124638389eaaSLijun Ou 	 * 3	      0       unit * unit * unit
124738389eaaSLijun Ou 	 * 3	      1       unit * unit
124838389eaaSLijun Ou 	 * 3	      2       unit
124938389eaaSLijun Ou 	 */
125038389eaaSLijun Ou 	step = 1;
125138389eaaSLijun Ou 	max = hopnum - bt_level;
125238389eaaSLijun Ou 	for (i = 0; i < max; i++)
125338389eaaSLijun Ou 		step = step * unit;
125438389eaaSLijun Ou 
125538389eaaSLijun Ou 	return step;
125638389eaaSLijun Ou }
125738389eaaSLijun Ou 
125838389eaaSLijun Ou /**
125938389eaaSLijun Ou  * calc the root ba entries which could cover all regions
126038389eaaSLijun Ou  * @regions: buf region array
126138389eaaSLijun Ou  * @region_cnt: array size of @regions
126238389eaaSLijun Ou  * @unit: ba entries per bt page
126338389eaaSLijun Ou  */
126438389eaaSLijun Ou int hns_roce_hem_list_calc_root_ba(const struct hns_roce_buf_region *regions,
126538389eaaSLijun Ou 				   int region_cnt, int unit)
126638389eaaSLijun Ou {
126738389eaaSLijun Ou 	struct hns_roce_buf_region *r;
126838389eaaSLijun Ou 	int total = 0;
126938389eaaSLijun Ou 	int step;
127038389eaaSLijun Ou 	int i;
127138389eaaSLijun Ou 
127238389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++) {
127338389eaaSLijun Ou 		r = (struct hns_roce_buf_region *)&regions[i];
127438389eaaSLijun Ou 		if (r->hopnum > 1) {
127538389eaaSLijun Ou 			step = hem_list_calc_ba_range(r->hopnum, 1, unit);
127638389eaaSLijun Ou 			if (step > 0)
127738389eaaSLijun Ou 				total += (r->count + step - 1) / step;
127838389eaaSLijun Ou 		} else {
127938389eaaSLijun Ou 			total += r->count;
128038389eaaSLijun Ou 		}
128138389eaaSLijun Ou 	}
128238389eaaSLijun Ou 
128338389eaaSLijun Ou 	return total;
128438389eaaSLijun Ou }
128538389eaaSLijun Ou 
128638389eaaSLijun Ou static int hem_list_alloc_mid_bt(struct hns_roce_dev *hr_dev,
128738389eaaSLijun Ou 				 const struct hns_roce_buf_region *r, int unit,
128838389eaaSLijun Ou 				 int offset, struct list_head *mid_bt,
128938389eaaSLijun Ou 				 struct list_head *btm_bt)
129038389eaaSLijun Ou {
129138389eaaSLijun Ou 	struct roce_hem_item *hem_ptrs[HNS_ROCE_MAX_BT_LEVEL] = { NULL };
129238389eaaSLijun Ou 	struct list_head temp_list[HNS_ROCE_MAX_BT_LEVEL];
129338389eaaSLijun Ou 	struct roce_hem_item *cur, *pre;
129438389eaaSLijun Ou 	const int hopnum = r->hopnum;
129538389eaaSLijun Ou 	int start_aligned;
129638389eaaSLijun Ou 	int distance;
129738389eaaSLijun Ou 	int ret = 0;
129838389eaaSLijun Ou 	int max_ofs;
129938389eaaSLijun Ou 	int level;
130038389eaaSLijun Ou 	u32 step;
130138389eaaSLijun Ou 	int end;
130238389eaaSLijun Ou 
130338389eaaSLijun Ou 	if (hopnum <= 1)
130438389eaaSLijun Ou 		return 0;
130538389eaaSLijun Ou 
130638389eaaSLijun Ou 	if (hopnum > HNS_ROCE_MAX_BT_LEVEL) {
130738389eaaSLijun Ou 		dev_err(hr_dev->dev, "invalid hopnum %d!\n", hopnum);
130838389eaaSLijun Ou 		return -EINVAL;
130938389eaaSLijun Ou 	}
131038389eaaSLijun Ou 
131138389eaaSLijun Ou 	if (offset < r->offset) {
131238389eaaSLijun Ou 		dev_err(hr_dev->dev, "invalid offset %d,min %d!\n",
131338389eaaSLijun Ou 			offset, r->offset);
131438389eaaSLijun Ou 		return -EINVAL;
131538389eaaSLijun Ou 	}
131638389eaaSLijun Ou 
131738389eaaSLijun Ou 	distance = offset - r->offset;
131838389eaaSLijun Ou 	max_ofs = r->offset + r->count - 1;
131938389eaaSLijun Ou 	for (level = 0; level < hopnum; level++)
132038389eaaSLijun Ou 		INIT_LIST_HEAD(&temp_list[level]);
132138389eaaSLijun Ou 
132238389eaaSLijun Ou 	/* config L1 bt to last bt and link them to corresponding parent */
132338389eaaSLijun Ou 	for (level = 1; level < hopnum; level++) {
132438389eaaSLijun Ou 		cur = hem_list_search_item(&mid_bt[level], offset);
132538389eaaSLijun Ou 		if (cur) {
132638389eaaSLijun Ou 			hem_ptrs[level] = cur;
132738389eaaSLijun Ou 			continue;
132838389eaaSLijun Ou 		}
132938389eaaSLijun Ou 
133038389eaaSLijun Ou 		step = hem_list_calc_ba_range(hopnum, level, unit);
133138389eaaSLijun Ou 		if (step < 1) {
133238389eaaSLijun Ou 			ret = -EINVAL;
133338389eaaSLijun Ou 			goto err_exit;
133438389eaaSLijun Ou 		}
133538389eaaSLijun Ou 
133638389eaaSLijun Ou 		start_aligned = (distance / step) * step + r->offset;
133738389eaaSLijun Ou 		end = min_t(int, start_aligned + step - 1, max_ofs);
133838389eaaSLijun Ou 		cur = hem_list_alloc_item(hr_dev, start_aligned, end, unit,
133938389eaaSLijun Ou 					  true, level);
134038389eaaSLijun Ou 		if (!cur) {
134138389eaaSLijun Ou 			ret = -ENOMEM;
134238389eaaSLijun Ou 			goto err_exit;
134338389eaaSLijun Ou 		}
134438389eaaSLijun Ou 		hem_ptrs[level] = cur;
134538389eaaSLijun Ou 		list_add(&cur->list, &temp_list[level]);
134638389eaaSLijun Ou 		if (hem_list_is_bottom_bt(hopnum, level))
134738389eaaSLijun Ou 			list_add(&cur->sibling, &temp_list[0]);
134838389eaaSLijun Ou 
134938389eaaSLijun Ou 		/* link bt to parent bt */
135038389eaaSLijun Ou 		if (level > 1) {
135138389eaaSLijun Ou 			pre = hem_ptrs[level - 1];
135238389eaaSLijun Ou 			step = (cur->start - pre->start) / step * BA_BYTE_LEN;
135338389eaaSLijun Ou 			hem_list_link_bt(hr_dev, pre->addr + step,
135438389eaaSLijun Ou 					 cur->dma_addr);
135538389eaaSLijun Ou 		}
135638389eaaSLijun Ou 	}
135738389eaaSLijun Ou 
135838389eaaSLijun Ou 	list_splice(&temp_list[0], btm_bt);
135938389eaaSLijun Ou 	for (level = 1; level < hopnum; level++)
136038389eaaSLijun Ou 		list_splice(&temp_list[level], &mid_bt[level]);
136138389eaaSLijun Ou 
136238389eaaSLijun Ou 	return 0;
136338389eaaSLijun Ou 
136438389eaaSLijun Ou err_exit:
136538389eaaSLijun Ou 	for (level = 1; level < hopnum; level++)
136638389eaaSLijun Ou 		hem_list_free_all(hr_dev, &temp_list[level], true);
136738389eaaSLijun Ou 
136838389eaaSLijun Ou 	return ret;
136938389eaaSLijun Ou }
137038389eaaSLijun Ou 
137138389eaaSLijun Ou static int hem_list_alloc_root_bt(struct hns_roce_dev *hr_dev,
137238389eaaSLijun Ou 				  struct hns_roce_hem_list *hem_list, int unit,
137338389eaaSLijun Ou 				  const struct hns_roce_buf_region *regions,
137438389eaaSLijun Ou 				  int region_cnt)
137538389eaaSLijun Ou {
137638389eaaSLijun Ou 	struct roce_hem_item *hem, *temp_hem, *root_hem;
137738389eaaSLijun Ou 	struct list_head temp_list[HNS_ROCE_MAX_BT_REGION];
137838389eaaSLijun Ou 	const struct hns_roce_buf_region *r;
137938389eaaSLijun Ou 	struct list_head temp_root;
138038389eaaSLijun Ou 	struct list_head temp_btm;
138138389eaaSLijun Ou 	void *cpu_base;
138238389eaaSLijun Ou 	u64 phy_base;
138338389eaaSLijun Ou 	int ret = 0;
138438389eaaSLijun Ou 	int offset;
138538389eaaSLijun Ou 	int total;
138638389eaaSLijun Ou 	int step;
138738389eaaSLijun Ou 	int i;
138838389eaaSLijun Ou 
138938389eaaSLijun Ou 	r = &regions[0];
139038389eaaSLijun Ou 	root_hem = hem_list_search_item(&hem_list->root_bt, r->offset);
139138389eaaSLijun Ou 	if (root_hem)
139238389eaaSLijun Ou 		return 0;
139338389eaaSLijun Ou 
139438389eaaSLijun Ou 	INIT_LIST_HEAD(&temp_root);
139538389eaaSLijun Ou 	total = r->offset;
139638389eaaSLijun Ou 	/* indicate to last region */
139738389eaaSLijun Ou 	r = &regions[region_cnt - 1];
139838389eaaSLijun Ou 	root_hem = hem_list_alloc_item(hr_dev, total, r->offset + r->count - 1,
139938389eaaSLijun Ou 				       unit, true, 0);
140038389eaaSLijun Ou 	if (!root_hem)
140138389eaaSLijun Ou 		return -ENOMEM;
140238389eaaSLijun Ou 	list_add(&root_hem->list, &temp_root);
140338389eaaSLijun Ou 
140438389eaaSLijun Ou 	hem_list->root_ba = root_hem->dma_addr;
140538389eaaSLijun Ou 
140638389eaaSLijun Ou 	INIT_LIST_HEAD(&temp_btm);
140738389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++)
140838389eaaSLijun Ou 		INIT_LIST_HEAD(&temp_list[i]);
140938389eaaSLijun Ou 
141038389eaaSLijun Ou 	total = 0;
141138389eaaSLijun Ou 	for (i = 0; i < region_cnt && total < unit; i++) {
141238389eaaSLijun Ou 		r = &regions[i];
141338389eaaSLijun Ou 		if (!r->count)
141438389eaaSLijun Ou 			continue;
141538389eaaSLijun Ou 
141638389eaaSLijun Ou 		/* all regions's mid[x][0] shared the root_bt's trunk */
141738389eaaSLijun Ou 		cpu_base = root_hem->addr + total * BA_BYTE_LEN;
141838389eaaSLijun Ou 		phy_base = root_hem->dma_addr + total * BA_BYTE_LEN;
141938389eaaSLijun Ou 
142038389eaaSLijun Ou 		/* if hopnum is 0 or 1, cut a new fake hem from the root bt
142138389eaaSLijun Ou 		 * which's address share to all regions.
142238389eaaSLijun Ou 		 */
142338389eaaSLijun Ou 		if (hem_list_is_bottom_bt(r->hopnum, 0)) {
142438389eaaSLijun Ou 			hem = hem_list_alloc_item(hr_dev, r->offset,
142538389eaaSLijun Ou 						  r->offset + r->count - 1,
142638389eaaSLijun Ou 						  r->count, false, 0);
142738389eaaSLijun Ou 			if (!hem) {
142838389eaaSLijun Ou 				ret = -ENOMEM;
142938389eaaSLijun Ou 				goto err_exit;
143038389eaaSLijun Ou 			}
143138389eaaSLijun Ou 			hem_list_assign_bt(hr_dev, hem, cpu_base, phy_base);
143238389eaaSLijun Ou 			list_add(&hem->list, &temp_list[i]);
143338389eaaSLijun Ou 			list_add(&hem->sibling, &temp_btm);
143438389eaaSLijun Ou 			total += r->count;
143538389eaaSLijun Ou 		} else {
143638389eaaSLijun Ou 			step = hem_list_calc_ba_range(r->hopnum, 1, unit);
143738389eaaSLijun Ou 			if (step < 1) {
143838389eaaSLijun Ou 				ret = -EINVAL;
143938389eaaSLijun Ou 				goto err_exit;
144038389eaaSLijun Ou 			}
144138389eaaSLijun Ou 			/* if exist mid bt, link L1 to L0 */
144238389eaaSLijun Ou 			list_for_each_entry_safe(hem, temp_hem,
144338389eaaSLijun Ou 					  &hem_list->mid_bt[i][1], list) {
144438389eaaSLijun Ou 				offset = hem->start / step * BA_BYTE_LEN;
144538389eaaSLijun Ou 				hem_list_link_bt(hr_dev, cpu_base + offset,
144638389eaaSLijun Ou 						 hem->dma_addr);
144738389eaaSLijun Ou 				total++;
144838389eaaSLijun Ou 			}
144938389eaaSLijun Ou 		}
145038389eaaSLijun Ou 	}
145138389eaaSLijun Ou 
145238389eaaSLijun Ou 	list_splice(&temp_btm, &hem_list->btm_bt);
145338389eaaSLijun Ou 	list_splice(&temp_root, &hem_list->root_bt);
145438389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++)
145538389eaaSLijun Ou 		list_splice(&temp_list[i], &hem_list->mid_bt[i][0]);
145638389eaaSLijun Ou 
145738389eaaSLijun Ou 	return 0;
145838389eaaSLijun Ou 
145938389eaaSLijun Ou err_exit:
146038389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++)
146138389eaaSLijun Ou 		hem_list_free_all(hr_dev, &temp_list[i], false);
146238389eaaSLijun Ou 
146338389eaaSLijun Ou 	hem_list_free_all(hr_dev, &temp_root, true);
146438389eaaSLijun Ou 
146538389eaaSLijun Ou 	return ret;
146638389eaaSLijun Ou }
146738389eaaSLijun Ou 
146838389eaaSLijun Ou /* construct the base address table and link them by address hop config */
146938389eaaSLijun Ou int hns_roce_hem_list_request(struct hns_roce_dev *hr_dev,
147038389eaaSLijun Ou 			      struct hns_roce_hem_list *hem_list,
147138389eaaSLijun Ou 			      const struct hns_roce_buf_region *regions,
147238389eaaSLijun Ou 			      int region_cnt)
147338389eaaSLijun Ou {
147438389eaaSLijun Ou 	const struct hns_roce_buf_region *r;
147538389eaaSLijun Ou 	int ofs, end;
147638389eaaSLijun Ou 	int ret = 0;
147738389eaaSLijun Ou 	int unit;
147838389eaaSLijun Ou 	int i;
147938389eaaSLijun Ou 
148038389eaaSLijun Ou 	if (region_cnt > HNS_ROCE_MAX_BT_REGION) {
148138389eaaSLijun Ou 		dev_err(hr_dev->dev, "invalid region region_cnt %d!\n",
148238389eaaSLijun Ou 			region_cnt);
148338389eaaSLijun Ou 		return -EINVAL;
148438389eaaSLijun Ou 	}
148538389eaaSLijun Ou 
148638389eaaSLijun Ou 	unit = (1 << hem_list->bt_pg_shift) / BA_BYTE_LEN;
148738389eaaSLijun Ou 	for (i = 0; i < region_cnt; i++) {
148838389eaaSLijun Ou 		r = &regions[i];
148938389eaaSLijun Ou 		if (!r->count)
149038389eaaSLijun Ou 			continue;
149138389eaaSLijun Ou 
149238389eaaSLijun Ou 		end = r->offset + r->count;
149338389eaaSLijun Ou 		for (ofs = r->offset; ofs < end; ofs += unit) {
149438389eaaSLijun Ou 			ret = hem_list_alloc_mid_bt(hr_dev, r, unit, ofs,
149538389eaaSLijun Ou 						    hem_list->mid_bt[i],
149638389eaaSLijun Ou 						    &hem_list->btm_bt);
149738389eaaSLijun Ou 			if (ret) {
149838389eaaSLijun Ou 				dev_err(hr_dev->dev,
149938389eaaSLijun Ou 					"alloc hem trunk fail ret=%d!\n", ret);
150038389eaaSLijun Ou 				goto err_alloc;
150138389eaaSLijun Ou 			}
150238389eaaSLijun Ou 		}
150338389eaaSLijun Ou 	}
150438389eaaSLijun Ou 
150538389eaaSLijun Ou 	ret = hem_list_alloc_root_bt(hr_dev, hem_list, unit, regions,
150638389eaaSLijun Ou 				     region_cnt);
150738389eaaSLijun Ou 	if (ret)
150838389eaaSLijun Ou 		dev_err(hr_dev->dev, "alloc hem root fail ret=%d!\n", ret);
150938389eaaSLijun Ou 	else
151038389eaaSLijun Ou 		return 0;
151138389eaaSLijun Ou 
151238389eaaSLijun Ou err_alloc:
151338389eaaSLijun Ou 	hns_roce_hem_list_release(hr_dev, hem_list);
151438389eaaSLijun Ou 
151538389eaaSLijun Ou 	return ret;
151638389eaaSLijun Ou }
151738389eaaSLijun Ou 
151838389eaaSLijun Ou void hns_roce_hem_list_release(struct hns_roce_dev *hr_dev,
151938389eaaSLijun Ou 			       struct hns_roce_hem_list *hem_list)
152038389eaaSLijun Ou {
152138389eaaSLijun Ou 	int i, j;
152238389eaaSLijun Ou 
152338389eaaSLijun Ou 	for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++)
152438389eaaSLijun Ou 		for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++)
152538389eaaSLijun Ou 			hem_list_free_all(hr_dev, &hem_list->mid_bt[i][j],
152638389eaaSLijun Ou 					  j != 0);
152738389eaaSLijun Ou 
152838389eaaSLijun Ou 	hem_list_free_all(hr_dev, &hem_list->root_bt, true);
152938389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->btm_bt);
153038389eaaSLijun Ou 	hem_list->root_ba = 0;
153138389eaaSLijun Ou }
153238389eaaSLijun Ou 
153338389eaaSLijun Ou void hns_roce_hem_list_init(struct hns_roce_hem_list *hem_list,
153438389eaaSLijun Ou 			    int bt_page_order)
153538389eaaSLijun Ou {
153638389eaaSLijun Ou 	int i, j;
153738389eaaSLijun Ou 
153838389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->root_bt);
153938389eaaSLijun Ou 	INIT_LIST_HEAD(&hem_list->btm_bt);
154038389eaaSLijun Ou 	for (i = 0; i < HNS_ROCE_MAX_BT_REGION; i++)
154138389eaaSLijun Ou 		for (j = 0; j < HNS_ROCE_MAX_BT_LEVEL; j++)
154238389eaaSLijun Ou 			INIT_LIST_HEAD(&hem_list->mid_bt[i][j]);
154338389eaaSLijun Ou 
154438389eaaSLijun Ou 	hem_list->bt_pg_shift = bt_page_order;
154538389eaaSLijun Ou }
154638389eaaSLijun Ou 
154738389eaaSLijun Ou void *hns_roce_hem_list_find_mtt(struct hns_roce_dev *hr_dev,
154838389eaaSLijun Ou 				 struct hns_roce_hem_list *hem_list,
154938389eaaSLijun Ou 				 int offset, int *mtt_cnt, u64 *phy_addr)
155038389eaaSLijun Ou {
155138389eaaSLijun Ou 	struct list_head *head = &hem_list->btm_bt;
155238389eaaSLijun Ou 	struct roce_hem_item *hem, *temp_hem;
155338389eaaSLijun Ou 	void *cpu_base = NULL;
155438389eaaSLijun Ou 	u64 phy_base = 0;
155538389eaaSLijun Ou 	int nr = 0;
155638389eaaSLijun Ou 
155738389eaaSLijun Ou 	list_for_each_entry_safe(hem, temp_hem, head, sibling) {
155838389eaaSLijun Ou 		if (hem_list_page_is_in_range(hem, offset)) {
155938389eaaSLijun Ou 			nr = offset - hem->start;
156038389eaaSLijun Ou 			cpu_base = hem->addr + nr * BA_BYTE_LEN;
156138389eaaSLijun Ou 			phy_base = hem->dma_addr + nr * BA_BYTE_LEN;
156238389eaaSLijun Ou 			nr = hem->end + 1 - offset;
156338389eaaSLijun Ou 			break;
156438389eaaSLijun Ou 		}
156538389eaaSLijun Ou 	}
156638389eaaSLijun Ou 
156738389eaaSLijun Ou 	if (mtt_cnt)
156838389eaaSLijun Ou 		*mtt_cnt = nr;
156938389eaaSLijun Ou 
157038389eaaSLijun Ou 	if (phy_addr)
157138389eaaSLijun Ou 		*phy_addr = phy_base;
157238389eaaSLijun Ou 
157338389eaaSLijun Ou 	return cpu_base;
157438389eaaSLijun Ou }
1575