1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (C) 2020 Xiphera Ltd. */ 3 4 #include <linux/kernel.h> 5 #include <linux/module.h> 6 #include <linux/err.h> 7 #include <linux/io.h> 8 #include <linux/hw_random.h> 9 #include <linux/platform_device.h> 10 #include <linux/delay.h> 11 12 #define CONTROL_REG 0x00000000 13 #define STATUS_REG 0x00000004 14 #define RAND_REG 0x00000000 15 16 #define HOST_TO_TRNG_RESET 0x00000001 17 #define HOST_TO_TRNG_RELEASE_RESET 0x00000002 18 #define HOST_TO_TRNG_ENABLE 0x80000000 19 #define HOST_TO_TRNG_ZEROIZE 0x80000004 20 #define HOST_TO_TRNG_ACK_ZEROIZE 0x80000008 21 #define HOST_TO_TRNG_READ 0x8000000F 22 23 /* trng statuses */ 24 #define TRNG_ACK_RESET 0x000000AC 25 #define TRNG_SUCCESSFUL_STARTUP 0x00000057 26 #define TRNG_FAILED_STARTUP 0x000000FA 27 #define TRNG_NEW_RAND_AVAILABLE 0x000000ED 28 29 struct xiphera_trng { 30 void __iomem *mem; 31 struct hwrng rng; 32 }; 33 34 static int xiphera_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 35 { 36 struct xiphera_trng *trng = container_of(rng, struct xiphera_trng, rng); 37 int ret = 0; 38 39 while (max >= sizeof(u32)) { 40 /* check for data */ 41 if (readl(trng->mem + STATUS_REG) == TRNG_NEW_RAND_AVAILABLE) { 42 *(u32 *)buf = readl(trng->mem + RAND_REG); 43 /* 44 * Inform the trng of the read 45 * and re-enable it to produce a new random number 46 */ 47 writel(HOST_TO_TRNG_READ, trng->mem + CONTROL_REG); 48 writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG); 49 ret += sizeof(u32); 50 buf += sizeof(u32); 51 max -= sizeof(u32); 52 } else { 53 break; 54 } 55 } 56 return ret; 57 } 58 59 static int xiphera_trng_probe(struct platform_device *pdev) 60 { 61 int ret; 62 struct xiphera_trng *trng; 63 struct device *dev = &pdev->dev; 64 65 trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL); 66 if (!trng) 67 return -ENOMEM; 68 69 trng->mem = devm_platform_ioremap_resource(pdev, 0); 70 if (IS_ERR(trng->mem)) 71 return PTR_ERR(trng->mem); 72 73 /* 74 * the trng needs to be reset first which might not happen in time, 75 * hence we incorporate a small delay to ensure proper behaviour 76 */ 77 writel(HOST_TO_TRNG_RESET, trng->mem + CONTROL_REG); 78 usleep_range(100, 200); 79 80 if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) { 81 /* 82 * there is a small chance the trng is just not ready yet, 83 * so we try one more time. If the second time fails, we give up 84 */ 85 usleep_range(100, 200); 86 if (readl(trng->mem + STATUS_REG) != TRNG_ACK_RESET) { 87 dev_err(dev, "failed to reset the trng ip\n"); 88 return -ENODEV; 89 } 90 } 91 92 /* 93 * once again, to ensure proper behaviour we sleep 94 * for a while after zeroizing the trng 95 */ 96 writel(HOST_TO_TRNG_RELEASE_RESET, trng->mem + CONTROL_REG); 97 writel(HOST_TO_TRNG_ENABLE, trng->mem + CONTROL_REG); 98 writel(HOST_TO_TRNG_ZEROIZE, trng->mem + CONTROL_REG); 99 msleep(20); 100 101 if (readl(trng->mem + STATUS_REG) != TRNG_SUCCESSFUL_STARTUP) { 102 /* diagnose the reason for the failure */ 103 if (readl(trng->mem + STATUS_REG) == TRNG_FAILED_STARTUP) { 104 dev_err(dev, "trng ip startup-tests failed\n"); 105 return -ENODEV; 106 } 107 dev_err(dev, "startup-tests yielded no response\n"); 108 return -ENODEV; 109 } 110 111 writel(HOST_TO_TRNG_ACK_ZEROIZE, trng->mem + CONTROL_REG); 112 113 trng->rng.name = pdev->name; 114 trng->rng.read = xiphera_trng_read; 115 trng->rng.quality = 900; 116 117 ret = devm_hwrng_register(dev, &trng->rng); 118 if (ret) { 119 dev_err(dev, "failed to register rng device: %d\n", ret); 120 return ret; 121 } 122 123 return 0; 124 } 125 126 static const struct of_device_id xiphera_trng_of_match[] = { 127 { .compatible = "xiphera,xip8001b-trng", }, 128 {}, 129 }; 130 MODULE_DEVICE_TABLE(of, xiphera_trng_of_match); 131 132 static struct platform_driver xiphera_trng_driver = { 133 .driver = { 134 .name = "xiphera-trng", 135 .of_match_table = xiphera_trng_of_match, 136 }, 137 .probe = xiphera_trng_probe, 138 }; 139 140 module_platform_driver(xiphera_trng_driver); 141 142 MODULE_LICENSE("GPL"); 143 MODULE_AUTHOR("Atte Tommiska"); 144 MODULE_DESCRIPTION("Xiphera FPGA-based true random number generator driver"); 145