1 // SPDX-License-Identifier: GPL-2.0
3 * CZ.NIC's Turris Omnia MCU TRNG driver
5 * 2024 by Marek BehĂșn <kabel@kernel.org>
8 #include <linux/bitfield.h>
9 #include <linux/completion.h>
10 #include <linux/container_of.h>
11 #include <linux/errno.h>
12 #include <linux/gpio/consumer.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/hw_random.h>
15 #include <linux/i2c.h>
16 #include <linux/interrupt.h>
17 #include <linux/minmax.h>
18 #include <linux/string.h>
19 #include <linux/types.h>
21 #include <linux/turris-omnia-mcu-interface.h>
22 #include "turris-omnia-mcu.h"
24 #define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64
26 static irqreturn_t
omnia_trng_irq_handler(int irq
, void *dev_id
)
28 struct omnia_mcu
*mcu
= dev_id
;
30 complete(&mcu
->trng_entropy_ready
);
35 static int omnia_trng_read(struct hwrng
*rng
, void *data
, size_t max
, bool wait
)
37 struct omnia_mcu
*mcu
= container_of(rng
, struct omnia_mcu
, trng
);
38 u8 reply
[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN
];
41 if (!wait
&& !completion_done(&mcu
->trng_entropy_ready
))
45 if (wait_for_completion_interruptible(&mcu
->trng_entropy_ready
))
48 err
= omnia_cmd_read(mcu
->client
,
49 OMNIA_CMD_TRNG_COLLECT_ENTROPY
,
50 reply
, sizeof(reply
));
54 bytes
= min3(reply
[0], max
, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN
);
55 } while (wait
&& !bytes
);
57 memcpy(data
, &reply
[1], bytes
);
62 int omnia_mcu_register_trng(struct omnia_mcu
*mcu
)
64 struct device
*dev
= &mcu
->client
->dev
;
68 if (!(mcu
->features
& OMNIA_FEAT_TRNG
))
71 irq_idx
= omnia_int_to_gpio_idx
[__bf_shf(OMNIA_INT_TRNG
)];
72 irq
= gpiod_to_irq(gpio_device_get_desc(mcu
->gc
.gpiodev
, irq_idx
));
74 return dev_err_probe(dev
, irq
, "Cannot get TRNG IRQ\n");
77 * If someone else cleared the TRNG interrupt but did not read the
78 * entropy, a new interrupt won't be generated, and entropy collection
79 * will be stuck. Ensure an interrupt will be generated by executing
80 * the collect entropy command (and discarding the result).
82 err
= omnia_cmd_read(mcu
->client
, OMNIA_CMD_TRNG_COLLECT_ENTROPY
,
87 init_completion(&mcu
->trng_entropy_ready
);
89 err
= devm_request_threaded_irq(dev
, irq
, NULL
, omnia_trng_irq_handler
,
90 IRQF_ONESHOT
, "turris-omnia-mcu-trng",
93 return dev_err_probe(dev
, err
, "Cannot request TRNG IRQ\n");
95 mcu
->trng
.name
= "turris-omnia-mcu-trng";
96 mcu
->trng
.read
= omnia_trng_read
;
98 err
= devm_hwrng_register(dev
, &mcu
->trng
);
100 return dev_err_probe(dev
, err
, "Cannot register TRNG\n");