123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * caam - Freescale FSL CAAM support for hw_random
- *
- * Copyright 2011 Freescale Semiconductor, Inc.
- * Copyright 2018-2019 NXP
- *
- * Based on caamalg.c crypto API driver.
- *
- */
- #include <linux/hw_random.h>
- #include <linux/completion.h>
- #include <linux/atomic.h>
- #include <linux/kfifo.h>
- #include "compat.h"
- #include "regs.h"
- #include "intern.h"
- #include "desc_constr.h"
- #include "jr.h"
- #include "error.h"
- #define CAAM_RNG_MAX_FIFO_STORE_SIZE 16
- /*
- * Length of used descriptors, see caam_init_desc()
- */
- #define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ + \
- CAAM_CMD_SZ + \
- CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
- /* rng per-device context */
- struct caam_rng_ctx {
- struct hwrng rng;
- struct device *jrdev;
- struct device *ctrldev;
- void *desc_async;
- void *desc_sync;
- struct work_struct worker;
- struct kfifo fifo;
- };
- struct caam_rng_job_ctx {
- struct completion *done;
- int *err;
- };
- static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
- {
- return (struct caam_rng_ctx *)r->priv;
- }
- static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
- void *context)
- {
- struct caam_rng_job_ctx *jctx = context;
- if (err)
- *jctx->err = caam_jr_strstatus(jrdev, err);
- complete(jctx->done);
- }
- static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
- {
- init_job_desc(desc, 0); /* + 1 cmd_sz */
- /* Generate random bytes: + 1 cmd_sz */
- append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
- OP_ALG_PR_ON);
- /* Store bytes: + 1 cmd_sz + caam_ptr_sz */
- append_fifo_store(desc, dst_dma,
- CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
- print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
- 16, 4, desc, desc_bytes(desc), 1);
- return desc;
- }
- static int caam_rng_read_one(struct device *jrdev,
- void *dst, int len,
- void *desc,
- struct completion *done)
- {
- dma_addr_t dst_dma;
- int err, ret = 0;
- struct caam_rng_job_ctx jctx = {
- .done = done,
- .err = &ret,
- };
- len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
- dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
- if (dma_mapping_error(jrdev, dst_dma)) {
- dev_err(jrdev, "unable to map destination memory\n");
- return -ENOMEM;
- }
- init_completion(done);
- err = caam_jr_enqueue(jrdev,
- caam_init_desc(desc, dst_dma),
- caam_rng_done, &jctx);
- if (err == -EINPROGRESS) {
- wait_for_completion(done);
- err = 0;
- }
- dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
- return err ?: (ret ?: len);
- }
- static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
- {
- struct scatterlist sg[1];
- struct completion done;
- int len, nents;
- sg_init_table(sg, ARRAY_SIZE(sg));
- nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
- CAAM_RNG_MAX_FIFO_STORE_SIZE);
- if (!nents)
- return;
- len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
- sg[0].length,
- ctx->desc_async,
- &done);
- if (len < 0)
- return;
- kfifo_dma_in_finish(&ctx->fifo, len);
- }
- static void caam_rng_worker(struct work_struct *work)
- {
- struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
- worker);
- caam_rng_fill_async(ctx);
- }
- static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
- {
- struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
- int out;
- if (wait) {
- struct completion done;
- return caam_rng_read_one(ctx->jrdev, dst, max,
- ctx->desc_sync, &done);
- }
- out = kfifo_out(&ctx->fifo, dst, max);
- if (kfifo_is_empty(&ctx->fifo))
- schedule_work(&ctx->worker);
- return out;
- }
- static void caam_cleanup(struct hwrng *rng)
- {
- struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
- flush_work(&ctx->worker);
- caam_jr_free(ctx->jrdev);
- kfifo_free(&ctx->fifo);
- }
- static int caam_init(struct hwrng *rng)
- {
- struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
- int err;
- ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
- GFP_DMA | GFP_KERNEL);
- if (!ctx->desc_sync)
- return -ENOMEM;
- ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
- GFP_DMA | GFP_KERNEL);
- if (!ctx->desc_async)
- return -ENOMEM;
- if (kfifo_alloc(&ctx->fifo, CAAM_RNG_MAX_FIFO_STORE_SIZE,
- GFP_DMA | GFP_KERNEL))
- return -ENOMEM;
- INIT_WORK(&ctx->worker, caam_rng_worker);
- ctx->jrdev = caam_jr_alloc();
- err = PTR_ERR_OR_ZERO(ctx->jrdev);
- if (err) {
- kfifo_free(&ctx->fifo);
- pr_err("Job Ring Device allocation for transform failed\n");
- return err;
- }
- /*
- * Fill async buffer to have early randomness data for
- * hw_random
- */
- caam_rng_fill_async(ctx);
- return 0;
- }
- int caam_rng_init(struct device *ctrldev);
- void caam_rng_exit(struct device *ctrldev)
- {
- devres_release_group(ctrldev, caam_rng_init);
- }
- int caam_rng_init(struct device *ctrldev)
- {
- struct caam_rng_ctx *ctx;
- u32 rng_inst;
- struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
- int ret;
- /* Check for an instantiated RNG before registration */
- if (priv->era < 10)
- rng_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) &
- CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
- else
- rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK;
- if (!rng_inst)
- return 0;
- if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
- return -ENOMEM;
- ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
- return -ENOMEM;
- ctx->ctrldev = ctrldev;
- ctx->rng.name = "rng-caam";
- ctx->rng.init = caam_init;
- ctx->rng.cleanup = caam_cleanup;
- ctx->rng.read = caam_read;
- ctx->rng.priv = (unsigned long)ctx;
- ctx->rng.quality = 1024;
- dev_info(ctrldev, "registering rng-caam\n");
- ret = devm_hwrng_register(ctrldev, &ctx->rng);
- if (ret) {
- caam_rng_exit(ctrldev);
- return ret;
- }
- devres_close_group(ctrldev, caam_rng_init);
- return 0;
- }
|