xref: /linux/drivers/crypto/loongson/loongson-rng.c (revision 766b2d724c8df071031412eea902b566a0049c31)
1*766b2d72SQunqin Zhao // SPDX-License-Identifier: GPL-2.0
2*766b2d72SQunqin Zhao /* Copyright (c) 2019 HiSilicon Limited. */
3*766b2d72SQunqin Zhao /* Copyright (c) 2025 Loongson Technology Corporation Limited. */
4*766b2d72SQunqin Zhao 
5*766b2d72SQunqin Zhao #include <linux/crypto.h>
6*766b2d72SQunqin Zhao #include <linux/err.h>
7*766b2d72SQunqin Zhao #include <linux/hw_random.h>
8*766b2d72SQunqin Zhao #include <linux/io.h>
9*766b2d72SQunqin Zhao #include <linux/iopoll.h>
10*766b2d72SQunqin Zhao #include <linux/kernel.h>
11*766b2d72SQunqin Zhao #include <linux/list.h>
12*766b2d72SQunqin Zhao #include <linux/mfd/loongson-se.h>
13*766b2d72SQunqin Zhao #include <linux/module.h>
14*766b2d72SQunqin Zhao #include <linux/mutex.h>
15*766b2d72SQunqin Zhao #include <linux/platform_device.h>
16*766b2d72SQunqin Zhao #include <linux/random.h>
17*766b2d72SQunqin Zhao #include <crypto/internal/rng.h>
18*766b2d72SQunqin Zhao 
19*766b2d72SQunqin Zhao #define SE_SEED_SIZE 32
20*766b2d72SQunqin Zhao 
21*766b2d72SQunqin Zhao struct loongson_rng_list {
22*766b2d72SQunqin Zhao 	struct mutex lock;
23*766b2d72SQunqin Zhao 	struct list_head list;
24*766b2d72SQunqin Zhao 	int registered;
25*766b2d72SQunqin Zhao };
26*766b2d72SQunqin Zhao 
27*766b2d72SQunqin Zhao struct loongson_rng {
28*766b2d72SQunqin Zhao 	u32 used;
29*766b2d72SQunqin Zhao 	struct loongson_se_engine *engine;
30*766b2d72SQunqin Zhao 	struct list_head list;
31*766b2d72SQunqin Zhao 	struct mutex lock;
32*766b2d72SQunqin Zhao };
33*766b2d72SQunqin Zhao 
34*766b2d72SQunqin Zhao struct loongson_rng_ctx {
35*766b2d72SQunqin Zhao 	struct loongson_rng *rng;
36*766b2d72SQunqin Zhao };
37*766b2d72SQunqin Zhao 
38*766b2d72SQunqin Zhao struct loongson_rng_cmd {
39*766b2d72SQunqin Zhao 	u32 cmd_id;
40*766b2d72SQunqin Zhao 	union {
41*766b2d72SQunqin Zhao 		u32 len;
42*766b2d72SQunqin Zhao 		u32 ret;
43*766b2d72SQunqin Zhao 	} u;
44*766b2d72SQunqin Zhao 	u32 seed_off;
45*766b2d72SQunqin Zhao 	u32 out_off;
46*766b2d72SQunqin Zhao 	u32 pad[4];
47*766b2d72SQunqin Zhao };
48*766b2d72SQunqin Zhao 
49*766b2d72SQunqin Zhao static struct loongson_rng_list rng_devices = {
50*766b2d72SQunqin Zhao 	.lock = __MUTEX_INITIALIZER(rng_devices.lock),
51*766b2d72SQunqin Zhao 	.list = LIST_HEAD_INIT(rng_devices.list),
52*766b2d72SQunqin Zhao };
53*766b2d72SQunqin Zhao 
54*766b2d72SQunqin Zhao static int loongson_rng_generate(struct crypto_rng *tfm, const u8 *src,
55*766b2d72SQunqin Zhao 			  unsigned int slen, u8 *dstn, unsigned int dlen)
56*766b2d72SQunqin Zhao {
57*766b2d72SQunqin Zhao 	struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);
58*766b2d72SQunqin Zhao 	struct loongson_rng *rng = ctx->rng;
59*766b2d72SQunqin Zhao 	struct loongson_rng_cmd *cmd = rng->engine->command;
60*766b2d72SQunqin Zhao 	int err, len;
61*766b2d72SQunqin Zhao 
62*766b2d72SQunqin Zhao 	mutex_lock(&rng->lock);
63*766b2d72SQunqin Zhao 	cmd->seed_off = 0;
64*766b2d72SQunqin Zhao 	do {
65*766b2d72SQunqin Zhao 		len = min(dlen, rng->engine->buffer_size);
66*766b2d72SQunqin Zhao 		cmd = rng->engine->command;
67*766b2d72SQunqin Zhao 		cmd->u.len = len;
68*766b2d72SQunqin Zhao 		err = loongson_se_send_engine_cmd(rng->engine);
69*766b2d72SQunqin Zhao 		if (err)
70*766b2d72SQunqin Zhao 			break;
71*766b2d72SQunqin Zhao 
72*766b2d72SQunqin Zhao 		cmd = rng->engine->command_ret;
73*766b2d72SQunqin Zhao 		if (cmd->u.ret) {
74*766b2d72SQunqin Zhao 			err = -EIO;
75*766b2d72SQunqin Zhao 			break;
76*766b2d72SQunqin Zhao 		}
77*766b2d72SQunqin Zhao 
78*766b2d72SQunqin Zhao 		memcpy(dstn, rng->engine->data_buffer, len);
79*766b2d72SQunqin Zhao 		dlen -= len;
80*766b2d72SQunqin Zhao 		dstn += len;
81*766b2d72SQunqin Zhao 	} while (dlen > 0);
82*766b2d72SQunqin Zhao 	mutex_unlock(&rng->lock);
83*766b2d72SQunqin Zhao 
84*766b2d72SQunqin Zhao 	return err;
85*766b2d72SQunqin Zhao }
86*766b2d72SQunqin Zhao 
87*766b2d72SQunqin Zhao static int loongson_rng_init(struct crypto_tfm *tfm)
88*766b2d72SQunqin Zhao {
89*766b2d72SQunqin Zhao 	struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);
90*766b2d72SQunqin Zhao 	struct loongson_rng *rng;
91*766b2d72SQunqin Zhao 	u32 min_used = U32_MAX;
92*766b2d72SQunqin Zhao 
93*766b2d72SQunqin Zhao 	mutex_lock(&rng_devices.lock);
94*766b2d72SQunqin Zhao 	list_for_each_entry(rng, &rng_devices.list, list) {
95*766b2d72SQunqin Zhao 		if (rng->used < min_used) {
96*766b2d72SQunqin Zhao 			ctx->rng = rng;
97*766b2d72SQunqin Zhao 			min_used = rng->used;
98*766b2d72SQunqin Zhao 		}
99*766b2d72SQunqin Zhao 	}
100*766b2d72SQunqin Zhao 	ctx->rng->used++;
101*766b2d72SQunqin Zhao 	mutex_unlock(&rng_devices.lock);
102*766b2d72SQunqin Zhao 
103*766b2d72SQunqin Zhao 	return 0;
104*766b2d72SQunqin Zhao }
105*766b2d72SQunqin Zhao 
106*766b2d72SQunqin Zhao static void loongson_rng_exit(struct crypto_tfm *tfm)
107*766b2d72SQunqin Zhao {
108*766b2d72SQunqin Zhao 	struct loongson_rng_ctx *ctx = crypto_tfm_ctx(tfm);
109*766b2d72SQunqin Zhao 
110*766b2d72SQunqin Zhao 	mutex_lock(&rng_devices.lock);
111*766b2d72SQunqin Zhao 	ctx->rng->used--;
112*766b2d72SQunqin Zhao 	mutex_unlock(&rng_devices.lock);
113*766b2d72SQunqin Zhao }
114*766b2d72SQunqin Zhao 
115*766b2d72SQunqin Zhao static int loongson_rng_seed(struct crypto_rng *tfm, const u8 *seed,
116*766b2d72SQunqin Zhao 			     unsigned int slen)
117*766b2d72SQunqin Zhao {
118*766b2d72SQunqin Zhao 	struct loongson_rng_ctx *ctx = crypto_rng_ctx(tfm);
119*766b2d72SQunqin Zhao 	struct loongson_rng *rng = ctx->rng;
120*766b2d72SQunqin Zhao 	struct loongson_rng_cmd *cmd;
121*766b2d72SQunqin Zhao 	int err;
122*766b2d72SQunqin Zhao 
123*766b2d72SQunqin Zhao 	if (slen < SE_SEED_SIZE)
124*766b2d72SQunqin Zhao 		return -EINVAL;
125*766b2d72SQunqin Zhao 
126*766b2d72SQunqin Zhao 	slen = min(slen, rng->engine->buffer_size);
127*766b2d72SQunqin Zhao 
128*766b2d72SQunqin Zhao 	mutex_lock(&rng->lock);
129*766b2d72SQunqin Zhao 	cmd = rng->engine->command;
130*766b2d72SQunqin Zhao 	cmd->u.len = slen;
131*766b2d72SQunqin Zhao 	cmd->seed_off = rng->engine->buffer_off;
132*766b2d72SQunqin Zhao 	memcpy(rng->engine->data_buffer, seed, slen);
133*766b2d72SQunqin Zhao 	err = loongson_se_send_engine_cmd(rng->engine);
134*766b2d72SQunqin Zhao 	if (err)
135*766b2d72SQunqin Zhao 		goto out;
136*766b2d72SQunqin Zhao 
137*766b2d72SQunqin Zhao 	cmd = rng->engine->command_ret;
138*766b2d72SQunqin Zhao 	if (cmd->u.ret)
139*766b2d72SQunqin Zhao 		err = -EIO;
140*766b2d72SQunqin Zhao out:
141*766b2d72SQunqin Zhao 	mutex_unlock(&rng->lock);
142*766b2d72SQunqin Zhao 
143*766b2d72SQunqin Zhao 	return err;
144*766b2d72SQunqin Zhao }
145*766b2d72SQunqin Zhao 
146*766b2d72SQunqin Zhao static struct rng_alg loongson_rng_alg = {
147*766b2d72SQunqin Zhao 	.generate = loongson_rng_generate,
148*766b2d72SQunqin Zhao 	.seed =	loongson_rng_seed,
149*766b2d72SQunqin Zhao 	.seedsize = SE_SEED_SIZE,
150*766b2d72SQunqin Zhao 	.base = {
151*766b2d72SQunqin Zhao 		.cra_name = "stdrng",
152*766b2d72SQunqin Zhao 		.cra_driver_name = "loongson_stdrng",
153*766b2d72SQunqin Zhao 		.cra_priority = 300,
154*766b2d72SQunqin Zhao 		.cra_ctxsize = sizeof(struct loongson_rng_ctx),
155*766b2d72SQunqin Zhao 		.cra_module = THIS_MODULE,
156*766b2d72SQunqin Zhao 		.cra_init = loongson_rng_init,
157*766b2d72SQunqin Zhao 		.cra_exit = loongson_rng_exit,
158*766b2d72SQunqin Zhao 	},
159*766b2d72SQunqin Zhao };
160*766b2d72SQunqin Zhao 
161*766b2d72SQunqin Zhao static int loongson_rng_probe(struct platform_device *pdev)
162*766b2d72SQunqin Zhao {
163*766b2d72SQunqin Zhao 	struct loongson_rng_cmd *cmd;
164*766b2d72SQunqin Zhao 	struct loongson_rng *rng;
165*766b2d72SQunqin Zhao 	int ret = 0;
166*766b2d72SQunqin Zhao 
167*766b2d72SQunqin Zhao 	rng = devm_kzalloc(&pdev->dev, sizeof(*rng), GFP_KERNEL);
168*766b2d72SQunqin Zhao 	if (!rng)
169*766b2d72SQunqin Zhao 		return -ENOMEM;
170*766b2d72SQunqin Zhao 
171*766b2d72SQunqin Zhao 	rng->engine = loongson_se_init_engine(pdev->dev.parent, SE_ENGINE_RNG);
172*766b2d72SQunqin Zhao 	if (!rng->engine)
173*766b2d72SQunqin Zhao 		return -ENODEV;
174*766b2d72SQunqin Zhao 	cmd = rng->engine->command;
175*766b2d72SQunqin Zhao 	cmd->cmd_id = SE_CMD_RNG;
176*766b2d72SQunqin Zhao 	cmd->out_off = rng->engine->buffer_off;
177*766b2d72SQunqin Zhao 	mutex_init(&rng->lock);
178*766b2d72SQunqin Zhao 
179*766b2d72SQunqin Zhao 	mutex_lock(&rng_devices.lock);
180*766b2d72SQunqin Zhao 
181*766b2d72SQunqin Zhao 	if (!rng_devices.registered) {
182*766b2d72SQunqin Zhao 		ret = crypto_register_rng(&loongson_rng_alg);
183*766b2d72SQunqin Zhao 		if (ret) {
184*766b2d72SQunqin Zhao 			dev_err(&pdev->dev, "failed to register crypto(%d)\n", ret);
185*766b2d72SQunqin Zhao 			goto out;
186*766b2d72SQunqin Zhao 		}
187*766b2d72SQunqin Zhao 		rng_devices.registered = 1;
188*766b2d72SQunqin Zhao 	}
189*766b2d72SQunqin Zhao 
190*766b2d72SQunqin Zhao 	list_add_tail(&rng->list, &rng_devices.list);
191*766b2d72SQunqin Zhao out:
192*766b2d72SQunqin Zhao 	mutex_unlock(&rng_devices.lock);
193*766b2d72SQunqin Zhao 
194*766b2d72SQunqin Zhao 	return ret;
195*766b2d72SQunqin Zhao }
196*766b2d72SQunqin Zhao 
197*766b2d72SQunqin Zhao static struct platform_driver loongson_rng_driver = {
198*766b2d72SQunqin Zhao 	.probe		= loongson_rng_probe,
199*766b2d72SQunqin Zhao 	.driver		= {
200*766b2d72SQunqin Zhao 		.name	= "loongson-rng",
201*766b2d72SQunqin Zhao 	},
202*766b2d72SQunqin Zhao };
203*766b2d72SQunqin Zhao module_platform_driver(loongson_rng_driver);
204*766b2d72SQunqin Zhao 
205*766b2d72SQunqin Zhao MODULE_ALIAS("platform:loongson-rng");
206*766b2d72SQunqin Zhao MODULE_LICENSE("GPL");
207*766b2d72SQunqin Zhao MODULE_AUTHOR("Yinggang Gu <guyinggang@loongson.cn>");
208*766b2d72SQunqin Zhao MODULE_AUTHOR("Qunqin Zhao <zhaoqunqin@loongson.cn>");
209*766b2d72SQunqin Zhao MODULE_DESCRIPTION("Loongson Random Number Generator driver");
210