| // SPDX-License-Identifier: GPL-2.0 | 
 | /* Copyright (c) 2019 HiSilicon Limited. */ | 
 | #include <crypto/internal/acompress.h> | 
 | #include <linux/bitfield.h> | 
 | #include <linux/dma-mapping.h> | 
 | #include <linux/scatterlist.h> | 
 | #include "zip.h" | 
 |  | 
 | /* hisi_zip_sqe dw3 */ | 
 | #define HZIP_BD_STATUS_M			GENMASK(7, 0) | 
 | /* hisi_zip_sqe dw7 */ | 
 | #define HZIP_IN_SGE_DATA_OFFSET_M		GENMASK(23, 0) | 
 | #define HZIP_SQE_TYPE_M				GENMASK(31, 28) | 
 | /* hisi_zip_sqe dw8 */ | 
 | #define HZIP_OUT_SGE_DATA_OFFSET_M		GENMASK(23, 0) | 
 | /* hisi_zip_sqe dw9 */ | 
 | #define HZIP_REQ_TYPE_M				GENMASK(7, 0) | 
 | #define HZIP_ALG_TYPE_ZLIB			0x02 | 
 | #define HZIP_ALG_TYPE_GZIP			0x03 | 
 | #define HZIP_BUF_TYPE_M				GENMASK(11, 8) | 
 | #define HZIP_PBUFFER				0x0 | 
 | #define HZIP_SGL				0x1 | 
 |  | 
 | #define HZIP_ZLIB_HEAD_SIZE			2 | 
 | #define HZIP_GZIP_HEAD_SIZE			10 | 
 |  | 
 | #define GZIP_HEAD_FHCRC_BIT			BIT(1) | 
 | #define GZIP_HEAD_FEXTRA_BIT			BIT(2) | 
 | #define GZIP_HEAD_FNAME_BIT			BIT(3) | 
 | #define GZIP_HEAD_FCOMMENT_BIT			BIT(4) | 
 |  | 
 | #define GZIP_HEAD_FLG_SHIFT			3 | 
 | #define GZIP_HEAD_FEXTRA_SHIFT			10 | 
 | #define GZIP_HEAD_FEXTRA_XLEN			2UL | 
 | #define GZIP_HEAD_FHCRC_SIZE			2 | 
 |  | 
 | #define HZIP_GZIP_HEAD_BUF			256 | 
 | #define HZIP_ALG_PRIORITY			300 | 
 | #define HZIP_SGL_SGE_NR				10 | 
 |  | 
 | static const u8 zlib_head[HZIP_ZLIB_HEAD_SIZE] = {0x78, 0x9c}; | 
 | static const u8 gzip_head[HZIP_GZIP_HEAD_SIZE] = { | 
 | 	0x1f, 0x8b, 0x08, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x03 | 
 | }; | 
 |  | 
 | enum hisi_zip_alg_type { | 
 | 	HZIP_ALG_TYPE_COMP = 0, | 
 | 	HZIP_ALG_TYPE_DECOMP = 1, | 
 | }; | 
 |  | 
 | enum { | 
 | 	HZIP_QPC_COMP, | 
 | 	HZIP_QPC_DECOMP, | 
 | 	HZIP_CTX_Q_NUM | 
 | }; | 
 |  | 
 | #define COMP_NAME_TO_TYPE(alg_name)					\ | 
 | 	(!strcmp((alg_name), "zlib-deflate") ? HZIP_ALG_TYPE_ZLIB :	\ | 
 | 	 !strcmp((alg_name), "gzip") ? HZIP_ALG_TYPE_GZIP : 0)		\ | 
 |  | 
 | #define TO_HEAD_SIZE(req_type)						\ | 
 | 	(((req_type) == HZIP_ALG_TYPE_ZLIB) ? sizeof(zlib_head) :	\ | 
 | 	 ((req_type) == HZIP_ALG_TYPE_GZIP) ? sizeof(gzip_head) : 0)	\ | 
 |  | 
 | #define TO_HEAD(req_type)						\ | 
 | 	(((req_type) == HZIP_ALG_TYPE_ZLIB) ? zlib_head :		\ | 
 | 	 ((req_type) == HZIP_ALG_TYPE_GZIP) ? gzip_head : NULL)		\ | 
 |  | 
 | struct hisi_zip_req { | 
 | 	struct acomp_req *req; | 
 | 	u32 sskip; | 
 | 	u32 dskip; | 
 | 	struct hisi_acc_hw_sgl *hw_src; | 
 | 	struct hisi_acc_hw_sgl *hw_dst; | 
 | 	dma_addr_t dma_src; | 
 | 	dma_addr_t dma_dst; | 
 | 	u16 req_id; | 
 | }; | 
 |  | 
 | struct hisi_zip_req_q { | 
 | 	struct hisi_zip_req *q; | 
 | 	unsigned long *req_bitmap; | 
 | 	rwlock_t req_lock; | 
 | 	u16 size; | 
 | }; | 
 |  | 
 | struct hisi_zip_qp_ctx { | 
 | 	struct hisi_qp *qp; | 
 | 	struct hisi_zip_req_q req_q; | 
 | 	struct hisi_acc_sgl_pool *sgl_pool; | 
 | 	struct hisi_zip *zip_dev; | 
 | 	struct hisi_zip_ctx *ctx; | 
 | }; | 
 |  | 
 | struct hisi_zip_sqe_ops { | 
 | 	u8 sqe_type; | 
 | 	void (*fill_addr)(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req); | 
 | 	void (*fill_buf_size)(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req); | 
 | 	void (*fill_buf_type)(struct hisi_zip_sqe *sqe, u8 buf_type); | 
 | 	void (*fill_req_type)(struct hisi_zip_sqe *sqe, u8 req_type); | 
 | 	void (*fill_tag)(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req); | 
 | 	void (*fill_sqe_type)(struct hisi_zip_sqe *sqe, u8 sqe_type); | 
 | 	u32 (*get_tag)(struct hisi_zip_sqe *sqe); | 
 | 	u32 (*get_status)(struct hisi_zip_sqe *sqe); | 
 | 	u32 (*get_dstlen)(struct hisi_zip_sqe *sqe); | 
 | }; | 
 |  | 
 | struct hisi_zip_ctx { | 
 | 	struct hisi_zip_qp_ctx qp_ctx[HZIP_CTX_Q_NUM]; | 
 | 	const struct hisi_zip_sqe_ops *ops; | 
 | }; | 
 |  | 
 | static int sgl_sge_nr_set(const char *val, const struct kernel_param *kp) | 
 | { | 
 | 	int ret; | 
 | 	u16 n; | 
 |  | 
 | 	if (!val) | 
 | 		return -EINVAL; | 
 |  | 
 | 	ret = kstrtou16(val, 10, &n); | 
 | 	if (ret || n == 0 || n > HISI_ACC_SGL_SGE_NR_MAX) | 
 | 		return -EINVAL; | 
 |  | 
 | 	return param_set_int(val, kp); | 
 | } | 
 |  | 
 | static const struct kernel_param_ops sgl_sge_nr_ops = { | 
 | 	.set = sgl_sge_nr_set, | 
 | 	.get = param_get_int, | 
 | }; | 
 |  | 
 | static u16 sgl_sge_nr = HZIP_SGL_SGE_NR; | 
 | module_param_cb(sgl_sge_nr, &sgl_sge_nr_ops, &sgl_sge_nr, 0444); | 
 | MODULE_PARM_DESC(sgl_sge_nr, "Number of sge in sgl(1-255)"); | 
 |  | 
 | static u16 get_extra_field_size(const u8 *start) | 
 | { | 
 | 	return *((u16 *)start) + GZIP_HEAD_FEXTRA_XLEN; | 
 | } | 
 |  | 
 | static u32 get_name_field_size(const u8 *start) | 
 | { | 
 | 	return strlen(start) + 1; | 
 | } | 
 |  | 
 | static u32 get_comment_field_size(const u8 *start) | 
 | { | 
 | 	return strlen(start) + 1; | 
 | } | 
 |  | 
 | static u32 __get_gzip_head_size(const u8 *src) | 
 | { | 
 | 	u8 head_flg = *(src + GZIP_HEAD_FLG_SHIFT); | 
 | 	u32 size = GZIP_HEAD_FEXTRA_SHIFT; | 
 |  | 
 | 	if (head_flg & GZIP_HEAD_FEXTRA_BIT) | 
 | 		size += get_extra_field_size(src + size); | 
 | 	if (head_flg & GZIP_HEAD_FNAME_BIT) | 
 | 		size += get_name_field_size(src + size); | 
 | 	if (head_flg & GZIP_HEAD_FCOMMENT_BIT) | 
 | 		size += get_comment_field_size(src + size); | 
 | 	if (head_flg & GZIP_HEAD_FHCRC_BIT) | 
 | 		size += GZIP_HEAD_FHCRC_SIZE; | 
 |  | 
 | 	return size; | 
 | } | 
 |  | 
 | static size_t __maybe_unused get_gzip_head_size(struct scatterlist *sgl) | 
 | { | 
 | 	char buf[HZIP_GZIP_HEAD_BUF]; | 
 |  | 
 | 	sg_copy_to_buffer(sgl, sg_nents(sgl), buf, sizeof(buf)); | 
 |  | 
 | 	return __get_gzip_head_size(buf); | 
 | } | 
 |  | 
 | static int add_comp_head(struct scatterlist *dst, u8 req_type) | 
 | { | 
 | 	int head_size = TO_HEAD_SIZE(req_type); | 
 | 	const u8 *head = TO_HEAD(req_type); | 
 | 	int ret; | 
 |  | 
 | 	ret = sg_copy_from_buffer(dst, sg_nents(dst), head, head_size); | 
 | 	if (ret != head_size) { | 
 | 		pr_err("the head size of buffer is wrong (%d)!\n", ret); | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	return head_size; | 
 | } | 
 |  | 
 | static int get_comp_head_size(struct acomp_req *acomp_req, u8 req_type) | 
 | { | 
 | 	if (!acomp_req->src || !acomp_req->slen) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (req_type == HZIP_ALG_TYPE_GZIP && | 
 | 	    acomp_req->slen < GZIP_HEAD_FEXTRA_SHIFT) | 
 | 		return -EINVAL; | 
 |  | 
 | 	switch (req_type) { | 
 | 	case HZIP_ALG_TYPE_ZLIB: | 
 | 		return TO_HEAD_SIZE(HZIP_ALG_TYPE_ZLIB); | 
 | 	case HZIP_ALG_TYPE_GZIP: | 
 | 		return TO_HEAD_SIZE(HZIP_ALG_TYPE_GZIP); | 
 | 	default: | 
 | 		pr_err("request type does not support!\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static struct hisi_zip_req *hisi_zip_create_req(struct acomp_req *req, | 
 | 						struct hisi_zip_qp_ctx *qp_ctx, | 
 | 						size_t head_size, bool is_comp) | 
 | { | 
 | 	struct hisi_zip_req_q *req_q = &qp_ctx->req_q; | 
 | 	struct hisi_zip_req *q = req_q->q; | 
 | 	struct hisi_zip_req *req_cache; | 
 | 	int req_id; | 
 |  | 
 | 	write_lock(&req_q->req_lock); | 
 |  | 
 | 	req_id = find_first_zero_bit(req_q->req_bitmap, req_q->size); | 
 | 	if (req_id >= req_q->size) { | 
 | 		write_unlock(&req_q->req_lock); | 
 | 		dev_dbg(&qp_ctx->qp->qm->pdev->dev, "req cache is full!\n"); | 
 | 		return ERR_PTR(-EAGAIN); | 
 | 	} | 
 | 	set_bit(req_id, req_q->req_bitmap); | 
 |  | 
 | 	req_cache = q + req_id; | 
 | 	req_cache->req_id = req_id; | 
 | 	req_cache->req = req; | 
 |  | 
 | 	if (is_comp) { | 
 | 		req_cache->sskip = 0; | 
 | 		req_cache->dskip = head_size; | 
 | 	} else { | 
 | 		req_cache->sskip = head_size; | 
 | 		req_cache->dskip = 0; | 
 | 	} | 
 |  | 
 | 	write_unlock(&req_q->req_lock); | 
 |  | 
 | 	return req_cache; | 
 | } | 
 |  | 
 | static void hisi_zip_remove_req(struct hisi_zip_qp_ctx *qp_ctx, | 
 | 				struct hisi_zip_req *req) | 
 | { | 
 | 	struct hisi_zip_req_q *req_q = &qp_ctx->req_q; | 
 |  | 
 | 	write_lock(&req_q->req_lock); | 
 | 	clear_bit(req->req_id, req_q->req_bitmap); | 
 | 	memset(req, 0, sizeof(struct hisi_zip_req)); | 
 | 	write_unlock(&req_q->req_lock); | 
 | } | 
 |  | 
 | static void hisi_zip_fill_addr(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req) | 
 | { | 
 | 	sqe->source_addr_l = lower_32_bits(req->dma_src); | 
 | 	sqe->source_addr_h = upper_32_bits(req->dma_src); | 
 | 	sqe->dest_addr_l = lower_32_bits(req->dma_dst); | 
 | 	sqe->dest_addr_h = upper_32_bits(req->dma_dst); | 
 | } | 
 |  | 
 | static void hisi_zip_fill_buf_size(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req) | 
 | { | 
 | 	struct acomp_req *a_req = req->req; | 
 |  | 
 | 	sqe->input_data_length = a_req->slen - req->sskip; | 
 | 	sqe->dest_avail_out = a_req->dlen - req->dskip; | 
 | 	sqe->dw7 = FIELD_PREP(HZIP_IN_SGE_DATA_OFFSET_M, req->sskip); | 
 | 	sqe->dw8 = FIELD_PREP(HZIP_OUT_SGE_DATA_OFFSET_M, req->dskip); | 
 | } | 
 |  | 
 | static void hisi_zip_fill_buf_type(struct hisi_zip_sqe *sqe, u8 buf_type) | 
 | { | 
 | 	u32 val; | 
 |  | 
 | 	val = sqe->dw9 & ~HZIP_BUF_TYPE_M; | 
 | 	val |= FIELD_PREP(HZIP_BUF_TYPE_M, buf_type); | 
 | 	sqe->dw9 = val; | 
 | } | 
 |  | 
 | static void hisi_zip_fill_req_type(struct hisi_zip_sqe *sqe, u8 req_type) | 
 | { | 
 | 	u32 val; | 
 |  | 
 | 	val = sqe->dw9 & ~HZIP_REQ_TYPE_M; | 
 | 	val |= FIELD_PREP(HZIP_REQ_TYPE_M, req_type); | 
 | 	sqe->dw9 = val; | 
 | } | 
 |  | 
 | static void hisi_zip_fill_tag_v1(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req) | 
 | { | 
 | 	sqe->dw13 = req->req_id; | 
 | } | 
 |  | 
 | static void hisi_zip_fill_tag_v2(struct hisi_zip_sqe *sqe, struct hisi_zip_req *req) | 
 | { | 
 | 	sqe->dw26 = req->req_id; | 
 | } | 
 |  | 
 | static void hisi_zip_fill_sqe_type(struct hisi_zip_sqe *sqe, u8 sqe_type) | 
 | { | 
 | 	u32 val; | 
 |  | 
 | 	val = sqe->dw7 & ~HZIP_SQE_TYPE_M; | 
 | 	val |= FIELD_PREP(HZIP_SQE_TYPE_M, sqe_type); | 
 | 	sqe->dw7 = val; | 
 | } | 
 |  | 
 | static void hisi_zip_fill_sqe(struct hisi_zip_ctx *ctx, struct hisi_zip_sqe *sqe, | 
 | 			      u8 req_type, struct hisi_zip_req *req) | 
 | { | 
 | 	const struct hisi_zip_sqe_ops *ops = ctx->ops; | 
 |  | 
 | 	memset(sqe, 0, sizeof(struct hisi_zip_sqe)); | 
 |  | 
 | 	ops->fill_addr(sqe, req); | 
 | 	ops->fill_buf_size(sqe, req); | 
 | 	ops->fill_buf_type(sqe, HZIP_SGL); | 
 | 	ops->fill_req_type(sqe, req_type); | 
 | 	ops->fill_tag(sqe, req); | 
 | 	ops->fill_sqe_type(sqe, ops->sqe_type); | 
 | } | 
 |  | 
 | static int hisi_zip_do_work(struct hisi_zip_req *req, | 
 | 			    struct hisi_zip_qp_ctx *qp_ctx) | 
 | { | 
 | 	struct hisi_acc_sgl_pool *pool = qp_ctx->sgl_pool; | 
 | 	struct hisi_zip_dfx *dfx = &qp_ctx->zip_dev->dfx; | 
 | 	struct acomp_req *a_req = req->req; | 
 | 	struct hisi_qp *qp = qp_ctx->qp; | 
 | 	struct device *dev = &qp->qm->pdev->dev; | 
 | 	struct hisi_zip_sqe zip_sqe; | 
 | 	int ret; | 
 |  | 
 | 	if (!a_req->src || !a_req->slen || !a_req->dst || !a_req->dlen) | 
 | 		return -EINVAL; | 
 |  | 
 | 	req->hw_src = hisi_acc_sg_buf_map_to_hw_sgl(dev, a_req->src, pool, | 
 | 						    req->req_id << 1, &req->dma_src); | 
 | 	if (IS_ERR(req->hw_src)) { | 
 | 		dev_err(dev, "failed to map the src buffer to hw sgl (%ld)!\n", | 
 | 			PTR_ERR(req->hw_src)); | 
 | 		return PTR_ERR(req->hw_src); | 
 | 	} | 
 |  | 
 | 	req->hw_dst = hisi_acc_sg_buf_map_to_hw_sgl(dev, a_req->dst, pool, | 
 | 						    (req->req_id << 1) + 1, | 
 | 						    &req->dma_dst); | 
 | 	if (IS_ERR(req->hw_dst)) { | 
 | 		ret = PTR_ERR(req->hw_dst); | 
 | 		dev_err(dev, "failed to map the dst buffer to hw slg (%d)!\n", | 
 | 			ret); | 
 | 		goto err_unmap_input; | 
 | 	} | 
 |  | 
 | 	hisi_zip_fill_sqe(qp_ctx->ctx, &zip_sqe, qp->req_type, req); | 
 |  | 
 | 	/* send command to start a task */ | 
 | 	atomic64_inc(&dfx->send_cnt); | 
 | 	ret = hisi_qp_send(qp, &zip_sqe); | 
 | 	if (ret < 0) { | 
 | 		atomic64_inc(&dfx->send_busy_cnt); | 
 | 		ret = -EAGAIN; | 
 | 		dev_dbg_ratelimited(dev, "failed to send request!\n"); | 
 | 		goto err_unmap_output; | 
 | 	} | 
 |  | 
 | 	return -EINPROGRESS; | 
 |  | 
 | err_unmap_output: | 
 | 	hisi_acc_sg_buf_unmap(dev, a_req->dst, req->hw_dst); | 
 | err_unmap_input: | 
 | 	hisi_acc_sg_buf_unmap(dev, a_req->src, req->hw_src); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static u32 hisi_zip_get_tag_v1(struct hisi_zip_sqe *sqe) | 
 | { | 
 | 	return sqe->dw13; | 
 | } | 
 |  | 
 | static u32 hisi_zip_get_tag_v2(struct hisi_zip_sqe *sqe) | 
 | { | 
 | 	return sqe->dw26; | 
 | } | 
 |  | 
 | static u32 hisi_zip_get_status(struct hisi_zip_sqe *sqe) | 
 | { | 
 | 	return sqe->dw3 & HZIP_BD_STATUS_M; | 
 | } | 
 |  | 
 | static u32 hisi_zip_get_dstlen(struct hisi_zip_sqe *sqe) | 
 | { | 
 | 	return sqe->produced; | 
 | } | 
 |  | 
 | static void hisi_zip_acomp_cb(struct hisi_qp *qp, void *data) | 
 | { | 
 | 	struct hisi_zip_qp_ctx *qp_ctx = qp->qp_ctx; | 
 | 	const struct hisi_zip_sqe_ops *ops = qp_ctx->ctx->ops; | 
 | 	struct hisi_zip_dfx *dfx = &qp_ctx->zip_dev->dfx; | 
 | 	struct hisi_zip_req_q *req_q = &qp_ctx->req_q; | 
 | 	struct device *dev = &qp->qm->pdev->dev; | 
 | 	struct hisi_zip_sqe *sqe = data; | 
 | 	u32 tag = ops->get_tag(sqe); | 
 | 	struct hisi_zip_req *req = req_q->q + tag; | 
 | 	struct acomp_req *acomp_req = req->req; | 
 | 	u32 status, dlen, head_size; | 
 | 	int err = 0; | 
 |  | 
 | 	atomic64_inc(&dfx->recv_cnt); | 
 | 	status = ops->get_status(sqe); | 
 | 	if (status != 0 && status != HZIP_NC_ERR) { | 
 | 		dev_err(dev, "%scompress fail in qp%u: %u, output: %u\n", | 
 | 			(qp->alg_type == 0) ? "" : "de", qp->qp_id, status, | 
 | 			sqe->produced); | 
 | 		atomic64_inc(&dfx->err_bd_cnt); | 
 | 		err = -EIO; | 
 | 	} | 
 |  | 
 | 	dlen = ops->get_dstlen(sqe); | 
 |  | 
 | 	hisi_acc_sg_buf_unmap(dev, acomp_req->src, req->hw_src); | 
 | 	hisi_acc_sg_buf_unmap(dev, acomp_req->dst, req->hw_dst); | 
 |  | 
 | 	head_size = (qp->alg_type == 0) ? TO_HEAD_SIZE(qp->req_type) : 0; | 
 | 	acomp_req->dlen = dlen + head_size; | 
 |  | 
 | 	if (acomp_req->base.complete) | 
 | 		acomp_request_complete(acomp_req, err); | 
 |  | 
 | 	hisi_zip_remove_req(qp_ctx, req); | 
 | } | 
 |  | 
 | static int hisi_zip_acompress(struct acomp_req *acomp_req) | 
 | { | 
 | 	struct hisi_zip_ctx *ctx = crypto_tfm_ctx(acomp_req->base.tfm); | 
 | 	struct hisi_zip_qp_ctx *qp_ctx = &ctx->qp_ctx[HZIP_QPC_COMP]; | 
 | 	struct device *dev = &qp_ctx->qp->qm->pdev->dev; | 
 | 	struct hisi_zip_req *req; | 
 | 	int head_size; | 
 | 	int ret; | 
 |  | 
 | 	/* let's output compression head now */ | 
 | 	head_size = add_comp_head(acomp_req->dst, qp_ctx->qp->req_type); | 
 | 	if (head_size < 0) { | 
 | 		dev_err_ratelimited(dev, "failed to add comp head (%d)!\n", | 
 | 				    head_size); | 
 | 		return head_size; | 
 | 	} | 
 |  | 
 | 	req = hisi_zip_create_req(acomp_req, qp_ctx, head_size, true); | 
 | 	if (IS_ERR(req)) | 
 | 		return PTR_ERR(req); | 
 |  | 
 | 	ret = hisi_zip_do_work(req, qp_ctx); | 
 | 	if (ret != -EINPROGRESS) { | 
 | 		dev_info_ratelimited(dev, "failed to do compress (%d)!\n", ret); | 
 | 		hisi_zip_remove_req(qp_ctx, req); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int hisi_zip_adecompress(struct acomp_req *acomp_req) | 
 | { | 
 | 	struct hisi_zip_ctx *ctx = crypto_tfm_ctx(acomp_req->base.tfm); | 
 | 	struct hisi_zip_qp_ctx *qp_ctx = &ctx->qp_ctx[HZIP_QPC_DECOMP]; | 
 | 	struct device *dev = &qp_ctx->qp->qm->pdev->dev; | 
 | 	struct hisi_zip_req *req; | 
 | 	int head_size, ret; | 
 |  | 
 | 	head_size = get_comp_head_size(acomp_req, qp_ctx->qp->req_type); | 
 | 	if (head_size < 0) { | 
 | 		dev_err_ratelimited(dev, "failed to get comp head size (%d)!\n", | 
 | 				    head_size); | 
 | 		return head_size; | 
 | 	} | 
 |  | 
 | 	req = hisi_zip_create_req(acomp_req, qp_ctx, head_size, false); | 
 | 	if (IS_ERR(req)) | 
 | 		return PTR_ERR(req); | 
 |  | 
 | 	ret = hisi_zip_do_work(req, qp_ctx); | 
 | 	if (ret != -EINPROGRESS) { | 
 | 		dev_info_ratelimited(dev, "failed to do decompress (%d)!\n", | 
 | 				     ret); | 
 | 		hisi_zip_remove_req(qp_ctx, req); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | static int hisi_zip_start_qp(struct hisi_qp *qp, struct hisi_zip_qp_ctx *ctx, | 
 | 			     int alg_type, int req_type) | 
 | { | 
 | 	struct device *dev = &qp->qm->pdev->dev; | 
 | 	int ret; | 
 |  | 
 | 	qp->req_type = req_type; | 
 | 	qp->alg_type = alg_type; | 
 | 	qp->qp_ctx = ctx; | 
 |  | 
 | 	ret = hisi_qm_start_qp(qp, 0); | 
 | 	if (ret < 0) { | 
 | 		dev_err(dev, "failed to start qp (%d)!\n", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ctx->qp = qp; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void hisi_zip_release_qp(struct hisi_zip_qp_ctx *ctx) | 
 | { | 
 | 	hisi_qm_stop_qp(ctx->qp); | 
 | 	hisi_qm_release_qp(ctx->qp); | 
 | } | 
 |  | 
 | static const struct hisi_zip_sqe_ops hisi_zip_ops_v1 = { | 
 | 	.sqe_type		= 0, | 
 | 	.fill_addr		= hisi_zip_fill_addr, | 
 | 	.fill_buf_size		= hisi_zip_fill_buf_size, | 
 | 	.fill_buf_type		= hisi_zip_fill_buf_type, | 
 | 	.fill_req_type		= hisi_zip_fill_req_type, | 
 | 	.fill_tag		= hisi_zip_fill_tag_v1, | 
 | 	.fill_sqe_type		= hisi_zip_fill_sqe_type, | 
 | 	.get_tag		= hisi_zip_get_tag_v1, | 
 | 	.get_status		= hisi_zip_get_status, | 
 | 	.get_dstlen		= hisi_zip_get_dstlen, | 
 | }; | 
 |  | 
 | static const struct hisi_zip_sqe_ops hisi_zip_ops_v2 = { | 
 | 	.sqe_type		= 0x3, | 
 | 	.fill_addr		= hisi_zip_fill_addr, | 
 | 	.fill_buf_size		= hisi_zip_fill_buf_size, | 
 | 	.fill_buf_type		= hisi_zip_fill_buf_type, | 
 | 	.fill_req_type		= hisi_zip_fill_req_type, | 
 | 	.fill_tag		= hisi_zip_fill_tag_v2, | 
 | 	.fill_sqe_type		= hisi_zip_fill_sqe_type, | 
 | 	.get_tag		= hisi_zip_get_tag_v2, | 
 | 	.get_status		= hisi_zip_get_status, | 
 | 	.get_dstlen		= hisi_zip_get_dstlen, | 
 | }; | 
 |  | 
 | static int hisi_zip_ctx_init(struct hisi_zip_ctx *hisi_zip_ctx, u8 req_type, int node) | 
 | { | 
 | 	struct hisi_qp *qps[HZIP_CTX_Q_NUM] = { NULL }; | 
 | 	struct hisi_zip_qp_ctx *qp_ctx; | 
 | 	struct hisi_zip *hisi_zip; | 
 | 	int ret, i, j; | 
 |  | 
 | 	ret = zip_create_qps(qps, HZIP_CTX_Q_NUM, node); | 
 | 	if (ret) { | 
 | 		pr_err("failed to create zip qps (%d)!\n", ret); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	hisi_zip = container_of(qps[0]->qm, struct hisi_zip, qm); | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) { | 
 | 		/* alg_type = 0 for compress, 1 for decompress in hw sqe */ | 
 | 		qp_ctx = &hisi_zip_ctx->qp_ctx[i]; | 
 | 		qp_ctx->ctx = hisi_zip_ctx; | 
 | 		ret = hisi_zip_start_qp(qps[i], qp_ctx, i, req_type); | 
 | 		if (ret) { | 
 | 			for (j = i - 1; j >= 0; j--) | 
 | 				hisi_qm_stop_qp(hisi_zip_ctx->qp_ctx[j].qp); | 
 |  | 
 | 			hisi_qm_free_qps(qps, HZIP_CTX_Q_NUM); | 
 | 			return ret; | 
 | 		} | 
 |  | 
 | 		qp_ctx->zip_dev = hisi_zip; | 
 | 	} | 
 |  | 
 | 	if (hisi_zip->qm.ver < QM_HW_V3) | 
 | 		hisi_zip_ctx->ops = &hisi_zip_ops_v1; | 
 | 	else | 
 | 		hisi_zip_ctx->ops = &hisi_zip_ops_v2; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void hisi_zip_ctx_exit(struct hisi_zip_ctx *hisi_zip_ctx) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 1; i >= 0; i--) | 
 | 		hisi_zip_release_qp(&hisi_zip_ctx->qp_ctx[i]); | 
 | } | 
 |  | 
 | static int hisi_zip_create_req_q(struct hisi_zip_ctx *ctx) | 
 | { | 
 | 	struct hisi_zip_req_q *req_q; | 
 | 	int i, ret; | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) { | 
 | 		req_q = &ctx->qp_ctx[i].req_q; | 
 | 		req_q->size = QM_Q_DEPTH; | 
 |  | 
 | 		req_q->req_bitmap = kcalloc(BITS_TO_LONGS(req_q->size), | 
 | 					    sizeof(long), GFP_KERNEL); | 
 | 		if (!req_q->req_bitmap) { | 
 | 			ret = -ENOMEM; | 
 | 			if (i == 0) | 
 | 				return ret; | 
 |  | 
 | 			goto err_free_loop0; | 
 | 		} | 
 | 		rwlock_init(&req_q->req_lock); | 
 |  | 
 | 		req_q->q = kcalloc(req_q->size, sizeof(struct hisi_zip_req), | 
 | 				   GFP_KERNEL); | 
 | 		if (!req_q->q) { | 
 | 			ret = -ENOMEM; | 
 | 			if (i == 0) | 
 | 				goto err_free_bitmap; | 
 | 			else | 
 | 				goto err_free_loop1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | err_free_loop1: | 
 | 	kfree(ctx->qp_ctx[HZIP_QPC_DECOMP].req_q.req_bitmap); | 
 | err_free_loop0: | 
 | 	kfree(ctx->qp_ctx[HZIP_QPC_COMP].req_q.q); | 
 | err_free_bitmap: | 
 | 	kfree(ctx->qp_ctx[HZIP_QPC_COMP].req_q.req_bitmap); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void hisi_zip_release_req_q(struct hisi_zip_ctx *ctx) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) { | 
 | 		kfree(ctx->qp_ctx[i].req_q.q); | 
 | 		kfree(ctx->qp_ctx[i].req_q.req_bitmap); | 
 | 	} | 
 | } | 
 |  | 
 | static int hisi_zip_create_sgl_pool(struct hisi_zip_ctx *ctx) | 
 | { | 
 | 	struct hisi_zip_qp_ctx *tmp; | 
 | 	struct device *dev; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) { | 
 | 		tmp = &ctx->qp_ctx[i]; | 
 | 		dev = &tmp->qp->qm->pdev->dev; | 
 | 		tmp->sgl_pool = hisi_acc_create_sgl_pool(dev, QM_Q_DEPTH << 1, | 
 | 							 sgl_sge_nr); | 
 | 		if (IS_ERR(tmp->sgl_pool)) { | 
 | 			if (i == 1) | 
 | 				goto err_free_sgl_pool0; | 
 | 			return -ENOMEM; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 |  | 
 | err_free_sgl_pool0: | 
 | 	hisi_acc_free_sgl_pool(&ctx->qp_ctx[HZIP_QPC_COMP].qp->qm->pdev->dev, | 
 | 			       ctx->qp_ctx[HZIP_QPC_COMP].sgl_pool); | 
 | 	return -ENOMEM; | 
 | } | 
 |  | 
 | static void hisi_zip_release_sgl_pool(struct hisi_zip_ctx *ctx) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) | 
 | 		hisi_acc_free_sgl_pool(&ctx->qp_ctx[i].qp->qm->pdev->dev, | 
 | 				       ctx->qp_ctx[i].sgl_pool); | 
 | } | 
 |  | 
 | static void hisi_zip_set_acomp_cb(struct hisi_zip_ctx *ctx, | 
 | 				  void (*fn)(struct hisi_qp *, void *)) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < HZIP_CTX_Q_NUM; i++) | 
 | 		ctx->qp_ctx[i].qp->req_cb = fn; | 
 | } | 
 |  | 
 | static int hisi_zip_acomp_init(struct crypto_acomp *tfm) | 
 | { | 
 | 	const char *alg_name = crypto_tfm_alg_name(&tfm->base); | 
 | 	struct hisi_zip_ctx *ctx = crypto_tfm_ctx(&tfm->base); | 
 | 	struct device *dev; | 
 | 	int ret; | 
 |  | 
 | 	ret = hisi_zip_ctx_init(ctx, COMP_NAME_TO_TYPE(alg_name), tfm->base.node); | 
 | 	if (ret) { | 
 | 		pr_err("failed to init ctx (%d)!\n", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	dev = &ctx->qp_ctx[0].qp->qm->pdev->dev; | 
 |  | 
 | 	ret = hisi_zip_create_req_q(ctx); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "failed to create request queue (%d)!\n", ret); | 
 | 		goto err_ctx_exit; | 
 | 	} | 
 |  | 
 | 	ret = hisi_zip_create_sgl_pool(ctx); | 
 | 	if (ret) { | 
 | 		dev_err(dev, "failed to create sgl pool (%d)!\n", ret); | 
 | 		goto err_release_req_q; | 
 | 	} | 
 |  | 
 | 	hisi_zip_set_acomp_cb(ctx, hisi_zip_acomp_cb); | 
 |  | 
 | 	return 0; | 
 |  | 
 | err_release_req_q: | 
 | 	hisi_zip_release_req_q(ctx); | 
 | err_ctx_exit: | 
 | 	hisi_zip_ctx_exit(ctx); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static void hisi_zip_acomp_exit(struct crypto_acomp *tfm) | 
 | { | 
 | 	struct hisi_zip_ctx *ctx = crypto_tfm_ctx(&tfm->base); | 
 |  | 
 | 	hisi_zip_set_acomp_cb(ctx, NULL); | 
 | 	hisi_zip_release_sgl_pool(ctx); | 
 | 	hisi_zip_release_req_q(ctx); | 
 | 	hisi_zip_ctx_exit(ctx); | 
 | } | 
 |  | 
 | static struct acomp_alg hisi_zip_acomp_zlib = { | 
 | 	.init			= hisi_zip_acomp_init, | 
 | 	.exit			= hisi_zip_acomp_exit, | 
 | 	.compress		= hisi_zip_acompress, | 
 | 	.decompress		= hisi_zip_adecompress, | 
 | 	.base			= { | 
 | 		.cra_name		= "zlib-deflate", | 
 | 		.cra_driver_name	= "hisi-zlib-acomp", | 
 | 		.cra_module		= THIS_MODULE, | 
 | 		.cra_priority           = HZIP_ALG_PRIORITY, | 
 | 		.cra_ctxsize		= sizeof(struct hisi_zip_ctx), | 
 | 	} | 
 | }; | 
 |  | 
 | static struct acomp_alg hisi_zip_acomp_gzip = { | 
 | 	.init			= hisi_zip_acomp_init, | 
 | 	.exit			= hisi_zip_acomp_exit, | 
 | 	.compress		= hisi_zip_acompress, | 
 | 	.decompress		= hisi_zip_adecompress, | 
 | 	.base			= { | 
 | 		.cra_name		= "gzip", | 
 | 		.cra_driver_name	= "hisi-gzip-acomp", | 
 | 		.cra_module		= THIS_MODULE, | 
 | 		.cra_priority           = HZIP_ALG_PRIORITY, | 
 | 		.cra_ctxsize		= sizeof(struct hisi_zip_ctx), | 
 | 	} | 
 | }; | 
 |  | 
 | int hisi_zip_register_to_crypto(struct hisi_qm *qm) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = crypto_register_acomp(&hisi_zip_acomp_zlib); | 
 | 	if (ret) { | 
 | 		pr_err("failed to register to zlib (%d)!\n", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	ret = crypto_register_acomp(&hisi_zip_acomp_gzip); | 
 | 	if (ret) { | 
 | 		pr_err("failed to register to gzip (%d)!\n", ret); | 
 | 		crypto_unregister_acomp(&hisi_zip_acomp_zlib); | 
 | 	} | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | void hisi_zip_unregister_from_crypto(struct hisi_qm *qm) | 
 | { | 
 | 	crypto_unregister_acomp(&hisi_zip_acomp_gzip); | 
 | 	crypto_unregister_acomp(&hisi_zip_acomp_zlib); | 
 | } |