1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2019 HiSilicon Limited. */
4 #include <linux/acpi.h>
5 #include <linux/crypto.h>
7 #include <linux/hw_random.h>
9 #include <linux/iopoll.h>
10 #include <linux/kernel.h>
11 #include <linux/list.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/platform_device.h>
15 #include <linux/random.h>
16 #include <crypto/internal/rng.h>
18 #define HISI_TRNG_REG 0x00F0
19 #define HISI_TRNG_BYTES 4
20 #define HISI_TRNG_QUALITY 512
22 #define TIMEOUT_US 10000
23 #define SW_DRBG_NUM_SHIFT 2
24 #define SW_DRBG_KEY_BASE 0x082C
25 #define SW_DRBG_SEED(n) (SW_DRBG_KEY_BASE - ((n) << SW_DRBG_NUM_SHIFT))
26 #define SW_DRBG_SEED_REGS_NUM 12
27 #define SW_DRBG_SEED_SIZE 48
28 #define SW_DRBG_BLOCKS 0x0830
29 #define SW_DRBG_INIT 0x0834
30 #define SW_DRBG_GEN 0x083c
31 #define SW_DRBG_STATUS 0x0840
32 #define SW_DRBG_BLOCKS_NUM 4095
33 #define SW_DRBG_DATA_BASE 0x0850
34 #define SW_DRBG_DATA_NUM 4
35 #define SW_DRBG_DATA(n) (SW_DRBG_DATA_BASE - ((n) << SW_DRBG_NUM_SHIFT))
36 #define SW_DRBG_BYTES 16
37 #define SW_DRBG_ENABLE_SHIFT 12
38 #define SEED_SHIFT_24 24
39 #define SEED_SHIFT_16 16
40 #define SEED_SHIFT_8 8
42 struct hisi_trng_list
{
44 struct list_head list
;
50 struct hisi_trng_list
*trng_list
;
51 struct list_head list
;
57 struct hisi_trng_ctx
{
58 struct hisi_trng
*trng
;
61 static atomic_t trng_active_devs
;
62 static struct hisi_trng_list trng_devices
;
64 static void hisi_trng_set_seed(struct hisi_trng
*trng
, const u8
*seed
)
68 for (i
= 0; i
< SW_DRBG_SEED_SIZE
;
69 i
+= SW_DRBG_SEED_SIZE
/ SW_DRBG_SEED_REGS_NUM
) {
70 val
= seed
[i
] << SEED_SHIFT_24
;
71 val
|= seed
[i
+ 1UL] << SEED_SHIFT_16
;
72 val
|= seed
[i
+ 2UL] << SEED_SHIFT_8
;
75 seed_reg
= (i
>> SW_DRBG_NUM_SHIFT
) % SW_DRBG_SEED_REGS_NUM
;
76 writel(val
, trng
->base
+ SW_DRBG_SEED(seed_reg
));
80 static int hisi_trng_seed(struct crypto_rng
*tfm
, const u8
*seed
,
83 struct hisi_trng_ctx
*ctx
= crypto_rng_ctx(tfm
);
84 struct hisi_trng
*trng
= ctx
->trng
;
88 if (slen
< SW_DRBG_SEED_SIZE
) {
89 pr_err("slen(%u) is not matched with trng(%d)\n", slen
,
94 writel(0x0, trng
->base
+ SW_DRBG_BLOCKS
);
95 hisi_trng_set_seed(trng
, seed
);
97 writel(SW_DRBG_BLOCKS_NUM
| (0x1 << SW_DRBG_ENABLE_SHIFT
),
98 trng
->base
+ SW_DRBG_BLOCKS
);
99 writel(0x1, trng
->base
+ SW_DRBG_INIT
);
101 ret
= readl_relaxed_poll_timeout(trng
->base
+ SW_DRBG_STATUS
,
102 val
, val
& BIT(0), SLEEP_US
, TIMEOUT_US
);
104 pr_err("fail to init trng(%d)\n", ret
);
109 static int hisi_trng_generate(struct crypto_rng
*tfm
, const u8
*src
,
110 unsigned int slen
, u8
*dstn
, unsigned int dlen
)
112 struct hisi_trng_ctx
*ctx
= crypto_rng_ctx(tfm
);
113 struct hisi_trng
*trng
= ctx
->trng
;
114 u32 data
[SW_DRBG_DATA_NUM
];
120 if (dlen
> SW_DRBG_BLOCKS_NUM
* SW_DRBG_BYTES
|| dlen
== 0) {
121 pr_err("dlen(%d) exceeds limit(%d)!\n", dlen
,
122 SW_DRBG_BLOCKS_NUM
* SW_DRBG_BYTES
);
127 ret
= readl_relaxed_poll_timeout(trng
->base
+ SW_DRBG_STATUS
,
128 val
, val
& BIT(1), SLEEP_US
, TIMEOUT_US
);
130 pr_err("fail to generate random number(%d)!\n", ret
);
134 for (i
= 0; i
< SW_DRBG_DATA_NUM
; i
++)
135 data
[i
] = readl(trng
->base
+ SW_DRBG_DATA(i
));
137 if (dlen
- currsize
>= SW_DRBG_BYTES
) {
138 memcpy(dstn
+ currsize
, data
, SW_DRBG_BYTES
);
139 currsize
+= SW_DRBG_BYTES
;
141 memcpy(dstn
+ currsize
, data
, dlen
- currsize
);
145 writel(0x1, trng
->base
+ SW_DRBG_GEN
);
146 } while (currsize
< dlen
);
151 static int hisi_trng_init(struct crypto_tfm
*tfm
)
153 struct hisi_trng_ctx
*ctx
= crypto_tfm_ctx(tfm
);
154 struct hisi_trng
*trng
;
157 mutex_lock(&trng_devices
.lock
);
158 list_for_each_entry(trng
, &trng_devices
.list
, list
) {
159 if (!trng
->is_used
) {
160 trng
->is_used
= true;
166 mutex_unlock(&trng_devices
.lock
);
171 static void hisi_trng_exit(struct crypto_tfm
*tfm
)
173 struct hisi_trng_ctx
*ctx
= crypto_tfm_ctx(tfm
);
175 mutex_lock(&trng_devices
.lock
);
176 ctx
->trng
->is_used
= false;
177 mutex_unlock(&trng_devices
.lock
);
180 static int hisi_trng_read(struct hwrng
*rng
, void *buf
, size_t max
, bool wait
)
182 struct hisi_trng
*trng
;
187 trng
= container_of(rng
, struct hisi_trng
, rng
);
190 ret
= readl_poll_timeout(trng
->base
+ HISI_TRNG_REG
, val
,
191 val
, SLEEP_US
, TIMEOUT_US
);
195 if (max
- currsize
>= HISI_TRNG_BYTES
) {
196 memcpy(buf
+ currsize
, &val
, HISI_TRNG_BYTES
);
197 currsize
+= HISI_TRNG_BYTES
;
203 /* copy remaining bytes */
204 memcpy(buf
+ currsize
, &val
, max
- currsize
);
206 } while (currsize
< max
);
211 static struct rng_alg hisi_trng_alg
= {
212 .generate
= hisi_trng_generate
,
213 .seed
= hisi_trng_seed
,
214 .seedsize
= SW_DRBG_SEED_SIZE
,
216 .cra_name
= "stdrng",
217 .cra_driver_name
= "hisi_stdrng",
219 .cra_ctxsize
= sizeof(struct hisi_trng_ctx
),
220 .cra_module
= THIS_MODULE
,
221 .cra_init
= hisi_trng_init
,
222 .cra_exit
= hisi_trng_exit
,
226 static void hisi_trng_add_to_list(struct hisi_trng
*trng
)
228 mutex_lock(&trng_devices
.lock
);
229 list_add_tail(&trng
->list
, &trng_devices
.list
);
230 mutex_unlock(&trng_devices
.lock
);
233 static int hisi_trng_del_from_list(struct hisi_trng
*trng
)
237 mutex_lock(&trng_devices
.lock
);
238 if (!trng
->is_used
) {
239 list_del(&trng
->list
);
242 mutex_unlock(&trng_devices
.lock
);
247 static int hisi_trng_probe(struct platform_device
*pdev
)
249 struct hisi_trng
*trng
;
252 trng
= devm_kzalloc(&pdev
->dev
, sizeof(*trng
), GFP_KERNEL
);
256 platform_set_drvdata(pdev
, trng
);
258 trng
->base
= devm_platform_ioremap_resource(pdev
, 0);
259 if (IS_ERR(trng
->base
))
260 return PTR_ERR(trng
->base
);
262 trng
->is_used
= false;
263 if (!trng_devices
.is_init
) {
264 INIT_LIST_HEAD(&trng_devices
.list
);
265 mutex_init(&trng_devices
.lock
);
266 trng_devices
.is_init
= true;
269 hisi_trng_add_to_list(trng
);
270 if (atomic_inc_return(&trng_active_devs
) == 1) {
271 ret
= crypto_register_rng(&hisi_trng_alg
);
274 "failed to register crypto(%d)\n", ret
);
275 atomic_dec_return(&trng_active_devs
);
276 goto err_remove_from_list
;
280 trng
->rng
.name
= pdev
->name
;
281 trng
->rng
.read
= hisi_trng_read
;
282 trng
->rng
.quality
= HISI_TRNG_QUALITY
;
283 ret
= devm_hwrng_register(&pdev
->dev
, &trng
->rng
);
285 dev_err(&pdev
->dev
, "failed to register hwrng: %d!\n", ret
);
286 goto err_crypto_unregister
;
291 err_crypto_unregister
:
292 if (atomic_dec_return(&trng_active_devs
) == 0)
293 crypto_unregister_rng(&hisi_trng_alg
);
295 err_remove_from_list
:
296 hisi_trng_del_from_list(trng
);
300 static int hisi_trng_remove(struct platform_device
*pdev
)
302 struct hisi_trng
*trng
= platform_get_drvdata(pdev
);
304 /* Wait until the task is finished */
305 while (hisi_trng_del_from_list(trng
))
308 if (atomic_dec_return(&trng_active_devs
) == 0)
309 crypto_unregister_rng(&hisi_trng_alg
);
314 static const struct acpi_device_id hisi_trng_acpi_match
[] = {
318 MODULE_DEVICE_TABLE(acpi
, hisi_trng_acpi_match
);
320 static struct platform_driver hisi_trng_driver
= {
321 .probe
= hisi_trng_probe
,
322 .remove
= hisi_trng_remove
,
324 .name
= "hisi-trng-v2",
325 .acpi_match_table
= ACPI_PTR(hisi_trng_acpi_match
),
329 module_platform_driver(hisi_trng_driver
);
331 MODULE_LICENSE("GPL v2");
332 MODULE_AUTHOR("Weili Qian <qianweili@huawei.com>");
333 MODULE_AUTHOR("Zaibo Xu <xuzaibo@huawei.com>");
334 MODULE_DESCRIPTION("HiSilicon true random number generator V2 driver");