1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * caam - Freescale FSL CAAM support for hw_random
4 *
5 * Copyright 2011 Freescale Semiconductor, Inc.
6 * Copyright 2018-2019 NXP
7 *
8 * Based on caamalg.c crypto API driver.
9 *
10 */
11
12#include <linux/hw_random.h>
13#include <linux/completion.h>
14#include <linux/atomic.h>
15#include <linux/kfifo.h>
16
17#include "compat.h"
18
19#include "regs.h"
20#include "intern.h"
21#include "desc_constr.h"
22#include "jr.h"
23#include "error.h"
24
25#define CAAM_RNG_MAX_FIFO_STORE_SIZE	16
26
27/*
28 * Length of used descriptors, see caam_init_desc()
29 */
30#define CAAM_RNG_DESC_LEN (CAAM_CMD_SZ +				\
31			   CAAM_CMD_SZ +				\
32			   CAAM_CMD_SZ + CAAM_PTR_SZ_MAX)
33
34/* rng per-device context */
35struct caam_rng_ctx {
36	struct hwrng rng;
37	struct device *jrdev;
38	struct device *ctrldev;
39	void *desc_async;
40	void *desc_sync;
41	struct work_struct worker;
42	struct kfifo fifo;
43};
44
45struct caam_rng_job_ctx {
46	struct completion *done;
47	int *err;
48};
49
50static struct caam_rng_ctx *to_caam_rng_ctx(struct hwrng *r)
51{
52	return (struct caam_rng_ctx *)r->priv;
53}
54
55static void caam_rng_done(struct device *jrdev, u32 *desc, u32 err,
56			  void *context)
57{
58	struct caam_rng_job_ctx *jctx = context;
59
60	if (err)
61		*jctx->err = caam_jr_strstatus(jrdev, err);
62
63	complete(jctx->done);
64}
65
66static u32 *caam_init_desc(u32 *desc, dma_addr_t dst_dma)
67{
68	init_job_desc(desc, 0);	/* + 1 cmd_sz */
69	/* Generate random bytes: + 1 cmd_sz */
70	append_operation(desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG |
71			 OP_ALG_PR_ON);
72	/* Store bytes: + 1 cmd_sz + caam_ptr_sz  */
73	append_fifo_store(desc, dst_dma,
74			  CAAM_RNG_MAX_FIFO_STORE_SIZE, FIFOST_TYPE_RNGSTORE);
75
76	print_hex_dump_debug("rng job desc@: ", DUMP_PREFIX_ADDRESS,
77			     16, 4, desc, desc_bytes(desc), 1);
78
79	return desc;
80}
81
82static int caam_rng_read_one(struct device *jrdev,
83			     void *dst, int len,
84			     void *desc,
85			     struct completion *done)
86{
87	dma_addr_t dst_dma;
88	int err, ret = 0;
89	struct caam_rng_job_ctx jctx = {
90		.done = done,
91		.err  = &ret,
92	};
93
94	len = CAAM_RNG_MAX_FIFO_STORE_SIZE;
95
96	dst_dma = dma_map_single(jrdev, dst, len, DMA_FROM_DEVICE);
97	if (dma_mapping_error(jrdev, dst_dma)) {
98		dev_err(jrdev, "unable to map destination memory\n");
99		return -ENOMEM;
100	}
101
102	init_completion(done);
103	err = caam_jr_enqueue(jrdev,
104			      caam_init_desc(desc, dst_dma),
105			      caam_rng_done, &jctx);
106	if (err == -EINPROGRESS) {
107		wait_for_completion(done);
108		err = 0;
109	}
110
111	dma_unmap_single(jrdev, dst_dma, len, DMA_FROM_DEVICE);
112
113	return err ?: (ret ?: len);
114}
115
116static void caam_rng_fill_async(struct caam_rng_ctx *ctx)
117{
118	struct scatterlist sg[1];
119	struct completion done;
120	int len, nents;
121
122	sg_init_table(sg, ARRAY_SIZE(sg));
123	nents = kfifo_dma_in_prepare(&ctx->fifo, sg, ARRAY_SIZE(sg),
124				     CAAM_RNG_MAX_FIFO_STORE_SIZE);
125	if (!nents)
126		return;
127
128	len = caam_rng_read_one(ctx->jrdev, sg_virt(&sg[0]),
129				sg[0].length,
130				ctx->desc_async,
131				&done);
132	if (len < 0)
133		return;
134
135	kfifo_dma_in_finish(&ctx->fifo, len);
136}
137
138static void caam_rng_worker(struct work_struct *work)
139{
140	struct caam_rng_ctx *ctx = container_of(work, struct caam_rng_ctx,
141						worker);
142	caam_rng_fill_async(ctx);
143}
144
145static int caam_read(struct hwrng *rng, void *dst, size_t max, bool wait)
146{
147	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
148	int out;
149
150	if (wait) {
151		struct completion done;
152
153		return caam_rng_read_one(ctx->jrdev, dst, max,
154					 ctx->desc_sync, &done);
155	}
156
157	out = kfifo_out(&ctx->fifo, dst, max);
158	if (kfifo_is_empty(&ctx->fifo))
159		schedule_work(&ctx->worker);
160
161	return out;
162}
163
164static void caam_cleanup(struct hwrng *rng)
165{
166	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
167
168	flush_work(&ctx->worker);
169	caam_jr_free(ctx->jrdev);
170	kfifo_free(&ctx->fifo);
171}
172
173static int caam_init(struct hwrng *rng)
174{
175	struct caam_rng_ctx *ctx = to_caam_rng_ctx(rng);
176	int err;
177
178	ctx->desc_sync = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
179				      GFP_DMA | GFP_KERNEL);
180	if (!ctx->desc_sync)
181		return -ENOMEM;
182
183	ctx->desc_async = devm_kzalloc(ctx->ctrldev, CAAM_RNG_DESC_LEN,
184				       GFP_DMA | GFP_KERNEL);
185	if (!ctx->desc_async)
186		return -ENOMEM;
187
188	if (kfifo_alloc(&ctx->fifo, CAAM_RNG_MAX_FIFO_STORE_SIZE,
189			GFP_DMA | GFP_KERNEL))
190		return -ENOMEM;
191
192	INIT_WORK(&ctx->worker, caam_rng_worker);
193
194	ctx->jrdev = caam_jr_alloc();
195	err = PTR_ERR_OR_ZERO(ctx->jrdev);
196	if (err) {
197		kfifo_free(&ctx->fifo);
198		pr_err("Job Ring Device allocation for transform failed\n");
199		return err;
200	}
201
202	/*
203	 * Fill async buffer to have early randomness data for
204	 * hw_random
205	 */
206	caam_rng_fill_async(ctx);
207
208	return 0;
209}
210
211int caam_rng_init(struct device *ctrldev);
212
213void caam_rng_exit(struct device *ctrldev)
214{
215	devres_release_group(ctrldev, caam_rng_init);
216}
217
218int caam_rng_init(struct device *ctrldev)
219{
220	struct caam_rng_ctx *ctx;
221	u32 rng_inst;
222	struct caam_drv_private *priv = dev_get_drvdata(ctrldev);
223	int ret;
224
225	/* Check for an instantiated RNG before registration */
226	if (priv->era < 10)
227		rng_inst = (rd_reg32(&priv->ctrl->perfmon.cha_num_ls) &
228			    CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
229	else
230		rng_inst = rd_reg32(&priv->ctrl->vreg.rng) & CHA_VER_NUM_MASK;
231
232	if (!rng_inst)
233		return 0;
234
235	if (!devres_open_group(ctrldev, caam_rng_init, GFP_KERNEL))
236		return -ENOMEM;
237
238	ctx = devm_kzalloc(ctrldev, sizeof(*ctx), GFP_KERNEL);
239	if (!ctx)
240		return -ENOMEM;
241
242	ctx->ctrldev = ctrldev;
243
244	ctx->rng.name    = "rng-caam";
245	ctx->rng.init    = caam_init;
246	ctx->rng.cleanup = caam_cleanup;
247	ctx->rng.read    = caam_read;
248	ctx->rng.priv    = (unsigned long)ctx;
249	ctx->rng.quality = 1024;
250
251	dev_info(ctrldev, "registering rng-caam\n");
252
253	ret = devm_hwrng_register(ctrldev, &ctx->rng);
254	if (ret) {
255		caam_rng_exit(ctrldev);
256		return ret;
257	}
258
259	devres_close_group(ctrldev, caam_rng_init);
260	return 0;
261}
262