1a08c9b2cSAlexander Lobakin // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2a08c9b2cSAlexander Lobakin /* Copyright (c) 2020 Marvell International Ltd. */
3a08c9b2cSAlexander Lobakin 
4a08c9b2cSAlexander Lobakin #include <linux/dma-mapping.h>
5a08c9b2cSAlexander Lobakin #include <linux/qed/qed_chain.h>
6a08c9b2cSAlexander Lobakin #include <linux/vmalloc.h>
7a08c9b2cSAlexander Lobakin 
8a08c9b2cSAlexander Lobakin #include "qed_dev_api.h"
9a08c9b2cSAlexander Lobakin 
qed_chain_init(struct qed_chain * chain,const struct qed_chain_init_params * params,u32 page_cnt)10b6db3f71SAlexander Lobakin static void qed_chain_init(struct qed_chain *chain,
11b6db3f71SAlexander Lobakin 			   const struct qed_chain_init_params *params,
12b6db3f71SAlexander Lobakin 			   u32 page_cnt)
135e776d80SAlexander Lobakin {
145e776d80SAlexander Lobakin 	memset(chain, 0, sizeof(*chain));
155e776d80SAlexander Lobakin 
16b6db3f71SAlexander Lobakin 	chain->elem_size = params->elem_size;
17b6db3f71SAlexander Lobakin 	chain->intended_use = params->intended_use;
18b6db3f71SAlexander Lobakin 	chain->mode = params->mode;
19b6db3f71SAlexander Lobakin 	chain->cnt_type = params->cnt_type;
205e776d80SAlexander Lobakin 
2115506586SAlexander Lobakin 	chain->elem_per_page = ELEMS_PER_PAGE(params->elem_size,
2215506586SAlexander Lobakin 					      params->page_size);
23b6db3f71SAlexander Lobakin 	chain->usable_per_page = USABLE_ELEMS_PER_PAGE(params->elem_size,
2415506586SAlexander Lobakin 						       params->page_size,
25b6db3f71SAlexander Lobakin 						       params->mode);
26b6db3f71SAlexander Lobakin 	chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(params->elem_size,
27b6db3f71SAlexander Lobakin 						       params->mode);
285e776d80SAlexander Lobakin 
295e776d80SAlexander Lobakin 	chain->elem_per_page_mask = chain->elem_per_page - 1;
305e776d80SAlexander Lobakin 	chain->next_page_mask = chain->usable_per_page &
315e776d80SAlexander Lobakin 				chain->elem_per_page_mask;
325e776d80SAlexander Lobakin 
3315506586SAlexander Lobakin 	chain->page_size = params->page_size;
345e776d80SAlexander Lobakin 	chain->page_cnt = page_cnt;
355e776d80SAlexander Lobakin 	chain->capacity = chain->usable_per_page * page_cnt;
365e776d80SAlexander Lobakin 	chain->size = chain->elem_per_page * page_cnt;
37c3a321b0SAlexander Lobakin 
38b6db3f71SAlexander Lobakin 	if (params->ext_pbl_virt) {
39b6db3f71SAlexander Lobakin 		chain->pbl_sp.table_virt = params->ext_pbl_virt;
40b6db3f71SAlexander Lobakin 		chain->pbl_sp.table_phys = params->ext_pbl_phys;
41c3a321b0SAlexander Lobakin 
42c3a321b0SAlexander Lobakin 		chain->b_external_pbl = true;
43c3a321b0SAlexander Lobakin 	}
445e776d80SAlexander Lobakin }
455e776d80SAlexander Lobakin 
qed_chain_init_next_ptr_elem(const struct qed_chain * chain,void * virt_curr,void * virt_next,dma_addr_t phys_next)465e776d80SAlexander Lobakin static void qed_chain_init_next_ptr_elem(const struct qed_chain *chain,
475e776d80SAlexander Lobakin 					 void *virt_curr, void *virt_next,
485e776d80SAlexander Lobakin 					 dma_addr_t phys_next)
495e776d80SAlexander Lobakin {
505e776d80SAlexander Lobakin 	struct qed_chain_next *next;
515e776d80SAlexander Lobakin 	u32 size;
525e776d80SAlexander Lobakin 
535e776d80SAlexander Lobakin 	size = chain->elem_size * chain->usable_per_page;
545e776d80SAlexander Lobakin 	next = virt_curr + size;
555e776d80SAlexander Lobakin 
565e776d80SAlexander Lobakin 	DMA_REGPAIR_LE(next->next_phys, phys_next);
575e776d80SAlexander Lobakin 	next->next_virt = virt_next;
585e776d80SAlexander Lobakin }
595e776d80SAlexander Lobakin 
qed_chain_init_mem(struct qed_chain * chain,void * virt_addr,dma_addr_t phys_addr)605e776d80SAlexander Lobakin static void qed_chain_init_mem(struct qed_chain *chain, void *virt_addr,
615e776d80SAlexander Lobakin 			       dma_addr_t phys_addr)
625e776d80SAlexander Lobakin {
635e776d80SAlexander Lobakin 	chain->p_virt_addr = virt_addr;
645e776d80SAlexander Lobakin 	chain->p_phys_addr = phys_addr;
655e776d80SAlexander Lobakin }
665e776d80SAlexander Lobakin 
qed_chain_free_next_ptr(struct qed_dev * cdev,struct qed_chain * chain)67a08c9b2cSAlexander Lobakin static void qed_chain_free_next_ptr(struct qed_dev *cdev,
68a08c9b2cSAlexander Lobakin 				    struct qed_chain *chain)
69a08c9b2cSAlexander Lobakin {
70a08c9b2cSAlexander Lobakin 	struct device *dev = &cdev->pdev->dev;
71a08c9b2cSAlexander Lobakin 	struct qed_chain_next *next;
72a08c9b2cSAlexander Lobakin 	dma_addr_t phys, phys_next;
73a08c9b2cSAlexander Lobakin 	void *virt, *virt_next;
74a08c9b2cSAlexander Lobakin 	u32 size, i;
75a08c9b2cSAlexander Lobakin 
76a08c9b2cSAlexander Lobakin 	size = chain->elem_size * chain->usable_per_page;
77a08c9b2cSAlexander Lobakin 	virt = chain->p_virt_addr;
78a08c9b2cSAlexander Lobakin 	phys = chain->p_phys_addr;
79a08c9b2cSAlexander Lobakin 
80a08c9b2cSAlexander Lobakin 	for (i = 0; i < chain->page_cnt; i++) {
81a08c9b2cSAlexander Lobakin 		if (!virt)
82a08c9b2cSAlexander Lobakin 			break;
83a08c9b2cSAlexander Lobakin 
84a08c9b2cSAlexander Lobakin 		next = virt + size;
85a08c9b2cSAlexander Lobakin 		virt_next = next->next_virt;
86a08c9b2cSAlexander Lobakin 		phys_next = HILO_DMA_REGPAIR(next->next_phys);
87a08c9b2cSAlexander Lobakin 
8815506586SAlexander Lobakin 		dma_free_coherent(dev, chain->page_size, virt, phys);
89a08c9b2cSAlexander Lobakin 
90a08c9b2cSAlexander Lobakin 		virt = virt_next;
91a08c9b2cSAlexander Lobakin 		phys = phys_next;
92a08c9b2cSAlexander Lobakin 	}
93a08c9b2cSAlexander Lobakin }
94a08c9b2cSAlexander Lobakin 
qed_chain_free_single(struct qed_dev * cdev,struct qed_chain * chain)95a08c9b2cSAlexander Lobakin static void qed_chain_free_single(struct qed_dev *cdev,
96a08c9b2cSAlexander Lobakin 				  struct qed_chain *chain)
97a08c9b2cSAlexander Lobakin {
98a08c9b2cSAlexander Lobakin 	if (!chain->p_virt_addr)
99a08c9b2cSAlexander Lobakin 		return;
100a08c9b2cSAlexander Lobakin 
10115506586SAlexander Lobakin 	dma_free_coherent(&cdev->pdev->dev, chain->page_size,
102a08c9b2cSAlexander Lobakin 			  chain->p_virt_addr, chain->p_phys_addr);
103a08c9b2cSAlexander Lobakin }
104a08c9b2cSAlexander Lobakin 
qed_chain_free_pbl(struct qed_dev * cdev,struct qed_chain * chain)105a08c9b2cSAlexander Lobakin static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *chain)
106a08c9b2cSAlexander Lobakin {
107a08c9b2cSAlexander Lobakin 	struct device *dev = &cdev->pdev->dev;
108a08c9b2cSAlexander Lobakin 	struct addr_tbl_entry *entry;
1099b6ee3cfSAlexander Lobakin 	u32 i;
110a08c9b2cSAlexander Lobakin 
111a08c9b2cSAlexander Lobakin 	if (!chain->pbl.pp_addr_tbl)
112a08c9b2cSAlexander Lobakin 		return;
113a08c9b2cSAlexander Lobakin 
114a08c9b2cSAlexander Lobakin 	for (i = 0; i < chain->page_cnt; i++) {
115a08c9b2cSAlexander Lobakin 		entry = chain->pbl.pp_addr_tbl + i;
116a08c9b2cSAlexander Lobakin 		if (!entry->virt_addr)
117a08c9b2cSAlexander Lobakin 			break;
118a08c9b2cSAlexander Lobakin 
11915506586SAlexander Lobakin 		dma_free_coherent(dev, chain->page_size, entry->virt_addr,
120a08c9b2cSAlexander Lobakin 				  entry->dma_map);
121a08c9b2cSAlexander Lobakin 	}
122a08c9b2cSAlexander Lobakin 
123a08c9b2cSAlexander Lobakin 	if (!chain->b_external_pbl)
1249b6ee3cfSAlexander Lobakin 		dma_free_coherent(dev, chain->pbl_sp.table_size,
1259b6ee3cfSAlexander Lobakin 				  chain->pbl_sp.table_virt,
1269b6ee3cfSAlexander Lobakin 				  chain->pbl_sp.table_phys);
127a08c9b2cSAlexander Lobakin 
128a08c9b2cSAlexander Lobakin 	vfree(chain->pbl.pp_addr_tbl);
129a08c9b2cSAlexander Lobakin 	chain->pbl.pp_addr_tbl = NULL;
130a08c9b2cSAlexander Lobakin }
131a08c9b2cSAlexander Lobakin 
132a08c9b2cSAlexander Lobakin /**
133a08c9b2cSAlexander Lobakin  * qed_chain_free() - Free chain DMA memory.
134a08c9b2cSAlexander Lobakin  *
135a08c9b2cSAlexander Lobakin  * @cdev: Main device structure.
136a08c9b2cSAlexander Lobakin  * @chain: Chain to free.
137a08c9b2cSAlexander Lobakin  */
qed_chain_free(struct qed_dev * cdev,struct qed_chain * chain)138a08c9b2cSAlexander Lobakin void qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain)
139a08c9b2cSAlexander Lobakin {
140a08c9b2cSAlexander Lobakin 	switch (chain->mode) {
141a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_NEXT_PTR:
142a08c9b2cSAlexander Lobakin 		qed_chain_free_next_ptr(cdev, chain);
143a08c9b2cSAlexander Lobakin 		break;
144a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_SINGLE:
145a08c9b2cSAlexander Lobakin 		qed_chain_free_single(cdev, chain);
146a08c9b2cSAlexander Lobakin 		break;
147a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_PBL:
148a08c9b2cSAlexander Lobakin 		qed_chain_free_pbl(cdev, chain);
149a08c9b2cSAlexander Lobakin 		break;
150a08c9b2cSAlexander Lobakin 	default:
15196ca4c50SAlexander Lobakin 		return;
152a08c9b2cSAlexander Lobakin 	}
15396ca4c50SAlexander Lobakin 
15496ca4c50SAlexander Lobakin 	qed_chain_init_mem(chain, NULL, 0);
155a08c9b2cSAlexander Lobakin }
156a08c9b2cSAlexander Lobakin 
157a08c9b2cSAlexander Lobakin static int
qed_chain_alloc_sanity_check(struct qed_dev * cdev,const struct qed_chain_init_params * params,u32 page_cnt)158a08c9b2cSAlexander Lobakin qed_chain_alloc_sanity_check(struct qed_dev *cdev,
159b6db3f71SAlexander Lobakin 			     const struct qed_chain_init_params *params,
160b6db3f71SAlexander Lobakin 			     u32 page_cnt)
161a08c9b2cSAlexander Lobakin {
162b6db3f71SAlexander Lobakin 	u64 chain_size;
163b6db3f71SAlexander Lobakin 
16415506586SAlexander Lobakin 	chain_size = ELEMS_PER_PAGE(params->elem_size, params->page_size);
165b6db3f71SAlexander Lobakin 	chain_size *= page_cnt;
166b6db3f71SAlexander Lobakin 
167b6db3f71SAlexander Lobakin 	if (!chain_size)
168b6db3f71SAlexander Lobakin 		return -EINVAL;
169a08c9b2cSAlexander Lobakin 
170a08c9b2cSAlexander Lobakin 	/* The actual chain size can be larger than the maximal possible value
171a08c9b2cSAlexander Lobakin 	 * after rounding up the requested elements number to pages, and after
172a08c9b2cSAlexander Lobakin 	 * taking into account the unusuable elements (next-ptr elements).
173a08c9b2cSAlexander Lobakin 	 * The size of a "u16" chain can be (U16_MAX + 1) since the chain
174a08c9b2cSAlexander Lobakin 	 * size/capacity fields are of u32 type.
175a08c9b2cSAlexander Lobakin 	 */
176b6db3f71SAlexander Lobakin 	switch (params->cnt_type) {
177a08c9b2cSAlexander Lobakin 	case QED_CHAIN_CNT_TYPE_U16:
178a08c9b2cSAlexander Lobakin 		if (chain_size > U16_MAX + 1)
179a08c9b2cSAlexander Lobakin 			break;
180a08c9b2cSAlexander Lobakin 
181a08c9b2cSAlexander Lobakin 		return 0;
182a08c9b2cSAlexander Lobakin 	case QED_CHAIN_CNT_TYPE_U32:
183a08c9b2cSAlexander Lobakin 		if (chain_size > U32_MAX)
184a08c9b2cSAlexander Lobakin 			break;
185a08c9b2cSAlexander Lobakin 
186a08c9b2cSAlexander Lobakin 		return 0;
187a08c9b2cSAlexander Lobakin 	default:
188a08c9b2cSAlexander Lobakin 		return -EINVAL;
189a08c9b2cSAlexander Lobakin 	}
190a08c9b2cSAlexander Lobakin 
191a08c9b2cSAlexander Lobakin 	DP_NOTICE(cdev,
192a08c9b2cSAlexander Lobakin 		  "The actual chain size (0x%llx) is larger than the maximal possible value\n",
193a08c9b2cSAlexander Lobakin 		  chain_size);
194a08c9b2cSAlexander Lobakin 
195a08c9b2cSAlexander Lobakin 	return -EINVAL;
196a08c9b2cSAlexander Lobakin }
197a08c9b2cSAlexander Lobakin 
qed_chain_alloc_next_ptr(struct qed_dev * cdev,struct qed_chain * chain)198a08c9b2cSAlexander Lobakin static int qed_chain_alloc_next_ptr(struct qed_dev *cdev,
199a08c9b2cSAlexander Lobakin 				    struct qed_chain *chain)
200a08c9b2cSAlexander Lobakin {
201a08c9b2cSAlexander Lobakin 	struct device *dev = &cdev->pdev->dev;
202a08c9b2cSAlexander Lobakin 	void *virt, *virt_prev = NULL;
203a08c9b2cSAlexander Lobakin 	dma_addr_t phys;
204a08c9b2cSAlexander Lobakin 	u32 i;
205a08c9b2cSAlexander Lobakin 
206a08c9b2cSAlexander Lobakin 	for (i = 0; i < chain->page_cnt; i++) {
20715506586SAlexander Lobakin 		virt = dma_alloc_coherent(dev, chain->page_size, &phys,
208a08c9b2cSAlexander Lobakin 					  GFP_KERNEL);
209a08c9b2cSAlexander Lobakin 		if (!virt)
210a08c9b2cSAlexander Lobakin 			return -ENOMEM;
211a08c9b2cSAlexander Lobakin 
212a08c9b2cSAlexander Lobakin 		if (i == 0) {
213a08c9b2cSAlexander Lobakin 			qed_chain_init_mem(chain, virt, phys);
214a08c9b2cSAlexander Lobakin 			qed_chain_reset(chain);
215a08c9b2cSAlexander Lobakin 		} else {
216a08c9b2cSAlexander Lobakin 			qed_chain_init_next_ptr_elem(chain, virt_prev, virt,
217a08c9b2cSAlexander Lobakin 						     phys);
218a08c9b2cSAlexander Lobakin 		}
219a08c9b2cSAlexander Lobakin 
220a08c9b2cSAlexander Lobakin 		virt_prev = virt;
221a08c9b2cSAlexander Lobakin 	}
222a08c9b2cSAlexander Lobakin 
223a08c9b2cSAlexander Lobakin 	/* Last page's next element should point to the beginning of the
224a08c9b2cSAlexander Lobakin 	 * chain.
225a08c9b2cSAlexander Lobakin 	 */
226a08c9b2cSAlexander Lobakin 	qed_chain_init_next_ptr_elem(chain, virt_prev, chain->p_virt_addr,
227a08c9b2cSAlexander Lobakin 				     chain->p_phys_addr);
228a08c9b2cSAlexander Lobakin 
229a08c9b2cSAlexander Lobakin 	return 0;
230a08c9b2cSAlexander Lobakin }
231a08c9b2cSAlexander Lobakin 
qed_chain_alloc_single(struct qed_dev * cdev,struct qed_chain * chain)232a08c9b2cSAlexander Lobakin static int qed_chain_alloc_single(struct qed_dev *cdev,
233a08c9b2cSAlexander Lobakin 				  struct qed_chain *chain)
234a08c9b2cSAlexander Lobakin {
235a08c9b2cSAlexander Lobakin 	dma_addr_t phys;
236a08c9b2cSAlexander Lobakin 	void *virt;
237a08c9b2cSAlexander Lobakin 
23815506586SAlexander Lobakin 	virt = dma_alloc_coherent(&cdev->pdev->dev, chain->page_size,
239a08c9b2cSAlexander Lobakin 				  &phys, GFP_KERNEL);
240a08c9b2cSAlexander Lobakin 	if (!virt)
241a08c9b2cSAlexander Lobakin 		return -ENOMEM;
242a08c9b2cSAlexander Lobakin 
243a08c9b2cSAlexander Lobakin 	qed_chain_init_mem(chain, virt, phys);
244a08c9b2cSAlexander Lobakin 	qed_chain_reset(chain);
245a08c9b2cSAlexander Lobakin 
246a08c9b2cSAlexander Lobakin 	return 0;
247a08c9b2cSAlexander Lobakin }
248a08c9b2cSAlexander Lobakin 
qed_chain_alloc_pbl(struct qed_dev * cdev,struct qed_chain * chain)249c3a321b0SAlexander Lobakin static int qed_chain_alloc_pbl(struct qed_dev *cdev, struct qed_chain *chain)
250a08c9b2cSAlexander Lobakin {
251a08c9b2cSAlexander Lobakin 	struct device *dev = &cdev->pdev->dev;
252a08c9b2cSAlexander Lobakin 	struct addr_tbl_entry *addr_tbl;
253a08c9b2cSAlexander Lobakin 	dma_addr_t phys, pbl_phys;
2549b6ee3cfSAlexander Lobakin 	__le64 *pbl_virt;
255a08c9b2cSAlexander Lobakin 	u32 page_cnt, i;
256a08c9b2cSAlexander Lobakin 	size_t size;
257a08c9b2cSAlexander Lobakin 	void *virt;
258a08c9b2cSAlexander Lobakin 
259a08c9b2cSAlexander Lobakin 	page_cnt = chain->page_cnt;
260a08c9b2cSAlexander Lobakin 
261a08c9b2cSAlexander Lobakin 	size = array_size(page_cnt, sizeof(*addr_tbl));
262a08c9b2cSAlexander Lobakin 	if (unlikely(size == SIZE_MAX))
263a08c9b2cSAlexander Lobakin 		return -EOVERFLOW;
264a08c9b2cSAlexander Lobakin 
265a08c9b2cSAlexander Lobakin 	addr_tbl = vzalloc(size);
266a08c9b2cSAlexander Lobakin 	if (!addr_tbl)
267a08c9b2cSAlexander Lobakin 		return -ENOMEM;
268a08c9b2cSAlexander Lobakin 
269a08c9b2cSAlexander Lobakin 	chain->pbl.pp_addr_tbl = addr_tbl;
270a08c9b2cSAlexander Lobakin 
2711775da47SAlexander Lobakin 	if (chain->b_external_pbl) {
2721775da47SAlexander Lobakin 		pbl_virt = chain->pbl_sp.table_virt;
273c3a321b0SAlexander Lobakin 		goto alloc_pages;
2741775da47SAlexander Lobakin 	}
275a08c9b2cSAlexander Lobakin 
2769b6ee3cfSAlexander Lobakin 	size = array_size(page_cnt, sizeof(*pbl_virt));
277a08c9b2cSAlexander Lobakin 	if (unlikely(size == SIZE_MAX))
278a08c9b2cSAlexander Lobakin 		return -EOVERFLOW;
279a08c9b2cSAlexander Lobakin 
280c3a321b0SAlexander Lobakin 	pbl_virt = dma_alloc_coherent(dev, size, &pbl_phys, GFP_KERNEL);
281a08c9b2cSAlexander Lobakin 	if (!pbl_virt)
282a08c9b2cSAlexander Lobakin 		return -ENOMEM;
283a08c9b2cSAlexander Lobakin 
2849b6ee3cfSAlexander Lobakin 	chain->pbl_sp.table_virt = pbl_virt;
2859b6ee3cfSAlexander Lobakin 	chain->pbl_sp.table_phys = pbl_phys;
2869b6ee3cfSAlexander Lobakin 	chain->pbl_sp.table_size = size;
287a08c9b2cSAlexander Lobakin 
288c3a321b0SAlexander Lobakin alloc_pages:
289a08c9b2cSAlexander Lobakin 	for (i = 0; i < page_cnt; i++) {
29015506586SAlexander Lobakin 		virt = dma_alloc_coherent(dev, chain->page_size, &phys,
291a08c9b2cSAlexander Lobakin 					  GFP_KERNEL);
292a08c9b2cSAlexander Lobakin 		if (!virt)
293a08c9b2cSAlexander Lobakin 			return -ENOMEM;
294a08c9b2cSAlexander Lobakin 
295a08c9b2cSAlexander Lobakin 		if (i == 0) {
296a08c9b2cSAlexander Lobakin 			qed_chain_init_mem(chain, virt, phys);
297a08c9b2cSAlexander Lobakin 			qed_chain_reset(chain);
298a08c9b2cSAlexander Lobakin 		}
299a08c9b2cSAlexander Lobakin 
300a08c9b2cSAlexander Lobakin 		/* Fill the PBL table with the physical address of the page */
3019b6ee3cfSAlexander Lobakin 		pbl_virt[i] = cpu_to_le64(phys);
302a08c9b2cSAlexander Lobakin 
303a08c9b2cSAlexander Lobakin 		/* Keep the virtual address of the page */
304a08c9b2cSAlexander Lobakin 		addr_tbl[i].virt_addr = virt;
305a08c9b2cSAlexander Lobakin 		addr_tbl[i].dma_map = phys;
306a08c9b2cSAlexander Lobakin 	}
307a08c9b2cSAlexander Lobakin 
308a08c9b2cSAlexander Lobakin 	return 0;
309a08c9b2cSAlexander Lobakin }
310a08c9b2cSAlexander Lobakin 
311b6db3f71SAlexander Lobakin /**
312b6db3f71SAlexander Lobakin  * qed_chain_alloc() - Allocate and initialize a chain.
313b6db3f71SAlexander Lobakin  *
314b6db3f71SAlexander Lobakin  * @cdev: Main device structure.
315b6db3f71SAlexander Lobakin  * @chain: Chain to be processed.
316b6db3f71SAlexander Lobakin  * @params: Chain initialization parameters.
317b6db3f71SAlexander Lobakin  *
318b6db3f71SAlexander Lobakin  * Return: 0 on success, negative errno otherwise.
319b6db3f71SAlexander Lobakin  */
qed_chain_alloc(struct qed_dev * cdev,struct qed_chain * chain,struct qed_chain_init_params * params)320b6db3f71SAlexander Lobakin int qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain,
321b6db3f71SAlexander Lobakin 		    struct qed_chain_init_params *params)
322a08c9b2cSAlexander Lobakin {
323a08c9b2cSAlexander Lobakin 	u32 page_cnt;
324a08c9b2cSAlexander Lobakin 	int rc;
325a08c9b2cSAlexander Lobakin 
32615506586SAlexander Lobakin 	if (!params->page_size)
32715506586SAlexander Lobakin 		params->page_size = QED_CHAIN_PAGE_SIZE;
32815506586SAlexander Lobakin 
329b6db3f71SAlexander Lobakin 	if (params->mode == QED_CHAIN_MODE_SINGLE)
330a08c9b2cSAlexander Lobakin 		page_cnt = 1;
331a08c9b2cSAlexander Lobakin 	else
332b6db3f71SAlexander Lobakin 		page_cnt = QED_CHAIN_PAGE_CNT(params->num_elems,
333b6db3f71SAlexander Lobakin 					      params->elem_size,
33415506586SAlexander Lobakin 					      params->page_size,
335b6db3f71SAlexander Lobakin 					      params->mode);
336a08c9b2cSAlexander Lobakin 
337b6db3f71SAlexander Lobakin 	rc = qed_chain_alloc_sanity_check(cdev, params, page_cnt);
338a08c9b2cSAlexander Lobakin 	if (rc) {
339a08c9b2cSAlexander Lobakin 		DP_NOTICE(cdev,
340a08c9b2cSAlexander Lobakin 			  "Cannot allocate a chain with the given arguments:\n");
341a08c9b2cSAlexander Lobakin 		DP_NOTICE(cdev,
34215506586SAlexander Lobakin 			  "[use_mode %d, mode %d, cnt_type %d, num_elems %d, elem_size %zu, page_size %u]\n",
343b6db3f71SAlexander Lobakin 			  params->intended_use, params->mode, params->cnt_type,
34415506586SAlexander Lobakin 			  params->num_elems, params->elem_size,
34515506586SAlexander Lobakin 			  params->page_size);
346a08c9b2cSAlexander Lobakin 		return rc;
347a08c9b2cSAlexander Lobakin 	}
348a08c9b2cSAlexander Lobakin 
349b6db3f71SAlexander Lobakin 	qed_chain_init(chain, params, page_cnt);
350a08c9b2cSAlexander Lobakin 
351b6db3f71SAlexander Lobakin 	switch (params->mode) {
352a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_NEXT_PTR:
353a08c9b2cSAlexander Lobakin 		rc = qed_chain_alloc_next_ptr(cdev, chain);
354a08c9b2cSAlexander Lobakin 		break;
355a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_SINGLE:
356a08c9b2cSAlexander Lobakin 		rc = qed_chain_alloc_single(cdev, chain);
357a08c9b2cSAlexander Lobakin 		break;
358a08c9b2cSAlexander Lobakin 	case QED_CHAIN_MODE_PBL:
359c3a321b0SAlexander Lobakin 		rc = qed_chain_alloc_pbl(cdev, chain);
360a08c9b2cSAlexander Lobakin 		break;
361a08c9b2cSAlexander Lobakin 	default:
362a08c9b2cSAlexander Lobakin 		return -EINVAL;
363a08c9b2cSAlexander Lobakin 	}
364a08c9b2cSAlexander Lobakin 
365a08c9b2cSAlexander Lobakin 	if (!rc)
366a08c9b2cSAlexander Lobakin 		return 0;
367a08c9b2cSAlexander Lobakin 
368a08c9b2cSAlexander Lobakin 	qed_chain_free(cdev, chain);
369a08c9b2cSAlexander Lobakin 
370a08c9b2cSAlexander Lobakin 	return rc;
371a08c9b2cSAlexander Lobakin }
372