162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * caam - Freescale FSL CAAM support for hw_random
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc.
662306a36Sopenharmony_ci * Copyright 2018-2019, 2023 NXP
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on caamalg.c crypto API driver.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/hw_random.h>
1362306a36Sopenharmony_ci#include <linux/completion.h>
1462306a36Sopenharmony_ci#include <linux/atomic.h>
1562306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1662306a36Sopenharmony_ci#include <linux/kernel.h>
1762306a36Sopenharmony_ci#include <linux/kfifo.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "compat.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "regs.h"
2262306a36Sopenharmony_ci#include "intern.h"
2362306a36Sopenharmony_ci#include "desc_constr.h"
2462306a36Sopenharmony_ci#include "jr.h"
2562306a36Sopenharmony_ci#include "error.h"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define CAAM_RNG_MAX_FIFO_STORE_SIZE	16
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/*
3062306a36Sopenharmony_ci * Length of used descriptors, see caam_init_desc()
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ +				\
3362306a36Sopenharmony_ci			   CAAM_CMD_SZ +				\
3462306a36Sopenharmony_ci			   CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/* rng per-device context */
3762306a36Sopenharmony_cistruct caam_rng_ctx {
3862306a36Sopenharmony_ci	struct hwrng rng;
3962306a36Sopenharmony_ci	struct device *jrdev;
4062306a36Sopenharmony_ci	struct device *ctrldev;
4162306a36Sopenharmony_ci	void *desc_async;
4262306a36Sopenharmony_ci	void *desc_sync;
4362306a36Sopenharmony_ci	struct work_struct worker;
4462306a36Sopenharmony_ci	struct kfifo fifo;
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistruct caam_rng_job_ctx {
4862306a36Sopenharmony_ci	struct completion *done;
4962306a36Sopenharmony_ci	int *err;
5062306a36Sopenharmony_ci};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return (struct caam_rng_ctx *)r->priv;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
5862306a36Sopenharmony_ci			  void *context)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct caam_rng_job_ctx *jctx = context;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	if (err)
6362306a36Sopenharmony_ci		*jctx->err = caam_jr_strstatus(jrdev, err);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	complete(jctx->done);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	init_job_desc(desc, 0);	/* + 1 cmd_sz */
7162306a36Sopenharmony_ci	/* Generate random bytes: + 1 cmd_sz */
7262306a36Sopenharmony_ci	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
7362306a36Sopenharmony_ci			 OP_ALG_PR_ON);
7462306a36Sopenharmony_ci	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
7562306a36Sopenharmony_ci	append_fifo_store(desc, dst_dma,
7662306a36Sopenharmony_ci			  CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
7962306a36Sopenharmony_ci			     16, 4, desc, desc_bytes(desc), 1);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return desc;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int caam_rng_read_one(struct device *jrdev,
8562306a36Sopenharmony_ci			     void *dst, int len,
8662306a36Sopenharmony_ci			     void *desc,
8762306a36Sopenharmony_ci			     struct completion *done)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	dma_addr_t dst_dma;
9062306a36Sopenharmony_ci	int err, ret = 0;
9162306a36Sopenharmony_ci	struct caam_rng_job_ctx jctx = {
9262306a36Sopenharmony_ci		.done = done,
9362306a36Sopenharmony_ci		.err  = &ret,
9462306a36Sopenharmony_ci	};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
9962306a36Sopenharmony_ci	if (dma_mapping_error(jrdev, dst_dma)) {
10062306a36Sopenharmony_ci		dev_err(jrdev, "unable to map destination memory\n");
10162306a36Sopenharmony_ci		return -ENOMEM;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	init_completion(done);
10562306a36Sopenharmony_ci	err = caam_jr_enqueue(jrdev,
10662306a36Sopenharmony_ci			      caam_init_desc(desc, dst_dma),
10762306a36Sopenharmony_ci			      caam_rng_done, &jctx);
10862306a36Sopenharmony_ci	if (err == -EINPROGRESS) {
10962306a36Sopenharmony_ci		wait_for_completion(done);
11062306a36Sopenharmony_ci		err = 0;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	return err ?: (ret ?: len);
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic void caam_rng_fill_async(struct caam_rng_ctx *ctx)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	struct scatterlist sg[1];
12162306a36Sopenharmony_ci	struct completion done;
12262306a36Sopenharmony_ci	int len, nents;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	sg_init_table(sg, ARRAY_SIZE(sg));
12562306a36Sopenharmony_ci	nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
12662306a36Sopenharmony_ci				     CAAM_RNG_MAX_FIFO_STORE_SIZE);
12762306a36Sopenharmony_ci	if (!nents)
12862306a36Sopenharmony_ci		return;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
13162306a36Sopenharmony_ci				sg[0].length,
13262306a36Sopenharmony_ci				ctx->desc_async,
13362306a36Sopenharmony_ci				&done);
13462306a36Sopenharmony_ci	if (len < 0)
13562306a36Sopenharmony_ci		return;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	kfifo_dma_in_finish(&ctx->fifo, len);
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic void caam_rng_worker(struct work_struct *work)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
14362306a36Sopenharmony_ci						worker);
14462306a36Sopenharmony_ci	caam_rng_fill_async(ctx);
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
15062306a36Sopenharmony_ci	int out;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (wait) {
15362306a36Sopenharmony_ci		struct completion done;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		return caam_rng_read_one(ctx->jrdev, dst, max,
15662306a36Sopenharmony_ci					 ctx->desc_sync, &done);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	out = kfifo_out(&ctx->fifo, dst, max);
16062306a36Sopenharmony_ci	if (kfifo_is_empty(&ctx->fifo))
16162306a36Sopenharmony_ci		schedule_work(&ctx->worker);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	return out;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_cistatic void caam_cleanup(struct hwrng *rng)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	flush_work(&ctx->worker);
17162306a36Sopenharmony_ci	caam_jr_free(ctx->jrdev);
17262306a36Sopenharmony_ci	kfifo_free(&ctx->fifo);
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
17662306a36Sopenharmony_cistatic inline void test_len(struct hwrng *rng, size_t len, bool wait)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	u8 *buf;
17962306a36Sopenharmony_ci	int read_len;
18062306a36Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
18162306a36Sopenharmony_ci	struct device *dev = ctx->ctrldev;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	buf = kcalloc(CAAM_RNG_MAX_FIFO_STORE_SIZE, sizeof(u8), GFP_KERNEL);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	while (len > 0) {
18662306a36Sopenharmony_ci		read_len = rng->read(rng, buf, len, wait);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci		if (read_len < 0 || (read_len == 0 && wait)) {
18962306a36Sopenharmony_ci			dev_err(dev, "RNG Read FAILED received %d bytes\n",
19062306a36Sopenharmony_ci				read_len);
19162306a36Sopenharmony_ci			kfree(buf);
19262306a36Sopenharmony_ci			return;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		print_hex_dump_debug("random bytes@: ",
19662306a36Sopenharmony_ci			DUMP_PREFIX_ADDRESS, 16, 4,
19762306a36Sopenharmony_ci			buf, read_len, 1);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		len = len - read_len;
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	kfree(buf);
20362306a36Sopenharmony_ci}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_cistatic inline void test_mode_once(struct hwrng *rng, bool wait)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	test_len(rng, 32, wait);
20862306a36Sopenharmony_ci	test_len(rng, 64, wait);
20962306a36Sopenharmony_ci	test_len(rng, 128, wait);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic void self_test(struct hwrng *rng)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	pr_info("Executing RNG SELF-TEST with wait\n");
21562306a36Sopenharmony_ci	test_mode_once(rng, true);
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci#endif
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_cistatic int caam_init(struct hwrng *rng)
22062306a36Sopenharmony_ci{
22162306a36Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
22262306a36Sopenharmony_ci	int err;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
22562306a36Sopenharmony_ci				      GFP_KERNEL);
22662306a36Sopenharmony_ci	if (!ctx->desc_sync)
22762306a36Sopenharmony_ci		return -ENOMEM;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
23062306a36Sopenharmony_ci				       GFP_KERNEL);
23162306a36Sopenharmony_ci	if (!ctx->desc_async)
23262306a36Sopenharmony_ci		return -ENOMEM;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (kfifo_alloc(&ctx->fifo, ALIGN(CAAM_RNG_MAX_FIFO_STORE_SIZE,
23562306a36Sopenharmony_ci					  dma_get_cache_alignment()),
23662306a36Sopenharmony_ci			GFP_KERNEL))
23762306a36Sopenharmony_ci		return -ENOMEM;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	INIT_WORK(&ctx->worker, caam_rng_worker);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ctx->jrdev = caam_jr_alloc();
24262306a36Sopenharmony_ci	err = PTR_ERR_OR_ZERO(ctx->jrdev);
24362306a36Sopenharmony_ci	if (err) {
24462306a36Sopenharmony_ci		kfifo_free(&ctx->fifo);
24562306a36Sopenharmony_ci		pr_err("Job Ring Device allocation for transform failed\n");
24662306a36Sopenharmony_ci		return err;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	/*
25062306a36Sopenharmony_ci	 * Fill async buffer to have early randomness data for
25162306a36Sopenharmony_ci	 * hw_random
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	caam_rng_fill_async(ctx);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return 0;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ciint caam_rng_init(struct device *ctrldev);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_civoid caam_rng_exit(struct device *ctrldev)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	devres_release_group(ctrldev, caam_rng_init);
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ciint caam_rng_init(struct device *ctrldev)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	struct caam_rng_ctx *ctx;
26862306a36Sopenharmony_ci	u32 rng_inst;
26962306a36Sopenharmony_ci	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Check for an instantiated RNG before registration */
27362306a36Sopenharmony_ci	if (priv->era < 10)
27462306a36Sopenharmony_ci		rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
27562306a36Sopenharmony_ci			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
27662306a36Sopenharmony_ci	else
27762306a36Sopenharmony_ci		rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (!rng_inst)
28062306a36Sopenharmony_ci		return 0;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
28362306a36Sopenharmony_ci		return -ENOMEM;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
28662306a36Sopenharmony_ci	if (!ctx)
28762306a36Sopenharmony_ci		return -ENOMEM;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	ctx->ctrldev = ctrldev;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ctx->rng.name    = "rng-caam";
29262306a36Sopenharmony_ci	ctx->rng.init    = caam_init;
29362306a36Sopenharmony_ci	ctx->rng.cleanup = caam_cleanup;
29462306a36Sopenharmony_ci	ctx->rng.read    = caam_read;
29562306a36Sopenharmony_ci	ctx->rng.priv    = (unsigned long)ctx;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	dev_info(ctrldev, "registering rng-caam\n");
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	ret = devm_hwrng_register(ctrldev, &ctx->rng);
30062306a36Sopenharmony_ci	if (ret) {
30162306a36Sopenharmony_ci		caam_rng_exit(ctrldev);
30262306a36Sopenharmony_ci		return ret;
30362306a36Sopenharmony_ci	}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci#ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
30662306a36Sopenharmony_ci	self_test(&ctx->rng);
30762306a36Sopenharmony_ci#endif
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	devres_close_group(ctrldev, caam_rng_init);
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_ci}
312