18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * caam - Freescale FSL CAAM support for hw_random
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2011 Freescale Semiconductor, Inc.
68c2ecf20Sopenharmony_ci * Copyright 2018-2019 NXP
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Based on caamalg.c crypto API driver.
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
138c2ecf20Sopenharmony_ci#include <linux/completion.h>
148c2ecf20Sopenharmony_ci#include <linux/atomic.h>
158c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "compat.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "regs.h"
208c2ecf20Sopenharmony_ci#include "intern.h"
218c2ecf20Sopenharmony_ci#include "desc_constr.h"
228c2ecf20Sopenharmony_ci#include "jr.h"
238c2ecf20Sopenharmony_ci#include "error.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define CAAM_RNG_MAX_FIFO_STORE_SIZE	16
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/*
288c2ecf20Sopenharmony_ci * Length of used descriptors, see caam_init_desc()
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_ci#define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ +				\
318c2ecf20Sopenharmony_ci			   CAAM_CMD_SZ +				\
328c2ecf20Sopenharmony_ci			   CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* rng per-device context */
358c2ecf20Sopenharmony_cistruct caam_rng_ctx {
368c2ecf20Sopenharmony_ci	struct hwrng rng;
378c2ecf20Sopenharmony_ci	struct device *jrdev;
388c2ecf20Sopenharmony_ci	struct device *ctrldev;
398c2ecf20Sopenharmony_ci	void *desc_async;
408c2ecf20Sopenharmony_ci	void *desc_sync;
418c2ecf20Sopenharmony_ci	struct work_struct worker;
428c2ecf20Sopenharmony_ci	struct kfifo fifo;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistruct caam_rng_job_ctx {
468c2ecf20Sopenharmony_ci	struct completion *done;
478c2ecf20Sopenharmony_ci	int *err;
488c2ecf20Sopenharmony_ci};
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	return (struct caam_rng_ctx *)r->priv;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
568c2ecf20Sopenharmony_ci			  void *context)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct caam_rng_job_ctx *jctx = context;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (err)
618c2ecf20Sopenharmony_ci		*jctx->err = caam_jr_strstatus(jrdev, err);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	complete(jctx->done);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	init_job_desc(desc, 0);	/* + 1 cmd_sz */
698c2ecf20Sopenharmony_ci	/* Generate random bytes: + 1 cmd_sz */
708c2ecf20Sopenharmony_ci	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
718c2ecf20Sopenharmony_ci			 OP_ALG_PR_ON);
728c2ecf20Sopenharmony_ci	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
738c2ecf20Sopenharmony_ci	append_fifo_store(desc, dst_dma,
748c2ecf20Sopenharmony_ci			  CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
778c2ecf20Sopenharmony_ci			     16, 4, desc, desc_bytes(desc), 1);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return desc;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int caam_rng_read_one(struct device *jrdev,
838c2ecf20Sopenharmony_ci			     void *dst, int len,
848c2ecf20Sopenharmony_ci			     void *desc,
858c2ecf20Sopenharmony_ci			     struct completion *done)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	dma_addr_t dst_dma;
888c2ecf20Sopenharmony_ci	int err, ret = 0;
898c2ecf20Sopenharmony_ci	struct caam_rng_job_ctx jctx = {
908c2ecf20Sopenharmony_ci		.done = done,
918c2ecf20Sopenharmony_ci		.err  = &ret,
928c2ecf20Sopenharmony_ci	};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
978c2ecf20Sopenharmony_ci	if (dma_mapping_error(jrdev, dst_dma)) {
988c2ecf20Sopenharmony_ci		dev_err(jrdev, "unable to map destination memory\n");
998c2ecf20Sopenharmony_ci		return -ENOMEM;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	init_completion(done);
1038c2ecf20Sopenharmony_ci	err = caam_jr_enqueue(jrdev,
1048c2ecf20Sopenharmony_ci			      caam_init_desc(desc, dst_dma),
1058c2ecf20Sopenharmony_ci			      caam_rng_done, &jctx);
1068c2ecf20Sopenharmony_ci	if (err == -EINPROGRESS) {
1078c2ecf20Sopenharmony_ci		wait_for_completion(done);
1088c2ecf20Sopenharmony_ci		err = 0;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	return err ?: (ret ?: len);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic void caam_rng_fill_async(struct caam_rng_ctx *ctx)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct scatterlist sg[1];
1198c2ecf20Sopenharmony_ci	struct completion done;
1208c2ecf20Sopenharmony_ci	int len, nents;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	sg_init_table(sg, ARRAY_SIZE(sg));
1238c2ecf20Sopenharmony_ci	nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
1248c2ecf20Sopenharmony_ci				     CAAM_RNG_MAX_FIFO_STORE_SIZE);
1258c2ecf20Sopenharmony_ci	if (!nents)
1268c2ecf20Sopenharmony_ci		return;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
1298c2ecf20Sopenharmony_ci				sg[0].length,
1308c2ecf20Sopenharmony_ci				ctx->desc_async,
1318c2ecf20Sopenharmony_ci				&done);
1328c2ecf20Sopenharmony_ci	if (len < 0)
1338c2ecf20Sopenharmony_ci		return;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	kfifo_dma_in_finish(&ctx->fifo, len);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic void caam_rng_worker(struct work_struct *work)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
1418c2ecf20Sopenharmony_ci						worker);
1428c2ecf20Sopenharmony_ci	caam_rng_fill_async(ctx);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
1488c2ecf20Sopenharmony_ci	int out;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (wait) {
1518c2ecf20Sopenharmony_ci		struct completion done;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		return caam_rng_read_one(ctx->jrdev, dst, max,
1548c2ecf20Sopenharmony_ci					 ctx->desc_sync, &done);
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	out = kfifo_out(&ctx->fifo, dst, max);
1588c2ecf20Sopenharmony_ci	if (kfifo_is_empty(&ctx->fifo))
1598c2ecf20Sopenharmony_ci		schedule_work(&ctx->worker);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	return out;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void caam_cleanup(struct hwrng *rng)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	flush_work(&ctx->worker);
1698c2ecf20Sopenharmony_ci	caam_jr_free(ctx->jrdev);
1708c2ecf20Sopenharmony_ci	kfifo_free(&ctx->fifo);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int caam_init(struct hwrng *rng)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
1768c2ecf20Sopenharmony_ci	int err;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
1798c2ecf20Sopenharmony_ci				      GFP_DMA | GFP_KERNEL);
1808c2ecf20Sopenharmony_ci	if (!ctx->desc_sync)
1818c2ecf20Sopenharmony_ci		return -ENOMEM;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
1848c2ecf20Sopenharmony_ci				       GFP_DMA | GFP_KERNEL);
1858c2ecf20Sopenharmony_ci	if (!ctx->desc_async)
1868c2ecf20Sopenharmony_ci		return -ENOMEM;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (kfifo_alloc(&ctx->fifo, CAAM_RNG_MAX_FIFO_STORE_SIZE,
1898c2ecf20Sopenharmony_ci			GFP_DMA | GFP_KERNEL))
1908c2ecf20Sopenharmony_ci		return -ENOMEM;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	INIT_WORK(&ctx->worker, caam_rng_worker);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ctx->jrdev = caam_jr_alloc();
1958c2ecf20Sopenharmony_ci	err = PTR_ERR_OR_ZERO(ctx->jrdev);
1968c2ecf20Sopenharmony_ci	if (err) {
1978c2ecf20Sopenharmony_ci		kfifo_free(&ctx->fifo);
1988c2ecf20Sopenharmony_ci		pr_err("Job Ring Device allocation for transform failed\n");
1998c2ecf20Sopenharmony_ci		return err;
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	/*
2038c2ecf20Sopenharmony_ci	 * Fill async buffer to have early randomness data for
2048c2ecf20Sopenharmony_ci	 * hw_random
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	caam_rng_fill_async(ctx);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return 0;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ciint caam_rng_init(struct device *ctrldev);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_civoid caam_rng_exit(struct device *ctrldev)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	devres_release_group(ctrldev, caam_rng_init);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ciint caam_rng_init(struct device *ctrldev)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct caam_rng_ctx *ctx;
2218c2ecf20Sopenharmony_ci	u32 rng_inst;
2228c2ecf20Sopenharmony_ci	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
2238c2ecf20Sopenharmony_ci	int ret;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	/* Check for an instantiated RNG before registration */
2268c2ecf20Sopenharmony_ci	if (priv->era < 10)
2278c2ecf20Sopenharmony_ci		rng_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) &
2288c2ecf20Sopenharmony_ci			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
2298c2ecf20Sopenharmony_ci	else
2308c2ecf20Sopenharmony_ci		rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (!rng_inst)
2338c2ecf20Sopenharmony_ci		return 0;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
2368c2ecf20Sopenharmony_ci		return -ENOMEM;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
2398c2ecf20Sopenharmony_ci	if (!ctx)
2408c2ecf20Sopenharmony_ci		return -ENOMEM;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	ctx->ctrldev = ctrldev;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	ctx->rng.name    = "rng-caam";
2458c2ecf20Sopenharmony_ci	ctx->rng.init    = caam_init;
2468c2ecf20Sopenharmony_ci	ctx->rng.cleanup = caam_cleanup;
2478c2ecf20Sopenharmony_ci	ctx->rng.read    = caam_read;
2488c2ecf20Sopenharmony_ci	ctx->rng.priv    = (unsigned long)ctx;
2498c2ecf20Sopenharmony_ci	ctx->rng.quality = 1024;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	dev_info(ctrldev, "registering rng-caam\n");
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	ret = devm_hwrng_register(ctrldev, &ctx->rng);
2548c2ecf20Sopenharmony_ci	if (ret) {
2558c2ecf20Sopenharmony_ci		caam_rng_exit(ctrldev);
2568c2ecf20Sopenharmony_ci		return ret;
2578c2ecf20Sopenharmony_ci	}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	devres_close_group(ctrldev, caam_rng_init);
2608c2ecf20Sopenharmony_ci	return 0;
2618c2ecf20Sopenharmony_ci}
262