18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci/* Copyright (c) 2020 Marvell International Ltd. */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
58c2ecf20Sopenharmony_ci#include <linux/qed/qed_chain.h>
68c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "qed_dev_api.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_cistatic void qed_chain_init(struct qed_chain *chain,
118c2ecf20Sopenharmony_ci			   const struct qed_chain_init_params *params,
128c2ecf20Sopenharmony_ci			   u32 page_cnt)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	memset(chain, 0, sizeof(*chain));
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	chain->elem_size = params->elem_size;
178c2ecf20Sopenharmony_ci	chain->intended_use = params->intended_use;
188c2ecf20Sopenharmony_ci	chain->mode = params->mode;
198c2ecf20Sopenharmony_ci	chain->cnt_type = params->cnt_type;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	chain->elem_per_page = ELEMS_PER_PAGE(params->elem_size,
228c2ecf20Sopenharmony_ci					      params->page_size);
238c2ecf20Sopenharmony_ci	chain->usable_per_page = USABLE_ELEMS_PER_PAGE(params->elem_size,
248c2ecf20Sopenharmony_ci						       params->page_size,
258c2ecf20Sopenharmony_ci						       params->mode);
268c2ecf20Sopenharmony_ci	chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(params->elem_size,
278c2ecf20Sopenharmony_ci						       params->mode);
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	chain->elem_per_page_mask = chain->elem_per_page - 1;
308c2ecf20Sopenharmony_ci	chain->next_page_mask = chain->usable_per_page &
318c2ecf20Sopenharmony_ci				chain->elem_per_page_mask;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	chain->page_size = params->page_size;
348c2ecf20Sopenharmony_ci	chain->page_cnt = page_cnt;
358c2ecf20Sopenharmony_ci	chain->capacity = chain->usable_per_page * page_cnt;
368c2ecf20Sopenharmony_ci	chain->size = chain->elem_per_page * page_cnt;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (params->ext_pbl_virt) {
398c2ecf20Sopenharmony_ci		chain->pbl_sp.table_virt = params->ext_pbl_virt;
408c2ecf20Sopenharmony_ci		chain->pbl_sp.table_phys = params->ext_pbl_phys;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		chain->b_external_pbl = true;
438c2ecf20Sopenharmony_ci	}
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void qed_chain_init_next_ptr_elem(const struct qed_chain *chain,
478c2ecf20Sopenharmony_ci					 void *virt_curr, void *virt_next,
488c2ecf20Sopenharmony_ci					 dma_addr_t phys_next)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct qed_chain_next *next;
518c2ecf20Sopenharmony_ci	u32 size;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	size = chain->elem_size * chain->usable_per_page;
548c2ecf20Sopenharmony_ci	next = virt_curr + size;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	DMA_REGPAIR_LE(next->next_phys, phys_next);
578c2ecf20Sopenharmony_ci	next->next_virt = virt_next;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic void qed_chain_init_mem(struct qed_chain *chain, void *virt_addr,
618c2ecf20Sopenharmony_ci			       dma_addr_t phys_addr)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	chain->p_virt_addr = virt_addr;
648c2ecf20Sopenharmony_ci	chain->p_phys_addr = phys_addr;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void qed_chain_free_next_ptr(struct qed_dev *cdev,
688c2ecf20Sopenharmony_ci				    struct qed_chain *chain)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct device *dev = &cdev->pdev->dev;
718c2ecf20Sopenharmony_ci	struct qed_chain_next *next;
728c2ecf20Sopenharmony_ci	dma_addr_t phys, phys_next;
738c2ecf20Sopenharmony_ci	void *virt, *virt_next;
748c2ecf20Sopenharmony_ci	u32 size, i;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	size = chain->elem_size * chain->usable_per_page;
778c2ecf20Sopenharmony_ci	virt = chain->p_virt_addr;
788c2ecf20Sopenharmony_ci	phys = chain->p_phys_addr;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	for (i = 0; i < chain->page_cnt; i++) {
818c2ecf20Sopenharmony_ci		if (!virt)
828c2ecf20Sopenharmony_ci			break;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		next = virt + size;
858c2ecf20Sopenharmony_ci		virt_next = next->next_virt;
868c2ecf20Sopenharmony_ci		phys_next = HILO_DMA_REGPAIR(next->next_phys);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		dma_free_coherent(dev, chain->page_size, virt, phys);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		virt = virt_next;
918c2ecf20Sopenharmony_ci		phys = phys_next;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void qed_chain_free_single(struct qed_dev *cdev,
968c2ecf20Sopenharmony_ci				  struct qed_chain *chain)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	if (!chain->p_virt_addr)
998c2ecf20Sopenharmony_ci		return;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	dma_free_coherent(&cdev->pdev->dev, chain->page_size,
1028c2ecf20Sopenharmony_ci			  chain->p_virt_addr, chain->p_phys_addr);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *chain)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct device *dev = &cdev->pdev->dev;
1088c2ecf20Sopenharmony_ci	struct addr_tbl_entry *entry;
1098c2ecf20Sopenharmony_ci	u32 i;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (!chain->pbl.pp_addr_tbl)
1128c2ecf20Sopenharmony_ci		return;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	for (i = 0; i < chain->page_cnt; i++) {
1158c2ecf20Sopenharmony_ci		entry = chain->pbl.pp_addr_tbl + i;
1168c2ecf20Sopenharmony_ci		if (!entry->virt_addr)
1178c2ecf20Sopenharmony_ci			break;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		dma_free_coherent(dev, chain->page_size, entry->virt_addr,
1208c2ecf20Sopenharmony_ci				  entry->dma_map);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!chain->b_external_pbl)
1248c2ecf20Sopenharmony_ci		dma_free_coherent(dev, chain->pbl_sp.table_size,
1258c2ecf20Sopenharmony_ci				  chain->pbl_sp.table_virt,
1268c2ecf20Sopenharmony_ci				  chain->pbl_sp.table_phys);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	vfree(chain->pbl.pp_addr_tbl);
1298c2ecf20Sopenharmony_ci	chain->pbl.pp_addr_tbl = NULL;
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/**
1338c2ecf20Sopenharmony_ci * qed_chain_free() - Free chain DMA memory.
1348c2ecf20Sopenharmony_ci *
1358c2ecf20Sopenharmony_ci * @cdev: Main device structure.
1368c2ecf20Sopenharmony_ci * @chain: Chain to free.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_civoid qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	switch (chain->mode) {
1418c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_NEXT_PTR:
1428c2ecf20Sopenharmony_ci		qed_chain_free_next_ptr(cdev, chain);
1438c2ecf20Sopenharmony_ci		break;
1448c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_SINGLE:
1458c2ecf20Sopenharmony_ci		qed_chain_free_single(cdev, chain);
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_PBL:
1488c2ecf20Sopenharmony_ci		qed_chain_free_pbl(cdev, chain);
1498c2ecf20Sopenharmony_ci		break;
1508c2ecf20Sopenharmony_ci	default:
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	qed_chain_init_mem(chain, NULL, 0);
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_cistatic int
1588c2ecf20Sopenharmony_ciqed_chain_alloc_sanity_check(struct qed_dev *cdev,
1598c2ecf20Sopenharmony_ci			     const struct qed_chain_init_params *params,
1608c2ecf20Sopenharmony_ci			     u32 page_cnt)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	u64 chain_size;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	chain_size = ELEMS_PER_PAGE(params->elem_size, params->page_size);
1658c2ecf20Sopenharmony_ci	chain_size *= page_cnt;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	if (!chain_size)
1688c2ecf20Sopenharmony_ci		return -EINVAL;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* The actual chain size can be larger than the maximal possible value
1718c2ecf20Sopenharmony_ci	 * after rounding up the requested elements number to pages, and after
1728c2ecf20Sopenharmony_ci	 * taking into account the unusuable elements (next-ptr elements).
1738c2ecf20Sopenharmony_ci	 * The size of a "u16" chain can be (U16_MAX + 1) since the chain
1748c2ecf20Sopenharmony_ci	 * size/capacity fields are of u32 type.
1758c2ecf20Sopenharmony_ci	 */
1768c2ecf20Sopenharmony_ci	switch (params->cnt_type) {
1778c2ecf20Sopenharmony_ci	case QED_CHAIN_CNT_TYPE_U16:
1788c2ecf20Sopenharmony_ci		if (chain_size > U16_MAX + 1)
1798c2ecf20Sopenharmony_ci			break;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		return 0;
1828c2ecf20Sopenharmony_ci	case QED_CHAIN_CNT_TYPE_U32:
1838c2ecf20Sopenharmony_ci		if (chain_size > U32_MAX)
1848c2ecf20Sopenharmony_ci			break;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		return 0;
1878c2ecf20Sopenharmony_ci	default:
1888c2ecf20Sopenharmony_ci		return -EINVAL;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	DP_NOTICE(cdev,
1928c2ecf20Sopenharmony_ci		  "The actual chain size (0x%llx) is larger than the maximal possible value\n",
1938c2ecf20Sopenharmony_ci		  chain_size);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return -EINVAL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int qed_chain_alloc_next_ptr(struct qed_dev *cdev,
1998c2ecf20Sopenharmony_ci				    struct qed_chain *chain)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct device *dev = &cdev->pdev->dev;
2028c2ecf20Sopenharmony_ci	void *virt, *virt_prev = NULL;
2038c2ecf20Sopenharmony_ci	dma_addr_t phys;
2048c2ecf20Sopenharmony_ci	u32 i;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (i = 0; i < chain->page_cnt; i++) {
2078c2ecf20Sopenharmony_ci		virt = dma_alloc_coherent(dev, chain->page_size, &phys,
2088c2ecf20Sopenharmony_ci					  GFP_KERNEL);
2098c2ecf20Sopenharmony_ci		if (!virt)
2108c2ecf20Sopenharmony_ci			return -ENOMEM;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (i == 0) {
2138c2ecf20Sopenharmony_ci			qed_chain_init_mem(chain, virt, phys);
2148c2ecf20Sopenharmony_ci			qed_chain_reset(chain);
2158c2ecf20Sopenharmony_ci		} else {
2168c2ecf20Sopenharmony_ci			qed_chain_init_next_ptr_elem(chain, virt_prev, virt,
2178c2ecf20Sopenharmony_ci						     phys);
2188c2ecf20Sopenharmony_ci		}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		virt_prev = virt;
2218c2ecf20Sopenharmony_ci	}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	/* Last page's next element should point to the beginning of the
2248c2ecf20Sopenharmony_ci	 * chain.
2258c2ecf20Sopenharmony_ci	 */
2268c2ecf20Sopenharmony_ci	qed_chain_init_next_ptr_elem(chain, virt_prev, chain->p_virt_addr,
2278c2ecf20Sopenharmony_ci				     chain->p_phys_addr);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	return 0;
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cistatic int qed_chain_alloc_single(struct qed_dev *cdev,
2338c2ecf20Sopenharmony_ci				  struct qed_chain *chain)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	dma_addr_t phys;
2368c2ecf20Sopenharmony_ci	void *virt;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	virt = dma_alloc_coherent(&cdev->pdev->dev, chain->page_size,
2398c2ecf20Sopenharmony_ci				  &phys, GFP_KERNEL);
2408c2ecf20Sopenharmony_ci	if (!virt)
2418c2ecf20Sopenharmony_ci		return -ENOMEM;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	qed_chain_init_mem(chain, virt, phys);
2448c2ecf20Sopenharmony_ci	qed_chain_reset(chain);
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int qed_chain_alloc_pbl(struct qed_dev *cdev, struct qed_chain *chain)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct device *dev = &cdev->pdev->dev;
2528c2ecf20Sopenharmony_ci	struct addr_tbl_entry *addr_tbl;
2538c2ecf20Sopenharmony_ci	dma_addr_t phys, pbl_phys;
2548c2ecf20Sopenharmony_ci	__le64 *pbl_virt;
2558c2ecf20Sopenharmony_ci	u32 page_cnt, i;
2568c2ecf20Sopenharmony_ci	size_t size;
2578c2ecf20Sopenharmony_ci	void *virt;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	page_cnt = chain->page_cnt;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	size = array_size(page_cnt, sizeof(*addr_tbl));
2628c2ecf20Sopenharmony_ci	if (unlikely(size == SIZE_MAX))
2638c2ecf20Sopenharmony_ci		return -EOVERFLOW;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	addr_tbl = vzalloc(size);
2668c2ecf20Sopenharmony_ci	if (!addr_tbl)
2678c2ecf20Sopenharmony_ci		return -ENOMEM;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	chain->pbl.pp_addr_tbl = addr_tbl;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (chain->b_external_pbl) {
2728c2ecf20Sopenharmony_ci		pbl_virt = chain->pbl_sp.table_virt;
2738c2ecf20Sopenharmony_ci		goto alloc_pages;
2748c2ecf20Sopenharmony_ci	}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	size = array_size(page_cnt, sizeof(*pbl_virt));
2778c2ecf20Sopenharmony_ci	if (unlikely(size == SIZE_MAX))
2788c2ecf20Sopenharmony_ci		return -EOVERFLOW;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	pbl_virt = dma_alloc_coherent(dev, size, &pbl_phys, GFP_KERNEL);
2818c2ecf20Sopenharmony_ci	if (!pbl_virt)
2828c2ecf20Sopenharmony_ci		return -ENOMEM;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	chain->pbl_sp.table_virt = pbl_virt;
2858c2ecf20Sopenharmony_ci	chain->pbl_sp.table_phys = pbl_phys;
2868c2ecf20Sopenharmony_ci	chain->pbl_sp.table_size = size;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_cialloc_pages:
2898c2ecf20Sopenharmony_ci	for (i = 0; i < page_cnt; i++) {
2908c2ecf20Sopenharmony_ci		virt = dma_alloc_coherent(dev, chain->page_size, &phys,
2918c2ecf20Sopenharmony_ci					  GFP_KERNEL);
2928c2ecf20Sopenharmony_ci		if (!virt)
2938c2ecf20Sopenharmony_ci			return -ENOMEM;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		if (i == 0) {
2968c2ecf20Sopenharmony_ci			qed_chain_init_mem(chain, virt, phys);
2978c2ecf20Sopenharmony_ci			qed_chain_reset(chain);
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		/* Fill the PBL table with the physical address of the page */
3018c2ecf20Sopenharmony_ci		pbl_virt[i] = cpu_to_le64(phys);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		/* Keep the virtual address of the page */
3048c2ecf20Sopenharmony_ci		addr_tbl[i].virt_addr = virt;
3058c2ecf20Sopenharmony_ci		addr_tbl[i].dma_map = phys;
3068c2ecf20Sopenharmony_ci	}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	return 0;
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci/**
3128c2ecf20Sopenharmony_ci * qed_chain_alloc() - Allocate and initialize a chain.
3138c2ecf20Sopenharmony_ci *
3148c2ecf20Sopenharmony_ci * @cdev: Main device structure.
3158c2ecf20Sopenharmony_ci * @chain: Chain to be processed.
3168c2ecf20Sopenharmony_ci * @params: Chain initialization parameters.
3178c2ecf20Sopenharmony_ci *
3188c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno otherwise.
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_ciint qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain,
3218c2ecf20Sopenharmony_ci		    struct qed_chain_init_params *params)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	u32 page_cnt;
3248c2ecf20Sopenharmony_ci	int rc;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (!params->page_size)
3278c2ecf20Sopenharmony_ci		params->page_size = QED_CHAIN_PAGE_SIZE;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (params->mode == QED_CHAIN_MODE_SINGLE)
3308c2ecf20Sopenharmony_ci		page_cnt = 1;
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		page_cnt = QED_CHAIN_PAGE_CNT(params->num_elems,
3338c2ecf20Sopenharmony_ci					      params->elem_size,
3348c2ecf20Sopenharmony_ci					      params->page_size,
3358c2ecf20Sopenharmony_ci					      params->mode);
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	rc = qed_chain_alloc_sanity_check(cdev, params, page_cnt);
3388c2ecf20Sopenharmony_ci	if (rc) {
3398c2ecf20Sopenharmony_ci		DP_NOTICE(cdev,
3408c2ecf20Sopenharmony_ci			  "Cannot allocate a chain with the given arguments:\n");
3418c2ecf20Sopenharmony_ci		DP_NOTICE(cdev,
3428c2ecf20Sopenharmony_ci			  "[use_mode %d, mode %d, cnt_type %d, num_elems %d, elem_size %zu, page_size %u]\n",
3438c2ecf20Sopenharmony_ci			  params->intended_use, params->mode, params->cnt_type,
3448c2ecf20Sopenharmony_ci			  params->num_elems, params->elem_size,
3458c2ecf20Sopenharmony_ci			  params->page_size);
3468c2ecf20Sopenharmony_ci		return rc;
3478c2ecf20Sopenharmony_ci	}
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	qed_chain_init(chain, params, page_cnt);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	switch (params->mode) {
3528c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_NEXT_PTR:
3538c2ecf20Sopenharmony_ci		rc = qed_chain_alloc_next_ptr(cdev, chain);
3548c2ecf20Sopenharmony_ci		break;
3558c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_SINGLE:
3568c2ecf20Sopenharmony_ci		rc = qed_chain_alloc_single(cdev, chain);
3578c2ecf20Sopenharmony_ci		break;
3588c2ecf20Sopenharmony_ci	case QED_CHAIN_MODE_PBL:
3598c2ecf20Sopenharmony_ci		rc = qed_chain_alloc_pbl(cdev, chain);
3608c2ecf20Sopenharmony_ci		break;
3618c2ecf20Sopenharmony_ci	default:
3628c2ecf20Sopenharmony_ci		return -EINVAL;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (!rc)
3668c2ecf20Sopenharmony_ci		return 0;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	qed_chain_free(cdev, chain);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return rc;
3718c2ecf20Sopenharmony_ci}
372