1618b5dc4SHoria Geantă // SPDX-License-Identifier: GPL-2.0+
2e24f7c9eSYuan Kang /*
3e24f7c9eSYuan Kang * caam - Freescale FSL CAAM support for hw_random
4e24f7c9eSYuan Kang *
5e24f7c9eSYuan Kang * Copyright 2011 Freescale Semiconductor, Inc.
6ae1dd17dSHoria GeantA * Copyright 2018-2019, 2023 NXP
7e24f7c9eSYuan Kang *
8e24f7c9eSYuan Kang * Based on caamalg.c crypto API driver.
9e24f7c9eSYuan Kang *
10e24f7c9eSYuan Kang */
11e24f7c9eSYuan Kang
12e24f7c9eSYuan Kang #include <linux/hw_random.h>
13e24f7c9eSYuan Kang #include <linux/completion.h>
14e24f7c9eSYuan Kang #include <linux/atomic.h>
15199354d7SHerbert Xu #include <linux/dma-mapping.h>
16199354d7SHerbert Xu #include <linux/kernel.h>
172c5e88dcSAndrey Smirnov #include <linux/kfifo.h>
18e24f7c9eSYuan Kang
19e24f7c9eSYuan Kang #include "compat.h"
20e24f7c9eSYuan Kang
21e24f7c9eSYuan Kang #include "regs.h"
22e24f7c9eSYuan Kang #include "intern.h"
23e24f7c9eSYuan Kang #include "desc_constr.h"
24e24f7c9eSYuan Kang #include "jr.h"
25e24f7c9eSYuan Kang #include "error.h"
26e24f7c9eSYuan Kang
27ea53756dSAndrey Smirnov #define CAAM_RNG_MAX_FIFO_STORE_SIZE 16
282c5e88dcSAndrey Smirnov
29e24f7c9eSYuan Kang /*
302c5e88dcSAndrey Smirnov * Length of used descriptors, see caam_init_desc()
31e24f7c9eSYuan Kang */
322c5e88dcSAndrey Smirnov #define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ + \
332c5e88dcSAndrey Smirnov CAAM_CMD_SZ + \
342c5e88dcSAndrey Smirnov CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
35e24f7c9eSYuan Kang
36e24f7c9eSYuan Kang /* rng per-device context */
37e24f7c9eSYuan Kang struct caam_rng_ctx {
381517f63cSAndrey Smirnov struct hwrng rng;
39e24f7c9eSYuan Kang struct device *jrdev;
402c5e88dcSAndrey Smirnov struct device *ctrldev;
412c5e88dcSAndrey Smirnov void *desc_async;
422c5e88dcSAndrey Smirnov void *desc_sync;
432c5e88dcSAndrey Smirnov struct work_struct worker;
442c5e88dcSAndrey Smirnov struct kfifo fifo;
45e24f7c9eSYuan Kang };
46e24f7c9eSYuan Kang
4732107e43SAndrey Smirnov struct caam_rng_job_ctx {
4832107e43SAndrey Smirnov struct completion *done;
4932107e43SAndrey Smirnov int *err;
5032107e43SAndrey Smirnov };
5132107e43SAndrey Smirnov
to_caam_rng_ctx(struct hwrng * r)521517f63cSAndrey Smirnov static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
531517f63cSAndrey Smirnov {
541517f63cSAndrey Smirnov return (struct caam_rng_ctx *)r->priv;
551517f63cSAndrey Smirnov }
564e3a61c5SIuliana Prodan
caam_rng_done(struct device * jrdev,u32 * desc,u32 err,void * context)572c5e88dcSAndrey Smirnov static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
582c5e88dcSAndrey Smirnov void *context)
59e24f7c9eSYuan Kang {
6032107e43SAndrey Smirnov struct caam_rng_job_ctx *jctx = context;
61e24f7c9eSYuan Kang
62fa9659cdSMarek Vasut if (err)
6332107e43SAndrey Smirnov *jctx->err = caam_jr_strstatus(jrdev, err);
64e24f7c9eSYuan Kang
6532107e43SAndrey Smirnov complete(jctx->done);
66e24f7c9eSYuan Kang }
67e24f7c9eSYuan Kang
caam_init_desc(u32 * desc,dma_addr_t dst_dma)68ea53756dSAndrey Smirnov static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
69e24f7c9eSYuan Kang {
702c5e88dcSAndrey Smirnov init_job_desc(desc, 0); /* + 1 cmd_sz */
712c5e88dcSAndrey Smirnov /* Generate random bytes: + 1 cmd_sz */
72358ba762SAndrey Smirnov append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
73358ba762SAndrey Smirnov OP_ALG_PR_ON);
742c5e88dcSAndrey Smirnov /* Store bytes: + 1 cmd_sz + caam_ptr_sz */
75ea53756dSAndrey Smirnov append_fifo_store(desc, dst_dma,
76ea53756dSAndrey Smirnov CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
772c5e88dcSAndrey Smirnov
782c5e88dcSAndrey Smirnov print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
792c5e88dcSAndrey Smirnov 16, 4, desc, desc_bytes(desc), 1);
802c5e88dcSAndrey Smirnov
812c5e88dcSAndrey Smirnov return desc;
822c5e88dcSAndrey Smirnov }
832c5e88dcSAndrey Smirnov
caam_rng_read_one(struct device * jrdev,void * dst,int len,void * desc,struct completion * done)842c5e88dcSAndrey Smirnov static int caam_rng_read_one(struct device *jrdev,
852c5e88dcSAndrey Smirnov void *dst, int len,
862c5e88dcSAndrey Smirnov void *desc,
872c5e88dcSAndrey Smirnov struct completion *done)
882c5e88dcSAndrey Smirnov {
892c5e88dcSAndrey Smirnov dma_addr_t dst_dma;
9032107e43SAndrey Smirnov int err, ret = 0;
9132107e43SAndrey Smirnov struct caam_rng_job_ctx jctx = {
9232107e43SAndrey Smirnov .done = done,
9332107e43SAndrey Smirnov .err = &ret,
9432107e43SAndrey Smirnov };
95e24f7c9eSYuan Kang
96ea53756dSAndrey Smirnov len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
97e24f7c9eSYuan Kang
982c5e88dcSAndrey Smirnov dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
992c5e88dcSAndrey Smirnov if (dma_mapping_error(jrdev, dst_dma)) {
1002c5e88dcSAndrey Smirnov dev_err(jrdev, "unable to map destination memory\n");
1012c5e88dcSAndrey Smirnov return -ENOMEM;
102e24f7c9eSYuan Kang }
103e24f7c9eSYuan Kang
1042c5e88dcSAndrey Smirnov init_completion(done);
1052c5e88dcSAndrey Smirnov err = caam_jr_enqueue(jrdev,
106ea53756dSAndrey Smirnov caam_init_desc(desc, dst_dma),
10732107e43SAndrey Smirnov caam_rng_done, &jctx);
1082c5e88dcSAndrey Smirnov if (err == -EINPROGRESS) {
1092c5e88dcSAndrey Smirnov wait_for_completion(done);
1102c5e88dcSAndrey Smirnov err = 0;
1112c5e88dcSAndrey Smirnov }
1122c5e88dcSAndrey Smirnov
1132c5e88dcSAndrey Smirnov dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
1142c5e88dcSAndrey Smirnov
11532107e43SAndrey Smirnov return err ?: (ret ?: len);
1162c5e88dcSAndrey Smirnov }
1172c5e88dcSAndrey Smirnov
caam_rng_fill_async(struct caam_rng_ctx * ctx)1182c5e88dcSAndrey Smirnov static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
1192c5e88dcSAndrey Smirnov {
1202c5e88dcSAndrey Smirnov struct scatterlist sg[1];
1212c5e88dcSAndrey Smirnov struct completion done;
1222c5e88dcSAndrey Smirnov int len, nents;
1232c5e88dcSAndrey Smirnov
1242c5e88dcSAndrey Smirnov sg_init_table(sg, ARRAY_SIZE(sg));
1252c5e88dcSAndrey Smirnov nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
126ea53756dSAndrey Smirnov CAAM_RNG_MAX_FIFO_STORE_SIZE);
1272c5e88dcSAndrey Smirnov if (!nents)
1282c5e88dcSAndrey Smirnov return;
1292c5e88dcSAndrey Smirnov
1302c5e88dcSAndrey Smirnov len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
1312c5e88dcSAndrey Smirnov sg[0].length,
1322c5e88dcSAndrey Smirnov ctx->desc_async,
1332c5e88dcSAndrey Smirnov &done);
1342c5e88dcSAndrey Smirnov if (len < 0)
1352c5e88dcSAndrey Smirnov return;
1362c5e88dcSAndrey Smirnov
1372c5e88dcSAndrey Smirnov kfifo_dma_in_finish(&ctx->fifo, len);
1382c5e88dcSAndrey Smirnov }
1392c5e88dcSAndrey Smirnov
caam_rng_worker(struct work_struct * work)1402c5e88dcSAndrey Smirnov static void caam_rng_worker(struct work_struct *work)
1412c5e88dcSAndrey Smirnov {
1422c5e88dcSAndrey Smirnov struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
1432c5e88dcSAndrey Smirnov worker);
1442c5e88dcSAndrey Smirnov caam_rng_fill_async(ctx);
1452c5e88dcSAndrey Smirnov }
1462c5e88dcSAndrey Smirnov
caam_read(struct hwrng * rng,void * dst,size_t max,bool wait)1472c5e88dcSAndrey Smirnov static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
148e24f7c9eSYuan Kang {
1491517f63cSAndrey Smirnov struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
1502c5e88dcSAndrey Smirnov int out;
151e24f7c9eSYuan Kang
1522c5e88dcSAndrey Smirnov if (wait) {
1532c5e88dcSAndrey Smirnov struct completion done;
154e24f7c9eSYuan Kang
1552c5e88dcSAndrey Smirnov return caam_rng_read_one(ctx->jrdev, dst, max,
1562c5e88dcSAndrey Smirnov ctx->desc_sync, &done);
157e24f7c9eSYuan Kang }
158e24f7c9eSYuan Kang
1592c5e88dcSAndrey Smirnov out = kfifo_out(&ctx->fifo, dst, max);
160ea53756dSAndrey Smirnov if (kfifo_is_empty(&ctx->fifo))
1612c5e88dcSAndrey Smirnov schedule_work(&ctx->worker);
162e24f7c9eSYuan Kang
1632c5e88dcSAndrey Smirnov return out;
164e24f7c9eSYuan Kang }
165e24f7c9eSYuan Kang
caam_cleanup(struct hwrng * rng)166e24f7c9eSYuan Kang static void caam_cleanup(struct hwrng *rng)
167e24f7c9eSYuan Kang {
1681517f63cSAndrey Smirnov struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
169e24f7c9eSYuan Kang
1702c5e88dcSAndrey Smirnov flush_work(&ctx->worker);
1711517f63cSAndrey Smirnov caam_jr_free(ctx->jrdev);
1722c5e88dcSAndrey Smirnov kfifo_free(&ctx->fifo);
173e24f7c9eSYuan Kang }
174e24f7c9eSYuan Kang
175*2be0d806SVictoria Milhoan (b42089) #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
test_len(struct hwrng * rng,size_t len,bool wait)176*2be0d806SVictoria Milhoan (b42089) static inline void test_len(struct hwrng *rng, size_t len, bool wait)
177*2be0d806SVictoria Milhoan (b42089) {
178*2be0d806SVictoria Milhoan (b42089) u8 *buf;
179*2be0d806SVictoria Milhoan (b42089) int read_len;
180*2be0d806SVictoria Milhoan (b42089) struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
181*2be0d806SVictoria Milhoan (b42089) struct device *dev = ctx->ctrldev;
182*2be0d806SVictoria Milhoan (b42089)
183*2be0d806SVictoria Milhoan (b42089) buf = kcalloc(CAAM_RNG_MAX_FIFO_STORE_SIZE, sizeof(u8), GFP_KERNEL);
184*2be0d806SVictoria Milhoan (b42089)
185*2be0d806SVictoria Milhoan (b42089) while (len > 0) {
186*2be0d806SVictoria Milhoan (b42089) read_len = rng->read(rng, buf, len, wait);
187*2be0d806SVictoria Milhoan (b42089)
188*2be0d806SVictoria Milhoan (b42089) if (read_len < 0 || (read_len == 0 && wait)) {
189*2be0d806SVictoria Milhoan (b42089) dev_err(dev, "RNG Read FAILED received %d bytes\n",
190*2be0d806SVictoria Milhoan (b42089) read_len);
191*2be0d806SVictoria Milhoan (b42089) kfree(buf);
192*2be0d806SVictoria Milhoan (b42089) return;
193*2be0d806SVictoria Milhoan (b42089) }
194*2be0d806SVictoria Milhoan (b42089)
195*2be0d806SVictoria Milhoan (b42089) print_hex_dump_debug("random bytes@: ",
196*2be0d806SVictoria Milhoan (b42089) DUMP_PREFIX_ADDRESS, 16, 4,
197*2be0d806SVictoria Milhoan (b42089) buf, read_len, 1);
198*2be0d806SVictoria Milhoan (b42089)
199*2be0d806SVictoria Milhoan (b42089) len = len - read_len;
200*2be0d806SVictoria Milhoan (b42089) }
201*2be0d806SVictoria Milhoan (b42089)
202*2be0d806SVictoria Milhoan (b42089) kfree(buf);
203*2be0d806SVictoria Milhoan (b42089) }
204*2be0d806SVictoria Milhoan (b42089)
test_mode_once(struct hwrng * rng,bool wait)205*2be0d806SVictoria Milhoan (b42089) static inline void test_mode_once(struct hwrng *rng, bool wait)
206*2be0d806SVictoria Milhoan (b42089) {
207*2be0d806SVictoria Milhoan (b42089) test_len(rng, 32, wait);
208*2be0d806SVictoria Milhoan (b42089) test_len(rng, 64, wait);
209*2be0d806SVictoria Milhoan (b42089) test_len(rng, 128, wait);
210*2be0d806SVictoria Milhoan (b42089) }
211*2be0d806SVictoria Milhoan (b42089)
self_test(struct hwrng * rng)212*2be0d806SVictoria Milhoan (b42089) static void self_test(struct hwrng *rng)
213*2be0d806SVictoria Milhoan (b42089) {
214*2be0d806SVictoria Milhoan (b42089) pr_info("Executing RNG SELF-TEST with wait\n");
215*2be0d806SVictoria Milhoan (b42089) test_mode_once(rng, true);
216*2be0d806SVictoria Milhoan (b42089) }
217*2be0d806SVictoria Milhoan (b42089) #endif
218*2be0d806SVictoria Milhoan (b42089)
caam_init(struct hwrng * rng)2198483c831SAndrey Smirnov static int caam_init(struct hwrng *rng)
220e24f7c9eSYuan Kang {
2211517f63cSAndrey Smirnov struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
222ce572085SHoria Geanta int err;
223ce572085SHoria Geanta
2242c5e88dcSAndrey Smirnov ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
225199354d7SHerbert Xu GFP_KERNEL);
2262c5e88dcSAndrey Smirnov if (!ctx->desc_sync)
2272c5e88dcSAndrey Smirnov return -ENOMEM;
2282c5e88dcSAndrey Smirnov
2292c5e88dcSAndrey Smirnov ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
230199354d7SHerbert Xu GFP_KERNEL);
2312c5e88dcSAndrey Smirnov if (!ctx->desc_async)
2322c5e88dcSAndrey Smirnov return -ENOMEM;
2332c5e88dcSAndrey Smirnov
234199354d7SHerbert Xu if (kfifo_alloc(&ctx->fifo, ALIGN(CAAM_RNG_MAX_FIFO_STORE_SIZE,
235199354d7SHerbert Xu dma_get_cache_alignment()),
236199354d7SHerbert Xu GFP_KERNEL))
2372c5e88dcSAndrey Smirnov return -ENOMEM;
2382c5e88dcSAndrey Smirnov
2392c5e88dcSAndrey Smirnov INIT_WORK(&ctx->worker, caam_rng_worker);
2402c5e88dcSAndrey Smirnov
2418483c831SAndrey Smirnov ctx->jrdev = caam_jr_alloc();
2428483c831SAndrey Smirnov err = PTR_ERR_OR_ZERO(ctx->jrdev);
2438483c831SAndrey Smirnov if (err) {
2442c5e88dcSAndrey Smirnov kfifo_free(&ctx->fifo);
2458483c831SAndrey Smirnov pr_err("Job Ring Device allocation for transform failed\n");
2468483c831SAndrey Smirnov return err;
2478483c831SAndrey Smirnov }
248ce572085SHoria Geanta
2492c5e88dcSAndrey Smirnov /*
2502c5e88dcSAndrey Smirnov * Fill async buffer to have early randomness data for
2512c5e88dcSAndrey Smirnov * hw_random
2522c5e88dcSAndrey Smirnov */
2532c5e88dcSAndrey Smirnov caam_rng_fill_async(ctx);
2548483c831SAndrey Smirnov
2558483c831SAndrey Smirnov return 0;
256e24f7c9eSYuan Kang }
257e24f7c9eSYuan Kang
2581517f63cSAndrey Smirnov int caam_rng_init(struct device *ctrldev);
259e24f7c9eSYuan Kang
caam_rng_exit(struct device * ctrldev)2601517f63cSAndrey Smirnov void caam_rng_exit(struct device *ctrldev)
261e24f7c9eSYuan Kang {
2621517f63cSAndrey Smirnov devres_release_group(ctrldev, caam_rng_init);
263e24f7c9eSYuan Kang }
264e24f7c9eSYuan Kang
caam_rng_init(struct device * ctrldev)2651b46c90cSHoria Geantă int caam_rng_init(struct device *ctrldev)
266e24f7c9eSYuan Kang {
2671517f63cSAndrey Smirnov struct caam_rng_ctx *ctx;
268d239b10dSHoria Geantă u32 rng_inst;
2691b46c90cSHoria Geantă struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
2701517f63cSAndrey Smirnov int ret;
27135af6403SRuchika Gupta
272bf83490eSVictoria Milhoan /* Check for an instantiated RNG before registration */
273d239b10dSHoria Geantă if (priv->era < 10)
274ae1dd17dSHoria GeantA rng_inst = (rd_reg32(&priv->jr[0]->perfmon.cha_num_ls) &
275d239b10dSHoria Geantă CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
276d239b10dSHoria Geantă else
277ae1dd17dSHoria GeantA rng_inst = rd_reg32(&priv->jr[0]->vreg.rng) & CHA_VER_NUM_MASK;
278d239b10dSHoria Geantă
2791b46c90cSHoria Geantă if (!rng_inst)
2801b46c90cSHoria Geantă return 0;
281bf83490eSVictoria Milhoan
2821517f63cSAndrey Smirnov if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
2838483c831SAndrey Smirnov return -ENOMEM;
284e24f7c9eSYuan Kang
2852c5e88dcSAndrey Smirnov ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
2861517f63cSAndrey Smirnov if (!ctx)
2871517f63cSAndrey Smirnov return -ENOMEM;
2881517f63cSAndrey Smirnov
2892c5e88dcSAndrey Smirnov ctx->ctrldev = ctrldev;
2902c5e88dcSAndrey Smirnov
2911517f63cSAndrey Smirnov ctx->rng.name = "rng-caam";
2921517f63cSAndrey Smirnov ctx->rng.init = caam_init;
2931517f63cSAndrey Smirnov ctx->rng.cleanup = caam_cleanup;
2941517f63cSAndrey Smirnov ctx->rng.read = caam_read;
2951517f63cSAndrey Smirnov ctx->rng.priv = (unsigned long)ctx;
2961517f63cSAndrey Smirnov
2978483c831SAndrey Smirnov dev_info(ctrldev, "registering rng-caam\n");
298c59a1d41SIuliana Prodan
2991517f63cSAndrey Smirnov ret = devm_hwrng_register(ctrldev, &ctx->rng);
3001517f63cSAndrey Smirnov if (ret) {
3011517f63cSAndrey Smirnov caam_rng_exit(ctrldev);
3021517f63cSAndrey Smirnov return ret;
3034e3a61c5SIuliana Prodan }
304ac8ad307SFabio Estevam
305*2be0d806SVictoria Milhoan (b42089) #ifdef CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_TEST
306*2be0d806SVictoria Milhoan (b42089) self_test(&ctx->rng);
307*2be0d806SVictoria Milhoan (b42089) #endif
308*2be0d806SVictoria Milhoan (b42089)
3091517f63cSAndrey Smirnov devres_close_group(ctrldev, caam_rng_init);
3101517f63cSAndrey Smirnov return 0;
311e24f7c9eSYuan Kang }
312