141bb142aSMarek Behún // SPDX-License-Identifier: GPL-2.0
241bb142aSMarek Behún /*
341bb142aSMarek Behún * CZ.NIC's Turris Omnia MCU TRNG driver
441bb142aSMarek Behún *
541bb142aSMarek Behún * 2024 by Marek Behún <kabel@kernel.org>
641bb142aSMarek Behún */
741bb142aSMarek Behún
841bb142aSMarek Behún #include <linux/bitfield.h>
941bb142aSMarek Behún #include <linux/completion.h>
1041bb142aSMarek Behún #include <linux/container_of.h>
1141bb142aSMarek Behún #include <linux/errno.h>
1241bb142aSMarek Behún #include <linux/gpio/consumer.h>
1341bb142aSMarek Behún #include <linux/gpio/driver.h>
1441bb142aSMarek Behún #include <linux/hw_random.h>
1541bb142aSMarek Behún #include <linux/i2c.h>
1641bb142aSMarek Behún #include <linux/interrupt.h>
1741bb142aSMarek Behún #include <linux/minmax.h>
1841bb142aSMarek Behún #include <linux/string.h>
1941bb142aSMarek Behún #include <linux/types.h>
2041bb142aSMarek Behún
2141bb142aSMarek Behún #include <linux/turris-omnia-mcu-interface.h>
2241bb142aSMarek Behún #include "turris-omnia-mcu.h"
2341bb142aSMarek Behún
2441bb142aSMarek Behún #define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64
2541bb142aSMarek Behún
omnia_trng_irq_handler(int irq,void * dev_id)2641bb142aSMarek Behún static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id)
2741bb142aSMarek Behún {
2841bb142aSMarek Behún struct omnia_mcu *mcu = dev_id;
2941bb142aSMarek Behún
3041bb142aSMarek Behún complete(&mcu->trng_entropy_ready);
3141bb142aSMarek Behún
3241bb142aSMarek Behún return IRQ_HANDLED;
3341bb142aSMarek Behún }
3441bb142aSMarek Behún
omnia_trng_read(struct hwrng * rng,void * data,size_t max,bool wait)3541bb142aSMarek Behún static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait)
3641bb142aSMarek Behún {
3741bb142aSMarek Behún struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng);
3841bb142aSMarek Behún u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN];
3941bb142aSMarek Behún int err, bytes;
4041bb142aSMarek Behún
4141bb142aSMarek Behún if (!wait && !completion_done(&mcu->trng_entropy_ready))
4241bb142aSMarek Behún return 0;
4341bb142aSMarek Behún
4441bb142aSMarek Behún do {
4541bb142aSMarek Behún if (wait_for_completion_interruptible(&mcu->trng_entropy_ready))
4641bb142aSMarek Behún return -ERESTARTSYS;
4741bb142aSMarek Behún
4841bb142aSMarek Behún err = omnia_cmd_read(mcu->client,
4941bb142aSMarek Behún OMNIA_CMD_TRNG_COLLECT_ENTROPY,
5041bb142aSMarek Behún reply, sizeof(reply));
5141bb142aSMarek Behún if (err)
5241bb142aSMarek Behún return err;
5341bb142aSMarek Behún
5441bb142aSMarek Behún bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN);
5541bb142aSMarek Behún } while (wait && !bytes);
5641bb142aSMarek Behún
5741bb142aSMarek Behún memcpy(data, &reply[1], bytes);
5841bb142aSMarek Behún
5941bb142aSMarek Behún return bytes;
6041bb142aSMarek Behún }
6141bb142aSMarek Behún
omnia_mcu_register_trng(struct omnia_mcu * mcu)6241bb142aSMarek Behún int omnia_mcu_register_trng(struct omnia_mcu *mcu)
6341bb142aSMarek Behún {
6441bb142aSMarek Behún struct device *dev = &mcu->client->dev;
6541bb142aSMarek Behún u8 irq_idx, dummy;
6641bb142aSMarek Behún int irq, err;
6741bb142aSMarek Behún
6841bb142aSMarek Behún if (!(mcu->features & OMNIA_FEAT_TRNG))
6941bb142aSMarek Behún return 0;
7041bb142aSMarek Behún
7141bb142aSMarek Behún irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)];
7241bb142aSMarek Behún irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx));
73*2d516b8fSDan Carpenter if (irq < 0)
74*2d516b8fSDan Carpenter return dev_err_probe(dev, irq, "Cannot get TRNG IRQ\n");
7541bb142aSMarek Behún
7641bb142aSMarek Behún /*
7741bb142aSMarek Behún * If someone else cleared the TRNG interrupt but did not read the
7841bb142aSMarek Behún * entropy, a new interrupt won't be generated, and entropy collection
7941bb142aSMarek Behún * will be stuck. Ensure an interrupt will be generated by executing
8041bb142aSMarek Behún * the collect entropy command (and discarding the result).
8141bb142aSMarek Behún */
8241bb142aSMarek Behún err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY,
8341bb142aSMarek Behún &dummy, 1);
8441bb142aSMarek Behún if (err)
8541bb142aSMarek Behún return err;
8641bb142aSMarek Behún
8741bb142aSMarek Behún init_completion(&mcu->trng_entropy_ready);
8841bb142aSMarek Behún
8941bb142aSMarek Behún err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler,
9041bb142aSMarek Behún IRQF_ONESHOT, "turris-omnia-mcu-trng",
9141bb142aSMarek Behún mcu);
9241bb142aSMarek Behún if (err)
9341bb142aSMarek Behún return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n");
9441bb142aSMarek Behún
9541bb142aSMarek Behún mcu->trng.name = "turris-omnia-mcu-trng";
9641bb142aSMarek Behún mcu->trng.read = omnia_trng_read;
9741bb142aSMarek Behún
9841bb142aSMarek Behún err = devm_hwrng_register(dev, &mcu->trng);
9941bb142aSMarek Behún if (err)
10041bb142aSMarek Behún return dev_err_probe(dev, err, "Cannot register TRNG\n");
10141bb142aSMarek Behún
10241bb142aSMarek Behún return 0;
10341bb142aSMarek Behún }
104