1*41bb142aSMarek Behún // SPDX-License-Identifier: GPL-2.0 2*41bb142aSMarek Behún /* 3*41bb142aSMarek Behún * CZ.NIC's Turris Omnia MCU TRNG driver 4*41bb142aSMarek Behún * 5*41bb142aSMarek Behún * 2024 by Marek Behún <kabel@kernel.org> 6*41bb142aSMarek Behún */ 7*41bb142aSMarek Behún 8*41bb142aSMarek Behún #include <linux/bitfield.h> 9*41bb142aSMarek Behún #include <linux/completion.h> 10*41bb142aSMarek Behún #include <linux/container_of.h> 11*41bb142aSMarek Behún #include <linux/errno.h> 12*41bb142aSMarek Behún #include <linux/gpio/consumer.h> 13*41bb142aSMarek Behún #include <linux/gpio/driver.h> 14*41bb142aSMarek Behún #include <linux/hw_random.h> 15*41bb142aSMarek Behún #include <linux/i2c.h> 16*41bb142aSMarek Behún #include <linux/interrupt.h> 17*41bb142aSMarek Behún #include <linux/minmax.h> 18*41bb142aSMarek Behún #include <linux/string.h> 19*41bb142aSMarek Behún #include <linux/types.h> 20*41bb142aSMarek Behún 21*41bb142aSMarek Behún #include <linux/turris-omnia-mcu-interface.h> 22*41bb142aSMarek Behún #include "turris-omnia-mcu.h" 23*41bb142aSMarek Behún 24*41bb142aSMarek Behún #define OMNIA_CMD_TRNG_MAX_ENTROPY_LEN 64 25*41bb142aSMarek Behún 26*41bb142aSMarek Behún static irqreturn_t omnia_trng_irq_handler(int irq, void *dev_id) 27*41bb142aSMarek Behún { 28*41bb142aSMarek Behún struct omnia_mcu *mcu = dev_id; 29*41bb142aSMarek Behún 30*41bb142aSMarek Behún complete(&mcu->trng_entropy_ready); 31*41bb142aSMarek Behún 32*41bb142aSMarek Behún return IRQ_HANDLED; 33*41bb142aSMarek Behún } 34*41bb142aSMarek Behún 35*41bb142aSMarek Behún static int omnia_trng_read(struct hwrng *rng, void *data, size_t max, bool wait) 36*41bb142aSMarek Behún { 37*41bb142aSMarek Behún struct omnia_mcu *mcu = container_of(rng, struct omnia_mcu, trng); 38*41bb142aSMarek Behún u8 reply[1 + OMNIA_CMD_TRNG_MAX_ENTROPY_LEN]; 39*41bb142aSMarek Behún int err, bytes; 40*41bb142aSMarek Behún 41*41bb142aSMarek Behún if (!wait && !completion_done(&mcu->trng_entropy_ready)) 42*41bb142aSMarek Behún return 0; 43*41bb142aSMarek Behún 44*41bb142aSMarek Behún do { 45*41bb142aSMarek Behún if (wait_for_completion_interruptible(&mcu->trng_entropy_ready)) 46*41bb142aSMarek Behún return -ERESTARTSYS; 47*41bb142aSMarek Behún 48*41bb142aSMarek Behún err = omnia_cmd_read(mcu->client, 49*41bb142aSMarek Behún OMNIA_CMD_TRNG_COLLECT_ENTROPY, 50*41bb142aSMarek Behún reply, sizeof(reply)); 51*41bb142aSMarek Behún if (err) 52*41bb142aSMarek Behún return err; 53*41bb142aSMarek Behún 54*41bb142aSMarek Behún bytes = min3(reply[0], max, OMNIA_CMD_TRNG_MAX_ENTROPY_LEN); 55*41bb142aSMarek Behún } while (wait && !bytes); 56*41bb142aSMarek Behún 57*41bb142aSMarek Behún memcpy(data, &reply[1], bytes); 58*41bb142aSMarek Behún 59*41bb142aSMarek Behún return bytes; 60*41bb142aSMarek Behún } 61*41bb142aSMarek Behún 62*41bb142aSMarek Behún int omnia_mcu_register_trng(struct omnia_mcu *mcu) 63*41bb142aSMarek Behún { 64*41bb142aSMarek Behún struct device *dev = &mcu->client->dev; 65*41bb142aSMarek Behún u8 irq_idx, dummy; 66*41bb142aSMarek Behún int irq, err; 67*41bb142aSMarek Behún 68*41bb142aSMarek Behún if (!(mcu->features & OMNIA_FEAT_TRNG)) 69*41bb142aSMarek Behún return 0; 70*41bb142aSMarek Behún 71*41bb142aSMarek Behún irq_idx = omnia_int_to_gpio_idx[__bf_shf(OMNIA_INT_TRNG)]; 72*41bb142aSMarek Behún irq = gpiod_to_irq(gpio_device_get_desc(mcu->gc.gpiodev, irq_idx)); 73*41bb142aSMarek Behún if (!irq) 74*41bb142aSMarek Behún return dev_err_probe(dev, -ENXIO, "Cannot get TRNG IRQ\n"); 75*41bb142aSMarek Behún 76*41bb142aSMarek Behún /* 77*41bb142aSMarek Behún * If someone else cleared the TRNG interrupt but did not read the 78*41bb142aSMarek Behún * entropy, a new interrupt won't be generated, and entropy collection 79*41bb142aSMarek Behún * will be stuck. Ensure an interrupt will be generated by executing 80*41bb142aSMarek Behún * the collect entropy command (and discarding the result). 81*41bb142aSMarek Behún */ 82*41bb142aSMarek Behún err = omnia_cmd_read(mcu->client, OMNIA_CMD_TRNG_COLLECT_ENTROPY, 83*41bb142aSMarek Behún &dummy, 1); 84*41bb142aSMarek Behún if (err) 85*41bb142aSMarek Behún return err; 86*41bb142aSMarek Behún 87*41bb142aSMarek Behún init_completion(&mcu->trng_entropy_ready); 88*41bb142aSMarek Behún 89*41bb142aSMarek Behún err = devm_request_threaded_irq(dev, irq, NULL, omnia_trng_irq_handler, 90*41bb142aSMarek Behún IRQF_ONESHOT, "turris-omnia-mcu-trng", 91*41bb142aSMarek Behún mcu); 92*41bb142aSMarek Behún if (err) 93*41bb142aSMarek Behún return dev_err_probe(dev, err, "Cannot request TRNG IRQ\n"); 94*41bb142aSMarek Behún 95*41bb142aSMarek Behún mcu->trng.name = "turris-omnia-mcu-trng"; 96*41bb142aSMarek Behún mcu->trng.read = omnia_trng_read; 97*41bb142aSMarek Behún 98*41bb142aSMarek Behún err = devm_hwrng_register(dev, &mcu->trng); 99*41bb142aSMarek Behún if (err) 100*41bb142aSMarek Behún return dev_err_probe(dev, err, "Cannot register TRNG\n"); 101*41bb142aSMarek Behún 102*41bb142aSMarek Behún return 0; 103*41bb142aSMarek Behún } 104