// SPDX-License-Identifier: GPL-2.0 /* * Crypto acceleration support for Rockchip crypto * * Copyright (c) 2018, Fuzhou Rockchip Electronics Co., Ltd * * Author: Zain Wang * Mender: Lin Jinhan * * Some ideas are from marvell-cesa.c and s5p-sss.c driver. */ #include #include #include #include #include #include #include #include #include #include "rk_crypto_core.h" #include "rk_crypto_utils.h" #include "rk_crypto_v1.h" #include "rk_crypto_v2.h" #include "rk_crypto_v3.h" #include "cryptodev_linux/rk_cryptodev.h" #include "procfs.h" #define CRYPTO_NAME "rkcrypto" static struct rk_alg_ctx *rk_alg_ctx_cast(struct crypto_async_request *async_req) { struct rk_cipher_ctx *ctx = crypto_tfm_ctx(async_req->tfm); return &ctx->algs_ctx; } static int rk_crypto_enable_clk(struct rk_crypto_dev *rk_dev) { int ret; dev_dbg(rk_dev->dev, "clk_bulk_prepare_enable.\n"); ret = clk_bulk_prepare_enable(rk_dev->clks_num, rk_dev->clk_bulks); if (ret < 0) dev_err(rk_dev->dev, "failed to enable clks %d\n", ret); return ret; } static void rk_crypto_disable_clk(struct rk_crypto_dev *rk_dev) { dev_dbg(rk_dev->dev, "clk_bulk_disable_unprepare.\n"); clk_bulk_disable_unprepare(rk_dev->clks_num, rk_dev->clk_bulks); } static int rk_load_data(struct rk_crypto_dev *rk_dev, struct scatterlist *sg_src, struct scatterlist *sg_dst) { int ret = -EINVAL; unsigned int count; u32 src_nents, dst_nents; struct device *dev = rk_dev->dev; struct rk_alg_ctx *alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); alg_ctx->count = 0; /* 0 data input just do nothing */ if (alg_ctx->total == 0) return 0; src_nents = alg_ctx->src_nents; dst_nents = alg_ctx->dst_nents; /* skip assoclen data */ if (alg_ctx->assoclen && alg_ctx->left_bytes == alg_ctx->total) { CRYPTO_TRACE("have assoclen..."); if (alg_ctx->assoclen > rk_dev->aad_max) { ret = -ENOMEM; goto error; } if (!sg_pcopy_to_buffer(alg_ctx->req_src, alg_ctx->src_nents, rk_dev->addr_aad, alg_ctx->assoclen, 0)) { dev_err(dev, "[%s:%d] assoc pcopy err\n", __func__, __LINE__); ret = -EINVAL; goto error; } sg_init_one(&alg_ctx->sg_aad, rk_dev->addr_aad, alg_ctx->assoclen); if (!dma_map_sg(dev, &alg_ctx->sg_aad, 1, DMA_TO_DEVICE)) { dev_err(dev, "[%s:%d] dma_map_sg(sg_aad) error\n", __func__, __LINE__); ret = -ENOMEM; goto error; } alg_ctx->addr_aad_in = sg_dma_address(&alg_ctx->sg_aad); /* point sg_src and sg_dst skip assoc data */ sg_src = scatterwalk_ffwd(rk_dev->src, alg_ctx->req_src, alg_ctx->assoclen); sg_dst = (alg_ctx->req_src == alg_ctx->req_dst) ? sg_src : scatterwalk_ffwd(rk_dev->dst, alg_ctx->req_dst, alg_ctx->assoclen); alg_ctx->sg_src = sg_src; alg_ctx->sg_dst = sg_dst; src_nents = sg_nents_for_len(sg_src, alg_ctx->total); dst_nents = sg_nents_for_len(sg_dst, alg_ctx->total); CRYPTO_TRACE("src_nents = %u, dst_nents = %u", src_nents, dst_nents); } if (alg_ctx->left_bytes == alg_ctx->total) { alg_ctx->aligned = rk_crypto_check_align(sg_src, src_nents, sg_dst, dst_nents, alg_ctx->align_size); alg_ctx->is_dma = rk_crypto_check_dmafd(sg_src, src_nents) && rk_crypto_check_dmafd(sg_dst, dst_nents); } CRYPTO_TRACE("aligned = %d, is_dma = %d, total = %u, left_bytes = %u, assoclen = %u\n", alg_ctx->aligned, alg_ctx->is_dma, alg_ctx->total, alg_ctx->left_bytes, alg_ctx->assoclen); if (alg_ctx->aligned) { u32 nents; if (rk_dev->soc_data->use_lli_chain) { count = rk_crypto_hw_desc_maxlen(sg_src, alg_ctx->left_bytes, &nents); } else { nents = 1; count = min_t(unsigned int, alg_ctx->left_bytes, sg_src->length); } alg_ctx->map_nents = nents; alg_ctx->left_bytes -= count; if (!alg_ctx->is_dma && !dma_map_sg(dev, sg_src, nents, DMA_TO_DEVICE)) { dev_err(dev, "[%s:%d] dma_map_sg(src) error\n", __func__, __LINE__); ret = -EINVAL; goto error; } alg_ctx->addr_in = sg_dma_address(sg_src); if (sg_dst) { if (!alg_ctx->is_dma && !dma_map_sg(dev, sg_dst, nents, DMA_FROM_DEVICE)) { dev_err(dev, "[%s:%d] dma_map_sg(dst) error\n", __func__, __LINE__); dma_unmap_sg(dev, sg_src, 1, DMA_TO_DEVICE); ret = -EINVAL; goto error; } alg_ctx->addr_out = sg_dma_address(sg_dst); } } else { alg_ctx->map_nents = 1; count = (alg_ctx->left_bytes > rk_dev->vir_max) ? rk_dev->vir_max : alg_ctx->left_bytes; if (!sg_pcopy_to_buffer(alg_ctx->req_src, alg_ctx->src_nents, rk_dev->addr_vir, count, alg_ctx->assoclen + alg_ctx->total - alg_ctx->left_bytes)) { dev_err(dev, "[%s:%d] pcopy err\n", __func__, __LINE__); ret = -EINVAL; goto error; } alg_ctx->left_bytes -= count; sg_init_one(&alg_ctx->sg_tmp, rk_dev->addr_vir, count); if (!dma_map_sg(dev, &alg_ctx->sg_tmp, 1, DMA_TO_DEVICE)) { dev_err(dev, "[%s:%d] dma_map_sg(sg_tmp) error\n", __func__, __LINE__); ret = -ENOMEM; goto error; } alg_ctx->addr_in = sg_dma_address(&alg_ctx->sg_tmp); if (sg_dst) { if (!dma_map_sg(dev, &alg_ctx->sg_tmp, 1, DMA_FROM_DEVICE)) { dev_err(dev, "[%s:%d] dma_map_sg(sg_tmp) error\n", __func__, __LINE__); dma_unmap_sg(dev, &alg_ctx->sg_tmp, 1, DMA_TO_DEVICE); ret = -ENOMEM; goto error; } alg_ctx->addr_out = sg_dma_address(&alg_ctx->sg_tmp); } } alg_ctx->count = count; return 0; error: return ret; } static int rk_unload_data(struct rk_crypto_dev *rk_dev) { int ret = 0; struct scatterlist *sg_in, *sg_out; struct rk_alg_ctx *alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); u32 nents; CRYPTO_TRACE("aligned = %d, total = %u, left_bytes = %u\n", alg_ctx->aligned, alg_ctx->total, alg_ctx->left_bytes); /* 0 data input just do nothing */ if (alg_ctx->total == 0 || alg_ctx->count == 0) return 0; nents = alg_ctx->map_nents; sg_in = alg_ctx->aligned ? alg_ctx->sg_src : &alg_ctx->sg_tmp; /* only is dma buffer and aligned will skip unmap */ if (!alg_ctx->is_dma || !alg_ctx->aligned) dma_unmap_sg(rk_dev->dev, sg_in, nents, DMA_TO_DEVICE); if (alg_ctx->sg_dst) { sg_out = alg_ctx->aligned ? alg_ctx->sg_dst : &alg_ctx->sg_tmp; /* only is dma buffer and aligned will skip unmap */ if (!alg_ctx->is_dma || !alg_ctx->aligned) dma_unmap_sg(rk_dev->dev, sg_out, nents, DMA_FROM_DEVICE); } if (!alg_ctx->aligned && alg_ctx->req_dst) { if (!sg_pcopy_from_buffer(alg_ctx->req_dst, alg_ctx->dst_nents, rk_dev->addr_vir, alg_ctx->count, alg_ctx->total - alg_ctx->left_bytes - alg_ctx->count + alg_ctx->assoclen)) { ret = -EINVAL; goto exit; } } if (alg_ctx->assoclen) { dma_unmap_sg(rk_dev->dev, &alg_ctx->sg_aad, 1, DMA_TO_DEVICE); /* copy assoc data to dst */ if (!sg_pcopy_from_buffer(alg_ctx->req_dst, sg_nents(alg_ctx->req_dst), rk_dev->addr_aad, alg_ctx->assoclen, 0)) { ret = -EINVAL; goto exit; } } exit: return ret; } static void start_irq_timer(struct rk_crypto_dev *rk_dev) { mod_timer(&rk_dev->timer, jiffies + msecs_to_jiffies(3000)); } /* use timer to avoid crypto irq timeout */ static void rk_crypto_irq_timer_handle(struct timer_list *t) { struct rk_crypto_dev *rk_dev = from_timer(rk_dev, t, timer); unsigned long flags; spin_lock_irqsave(&rk_dev->lock, flags); rk_dev->err = -ETIMEDOUT; rk_dev->stat.timeout_cnt++; rk_unload_data(rk_dev); spin_unlock_irqrestore(&rk_dev->lock, flags); tasklet_schedule(&rk_dev->done_task); } static irqreturn_t rk_crypto_irq_handle(int irq, void *dev_id) { struct rk_crypto_dev *rk_dev = platform_get_drvdata(dev_id); struct rk_alg_ctx *alg_ctx; unsigned long flags; spin_lock_irqsave(&rk_dev->lock, flags); /* reset timeout timer */ start_irq_timer(rk_dev); alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); rk_dev->stat.irq_cnt++; if (alg_ctx->ops.irq_handle) alg_ctx->ops.irq_handle(irq, dev_id); /* already trigger timeout */ if (rk_dev->err != -ETIMEDOUT) { spin_unlock_irqrestore(&rk_dev->lock, flags); tasklet_schedule(&rk_dev->done_task); } else { spin_unlock_irqrestore(&rk_dev->lock, flags); } return IRQ_HANDLED; } static int rk_start_op(struct rk_crypto_dev *rk_dev) { struct rk_alg_ctx *alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); int ret; if (!alg_ctx || !alg_ctx->ops.start) return -EINVAL; alg_ctx->aligned = false; enable_irq(rk_dev->irq); start_irq_timer(rk_dev); ret = alg_ctx->ops.start(rk_dev); if (ret) return ret; /* fake calculations are used to trigger the Done Task */ if (alg_ctx->total == 0) { CRYPTO_TRACE("fake done_task"); rk_dev->stat.fake_cnt++; tasklet_schedule(&rk_dev->done_task); } return 0; } static int rk_update_op(struct rk_crypto_dev *rk_dev) { struct rk_alg_ctx *alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); if (!alg_ctx || !alg_ctx->ops.update) return -EINVAL; return alg_ctx->ops.update(rk_dev); } static void rk_complete_op(struct rk_crypto_dev *rk_dev, int err) { struct rk_alg_ctx *alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); disable_irq(rk_dev->irq); del_timer(&rk_dev->timer); rk_dev->stat.complete_cnt++; if (err) { rk_dev->stat.error_cnt++; rk_dev->stat.last_error = err; dev_err(rk_dev->dev, "complete_op err = %d\n", err); } if (!alg_ctx || !alg_ctx->ops.complete) return; alg_ctx->ops.complete(rk_dev->async_req, err); rk_dev->async_req = NULL; tasklet_schedule(&rk_dev->queue_task); } static int rk_crypto_enqueue(struct rk_crypto_dev *rk_dev, struct crypto_async_request *async_req) { unsigned long flags; int ret; spin_lock_irqsave(&rk_dev->lock, flags); ret = crypto_enqueue_request(&rk_dev->queue, async_req); if (rk_dev->queue.qlen > rk_dev->stat.ever_queue_max) rk_dev->stat.ever_queue_max = rk_dev->queue.qlen; if (rk_dev->busy) { rk_dev->stat.busy_cnt++; spin_unlock_irqrestore(&rk_dev->lock, flags); return ret; } rk_dev->stat.equeue_cnt++; rk_dev->busy = true; spin_unlock_irqrestore(&rk_dev->lock, flags); tasklet_schedule(&rk_dev->queue_task); return ret; } static void rk_crypto_queue_task_cb(unsigned long data) { struct rk_crypto_dev *rk_dev = (struct rk_crypto_dev *)data; struct crypto_async_request *async_req, *backlog; unsigned long flags; spin_lock_irqsave(&rk_dev->lock, flags); if (rk_dev->async_req) { dev_err(rk_dev->dev, "%s: Unexpected crypto paths.\n", __func__); goto exit; } rk_dev->err = 0; backlog = crypto_get_backlog(&rk_dev->queue); async_req = crypto_dequeue_request(&rk_dev->queue); if (!async_req) { rk_dev->busy = false; goto exit; } rk_dev->stat.dequeue_cnt++; if (backlog) { backlog->complete(backlog, -EINPROGRESS); backlog = NULL; } rk_dev->async_req = async_req; rk_dev->err = rk_start_op(rk_dev); if (rk_dev->err) rk_complete_op(rk_dev, rk_dev->err); exit: spin_unlock_irqrestore(&rk_dev->lock, flags); } static void rk_crypto_done_task_cb(unsigned long data) { struct rk_crypto_dev *rk_dev = (struct rk_crypto_dev *)data; struct rk_alg_ctx *alg_ctx; unsigned long flags; spin_lock_irqsave(&rk_dev->lock, flags); if (!rk_dev->async_req) { dev_err(rk_dev->dev, "done task receive invalid async_req\n"); spin_unlock_irqrestore(&rk_dev->lock, flags); return; } alg_ctx = rk_alg_ctx_cast(rk_dev->async_req); rk_dev->stat.done_cnt++; if (rk_dev->err) goto exit; if (alg_ctx->left_bytes == 0) { CRYPTO_TRACE("done task cb last calc"); /* unload data for last calculation */ rk_dev->err = rk_update_op(rk_dev); goto exit; } rk_dev->err = rk_update_op(rk_dev); if (rk_dev->err) goto exit; spin_unlock_irqrestore(&rk_dev->lock, flags); return; exit: rk_complete_op(rk_dev, rk_dev->err); spin_unlock_irqrestore(&rk_dev->lock, flags); } static struct rk_crypto_algt *rk_crypto_find_algs(struct rk_crypto_dev *rk_dev, char *name) { u32 i; struct rk_crypto_algt **algs; struct rk_crypto_algt *tmp_algs; uint32_t total_algs_num = 0; algs = rk_dev->soc_data->hw_get_algts(&total_algs_num); if (!algs || total_algs_num == 0) return NULL; for (i = 0; i < total_algs_num; i++, algs++) { tmp_algs = *algs; tmp_algs->rk_dev = rk_dev; if (strcmp(tmp_algs->name, name) == 0) return tmp_algs; } return NULL; } static int rk_crypto_register(struct rk_crypto_dev *rk_dev) { unsigned int i, k; char **algs_name; struct rk_crypto_algt *tmp_algs; struct rk_crypto_soc_data *soc_data; int err = 0; soc_data = rk_dev->soc_data; algs_name = soc_data->valid_algs_name; rk_dev->request_crypto(rk_dev, __func__); for (i = 0; i < soc_data->valid_algs_num; i++, algs_name++) { tmp_algs = rk_crypto_find_algs(rk_dev, *algs_name); if (!tmp_algs) { CRYPTO_TRACE("%s not matched!!!\n", *algs_name); continue; } if (soc_data->hw_is_algo_valid && !soc_data->hw_is_algo_valid(rk_dev, tmp_algs)) { CRYPTO_TRACE("%s skipped!!!\n", *algs_name); continue; } CRYPTO_TRACE("%s matched!!!\n", *algs_name); tmp_algs->rk_dev = rk_dev; if (tmp_algs->type == ALG_TYPE_CIPHER) { if (tmp_algs->mode == CIPHER_MODE_CTR || tmp_algs->mode == CIPHER_MODE_CFB || tmp_algs->mode == CIPHER_MODE_OFB) tmp_algs->alg.crypto.base.cra_blocksize = 1; if (tmp_algs->mode == CIPHER_MODE_ECB) tmp_algs->alg.crypto.ivsize = 0; /* rv1126 is not support aes192 */ if (soc_data->use_soft_aes192 && tmp_algs->algo == CIPHER_ALGO_AES) tmp_algs->use_soft_aes192 = true; err = crypto_register_skcipher(&tmp_algs->alg.crypto); } else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) { err = crypto_register_ahash(&tmp_algs->alg.hash); } else if (tmp_algs->type == ALG_TYPE_ASYM) { err = crypto_register_akcipher(&tmp_algs->alg.asym); } else if (tmp_algs->type == ALG_TYPE_AEAD) { if (soc_data->use_soft_aes192 && tmp_algs->algo == CIPHER_ALGO_AES) tmp_algs->use_soft_aes192 = true; err = crypto_register_aead(&tmp_algs->alg.aead); } else { continue; } if (err) goto err_cipher_algs; tmp_algs->valid_flag = true; CRYPTO_TRACE("%s register OK!!!\n", *algs_name); } rk_dev->release_crypto(rk_dev, __func__); return 0; err_cipher_algs: algs_name = soc_data->valid_algs_name; for (k = 0; k < i; k++, algs_name++) { tmp_algs = rk_crypto_find_algs(rk_dev, *algs_name); if (!tmp_algs) continue; if (tmp_algs->type == ALG_TYPE_CIPHER) crypto_unregister_skcipher(&tmp_algs->alg.crypto); else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) crypto_unregister_ahash(&tmp_algs->alg.hash); else if (tmp_algs->type == ALG_TYPE_ASYM) crypto_unregister_akcipher(&tmp_algs->alg.asym); else if (tmp_algs->type == ALG_TYPE_AEAD) crypto_unregister_aead(&tmp_algs->alg.aead); } rk_dev->release_crypto(rk_dev, __func__); return err; } static void rk_crypto_unregister(struct rk_crypto_dev *rk_dev) { unsigned int i; char **algs_name; struct rk_crypto_algt *tmp_algs; algs_name = rk_dev->soc_data->valid_algs_name; rk_dev->request_crypto(rk_dev, __func__); for (i = 0; i < rk_dev->soc_data->valid_algs_num; i++, algs_name++) { tmp_algs = rk_crypto_find_algs(rk_dev, *algs_name); if (!tmp_algs) continue; if (tmp_algs->type == ALG_TYPE_CIPHER) crypto_unregister_skcipher(&tmp_algs->alg.crypto); else if (tmp_algs->type == ALG_TYPE_HASH || tmp_algs->type == ALG_TYPE_HMAC) crypto_unregister_ahash(&tmp_algs->alg.hash); else if (tmp_algs->type == ALG_TYPE_ASYM) crypto_unregister_akcipher(&tmp_algs->alg.asym); } rk_dev->release_crypto(rk_dev, __func__); } static void rk_crypto_request(struct rk_crypto_dev *rk_dev, const char *name) { CRYPTO_TRACE("Crypto is requested by %s\n", name); rk_crypto_enable_clk(rk_dev); } static void rk_crypto_release(struct rk_crypto_dev *rk_dev, const char *name) { CRYPTO_TRACE("Crypto is released by %s\n", name); rk_crypto_disable_clk(rk_dev); } static void rk_crypto_action(void *data) { struct rk_crypto_dev *rk_dev = data; if (rk_dev->rst) reset_control_assert(rk_dev->rst); } static char *crypto_no_sm_algs_name[] = { "ecb(aes)", "cbc(aes)", "cfb(aes)", "ofb(aes)", "ctr(aes)", "gcm(aes)", "ecb(des)", "cbc(des)", "cfb(des)", "ofb(des)", "ecb(des3_ede)", "cbc(des3_ede)", "cfb(des3_ede)", "ofb(des3_ede)", "sha1", "sha224", "sha256", "sha384", "sha512", "md5", "hmac(sha1)", "hmac(sha256)", "hmac(sha512)", "hmac(md5)", "rsa" }; static char *crypto_rv1126_algs_name[] = { "ecb(sm4)", "cbc(sm4)", "cfb(sm4)", "ofb(sm4)", "ctr(sm4)", "gcm(sm4)", "ecb(aes)", "cbc(aes)", "cfb(aes)", "ofb(aes)", "ctr(aes)", "gcm(aes)", "ecb(des)", "cbc(des)", "cfb(des)", "ofb(des)", "ecb(des3_ede)", "cbc(des3_ede)", "cfb(des3_ede)", "ofb(des3_ede)", "sha1", "sha256", "sha512", "md5", "sm3", "hmac(sha1)", "hmac(sha256)", "hmac(sha512)", "hmac(md5)", "hmac(sm3)", "rsa" }; static char *crypto_full_algs_name[] = { "ecb(sm4)", "cbc(sm4)", "cfb(sm4)", "ofb(sm4)", "ctr(sm4)", "gcm(sm4)", "ecb(aes)", "cbc(aes)", "cfb(aes)", "ofb(aes)", "ctr(aes)", "gcm(aes)", "ecb(des)", "cbc(des)", "cfb(des)", "ofb(des)", "ecb(des3_ede)", "cbc(des3_ede)", "cfb(des3_ede)", "ofb(des3_ede)", "sha1", "sha224", "sha256", "sha384", "sha512", "md5", "sm3", "hmac(sha1)", "hmac(sha256)", "hmac(sha512)", "hmac(md5)", "hmac(sm3)", "rsa" }; static const struct rk_crypto_soc_data px30_soc_data = RK_CRYPTO_V2_SOC_DATA_INIT(crypto_no_sm_algs_name, false); static const struct rk_crypto_soc_data rv1126_soc_data = RK_CRYPTO_V2_SOC_DATA_INIT(crypto_rv1126_algs_name, true); static const struct rk_crypto_soc_data full_soc_data = RK_CRYPTO_V2_SOC_DATA_INIT(crypto_full_algs_name, false); static const struct rk_crypto_soc_data cryto_v3_soc_data = RK_CRYPTO_V3_SOC_DATA_INIT(crypto_full_algs_name); static char *rk3288_cipher_algs[] = { "ecb(aes)", "cbc(aes)", "ecb(des)", "cbc(des)", "ecb(des3_ede)", "cbc(des3_ede)", "sha1", "sha256", "md5", }; static const struct rk_crypto_soc_data rk3288_soc_data = RK_CRYPTO_V1_SOC_DATA_INIT(rk3288_cipher_algs); static const struct of_device_id crypto_of_id_table[] = { #if IS_ENABLED(CONFIG_CRYPTO_DEV_ROCKCHIP_V3) /* crypto v4 in belows same with crypto-v3*/ { .compatible = "rockchip,crypto-v4", .data = (void *)&cryto_v3_soc_data, }, /* crypto v3 in belows */ { .compatible = "rockchip,crypto-v3", .data = (void *)&cryto_v3_soc_data, }, #endif #if IS_ENABLED(CONFIG_CRYPTO_DEV_ROCKCHIP_V2) /* crypto v2 in belows */ { .compatible = "rockchip,px30-crypto", .data = (void *)&px30_soc_data, }, { .compatible = "rockchip,rv1126-crypto", .data = (void *)&rv1126_soc_data, }, { .compatible = "rockchip,rk3568-crypto", .data = (void *)&full_soc_data, }, { .compatible = "rockchip,rk3588-crypto", .data = (void *)&full_soc_data, }, #endif #if IS_ENABLED(CONFIG_CRYPTO_DEV_ROCKCHIP_V1) /* crypto v1 in belows */ { .compatible = "rockchip,rk3288-crypto", .data = (void *)&rk3288_soc_data, }, #endif { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, crypto_of_id_table); static int rk_crypto_probe(struct platform_device *pdev) { struct resource *res; struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; struct rk_crypto_soc_data *soc_data; const struct of_device_id *match; struct rk_crypto_dev *rk_dev; const char * const *rsts; uint32_t rst_num = 0; int err = 0; rk_dev = devm_kzalloc(&pdev->dev, sizeof(*rk_dev), GFP_KERNEL); if (!rk_dev) { err = -ENOMEM; goto err_crypto; } rk_dev->name = CRYPTO_NAME; match = of_match_node(crypto_of_id_table, np); soc_data = (struct rk_crypto_soc_data *)match->data; rk_dev->soc_data = soc_data; rsts = soc_data->hw_get_rsts(&rst_num); if (rsts && rsts[0]) { rk_dev->rst = devm_reset_control_get(dev, rsts[0]); if (IS_ERR(rk_dev->rst)) { err = PTR_ERR(rk_dev->rst); goto err_crypto; } reset_control_assert(rk_dev->rst); usleep_range(10, 20); reset_control_deassert(rk_dev->rst); } err = devm_add_action_or_reset(dev, rk_crypto_action, rk_dev); if (err) goto err_crypto; spin_lock_init(&rk_dev->lock); /* get crypto base */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rk_dev->reg = devm_ioremap_resource(dev, res); if (IS_ERR(rk_dev->reg)) { err = PTR_ERR(rk_dev->reg); goto err_crypto; } /* get pka base, if pka reg not set, pka reg = crypto + pka offset */ res = platform_get_resource(pdev, IORESOURCE_MEM, 1); rk_dev->pka_reg = devm_ioremap_resource(dev, res); if (IS_ERR(rk_dev->pka_reg)) rk_dev->pka_reg = rk_dev->reg + soc_data->default_pka_offset; rk_dev->clks_num = devm_clk_bulk_get_all(dev, &rk_dev->clk_bulks); if (rk_dev->clks_num < 0) { err = rk_dev->clks_num; dev_err(dev, "failed to get clks property\n"); goto err_crypto; } rk_dev->irq = platform_get_irq(pdev, 0); if (rk_dev->irq < 0) { dev_warn(dev, "control Interrupt is not available.\n"); err = rk_dev->irq; goto err_crypto; } err = devm_request_irq(dev, rk_dev->irq, rk_crypto_irq_handle, IRQF_SHARED, "rk-crypto", pdev); if (err) { dev_err(dev, "irq request failed.\n"); goto err_crypto; } disable_irq(rk_dev->irq); err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (err) { dev_err(dev, "crypto: No suitable DMA available.\n"); goto err_crypto; } rk_dev->dev = dev; rk_dev->hw_info = devm_kzalloc(dev, soc_data->hw_info_size, GFP_KERNEL); if (!rk_dev->hw_info) { err = -ENOMEM; goto err_crypto; } err = soc_data->hw_init(dev, rk_dev->hw_info); if (err) { dev_err(dev, "hw_init failed.\n"); goto err_crypto; } rk_dev->addr_vir = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA32, RK_BUFFER_ORDER); if (!rk_dev->addr_vir) { err = -ENOMEM; dev_err(dev, "__get_free_page failed.\n"); goto err_crypto; } rk_dev->vir_max = RK_BUFFER_SIZE; rk_dev->addr_aad = (void *)__get_free_page(GFP_KERNEL); if (!rk_dev->addr_aad) { err = -ENOMEM; dev_err(dev, "__get_free_page failed.\n"); goto err_crypto; } rk_dev->aad_max = RK_BUFFER_SIZE; platform_set_drvdata(pdev, rk_dev); tasklet_init(&rk_dev->queue_task, rk_crypto_queue_task_cb, (unsigned long)rk_dev); tasklet_init(&rk_dev->done_task, rk_crypto_done_task_cb, (unsigned long)rk_dev); crypto_init_queue(&rk_dev->queue, 50); timer_setup(&rk_dev->timer, rk_crypto_irq_timer_handle, 0); rk_dev->request_crypto = rk_crypto_request; rk_dev->release_crypto = rk_crypto_release; rk_dev->load_data = rk_load_data; rk_dev->unload_data = rk_unload_data; rk_dev->enqueue = rk_crypto_enqueue; rk_dev->busy = false; err = rk_crypto_register(rk_dev); if (err) { dev_err(dev, "err in register alg"); goto err_register_alg; } rk_cryptodev_register_dev(rk_dev->dev, soc_data->crypto_ver); rkcrypto_proc_init(rk_dev); dev_info(dev, "%s Accelerator successfully registered\n", soc_data->crypto_ver); return 0; err_register_alg: tasklet_kill(&rk_dev->queue_task); tasklet_kill(&rk_dev->done_task); err_crypto: return err; } static int rk_crypto_remove(struct platform_device *pdev) { struct rk_crypto_dev *rk_dev = platform_get_drvdata(pdev); rkcrypto_proc_cleanup(rk_dev); rk_cryptodev_unregister_dev(rk_dev->dev); del_timer_sync(&rk_dev->timer); rk_crypto_unregister(rk_dev); tasklet_kill(&rk_dev->done_task); tasklet_kill(&rk_dev->queue_task); if (rk_dev->addr_vir) free_pages((unsigned long)rk_dev->addr_vir, RK_BUFFER_ORDER); if (rk_dev->addr_aad) free_page((unsigned long)rk_dev->addr_aad); rk_dev->soc_data->hw_deinit(&pdev->dev, rk_dev->hw_info); return 0; } static struct platform_driver crypto_driver = { .probe = rk_crypto_probe, .remove = rk_crypto_remove, .driver = { .name = "rk-crypto", .of_match_table = crypto_of_id_table, }, }; module_platform_driver(crypto_driver); MODULE_AUTHOR("Lin Jinhan "); MODULE_DESCRIPTION("Support for Rockchip's cryptographic engine"); MODULE_LICENSE("GPL");