1*e53ca8efSChristian Marangi // SPDX-License-Identifier: GPL-2.0 2*e53ca8efSChristian Marangi /* Copyright (C) 2024 Christian Marangi */ 3*e53ca8efSChristian Marangi 4*e53ca8efSChristian Marangi #include <linux/kernel.h> 5*e53ca8efSChristian Marangi #include <linux/module.h> 6*e53ca8efSChristian Marangi #include <linux/mod_devicetable.h> 7*e53ca8efSChristian Marangi #include <linux/bitfield.h> 8*e53ca8efSChristian Marangi #include <linux/delay.h> 9*e53ca8efSChristian Marangi #include <linux/hw_random.h> 10*e53ca8efSChristian Marangi #include <linux/interrupt.h> 11*e53ca8efSChristian Marangi #include <linux/io.h> 12*e53ca8efSChristian Marangi #include <linux/iopoll.h> 13*e53ca8efSChristian Marangi #include <linux/platform_device.h> 14*e53ca8efSChristian Marangi 15*e53ca8efSChristian Marangi #define TRNG_IP_RDY 0x800 16*e53ca8efSChristian Marangi #define CNT_TRANS GENMASK(15, 8) 17*e53ca8efSChristian Marangi #define SAMPLE_RDY BIT(0) 18*e53ca8efSChristian Marangi #define TRNG_NS_SEK_AND_DAT_EN 0x804 19*e53ca8efSChristian Marangi #define RNG_EN BIT(31) /* referenced as ring_en */ 20*e53ca8efSChristian Marangi #define RAW_DATA_EN BIT(16) 21*e53ca8efSChristian Marangi #define TRNG_HEALTH_TEST_SW_RST 0x808 22*e53ca8efSChristian Marangi #define SW_RST BIT(0) /* Active High */ 23*e53ca8efSChristian Marangi #define TRNG_INTR_EN 0x818 24*e53ca8efSChristian Marangi #define INTR_MASK BIT(16) 25*e53ca8efSChristian Marangi #define CONTINUOUS_HEALTH_INITR_EN BIT(2) 26*e53ca8efSChristian Marangi #define SW_STARTUP_INITR_EN BIT(1) 27*e53ca8efSChristian Marangi #define RST_STARTUP_INITR_EN BIT(0) 28*e53ca8efSChristian Marangi /* Notice that Health Test are done only out of Reset and with RNG_EN */ 29*e53ca8efSChristian Marangi #define TRNG_HEALTH_TEST_STATUS 0x824 30*e53ca8efSChristian Marangi #define CONTINUOUS_HEALTH_AP_TEST_FAIL BIT(23) 31*e53ca8efSChristian Marangi #define CONTINUOUS_HEALTH_RC_TEST_FAIL BIT(22) 32*e53ca8efSChristian Marangi #define SW_STARTUP_TEST_DONE BIT(21) 33*e53ca8efSChristian Marangi #define SW_STARTUP_AP_TEST_FAIL BIT(20) 34*e53ca8efSChristian Marangi #define SW_STARTUP_RC_TEST_FAIL BIT(19) 35*e53ca8efSChristian Marangi #define RST_STARTUP_TEST_DONE BIT(18) 36*e53ca8efSChristian Marangi #define RST_STARTUP_AP_TEST_FAIL BIT(17) 37*e53ca8efSChristian Marangi #define RST_STARTUP_RC_TEST_FAIL BIT(16) 38*e53ca8efSChristian Marangi #define RAW_DATA_VALID BIT(7) 39*e53ca8efSChristian Marangi 40*e53ca8efSChristian Marangi #define TRNG_RAW_DATA_OUT 0x828 41*e53ca8efSChristian Marangi 42*e53ca8efSChristian Marangi #define TRNG_CNT_TRANS_VALID 0x80 43*e53ca8efSChristian Marangi #define BUSY_LOOP_SLEEP 10 44*e53ca8efSChristian Marangi #define BUSY_LOOP_TIMEOUT (BUSY_LOOP_SLEEP * 10000) 45*e53ca8efSChristian Marangi 46*e53ca8efSChristian Marangi struct airoha_trng { 47*e53ca8efSChristian Marangi void __iomem *base; 48*e53ca8efSChristian Marangi struct hwrng rng; 49*e53ca8efSChristian Marangi struct device *dev; 50*e53ca8efSChristian Marangi 51*e53ca8efSChristian Marangi struct completion rng_op_done; 52*e53ca8efSChristian Marangi }; 53*e53ca8efSChristian Marangi 54*e53ca8efSChristian Marangi static int airoha_trng_irq_mask(struct airoha_trng *trng) 55*e53ca8efSChristian Marangi { 56*e53ca8efSChristian Marangi u32 val; 57*e53ca8efSChristian Marangi 58*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_INTR_EN); 59*e53ca8efSChristian Marangi val |= INTR_MASK; 60*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_INTR_EN); 61*e53ca8efSChristian Marangi 62*e53ca8efSChristian Marangi return 0; 63*e53ca8efSChristian Marangi } 64*e53ca8efSChristian Marangi 65*e53ca8efSChristian Marangi static int airoha_trng_irq_unmask(struct airoha_trng *trng) 66*e53ca8efSChristian Marangi { 67*e53ca8efSChristian Marangi u32 val; 68*e53ca8efSChristian Marangi 69*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_INTR_EN); 70*e53ca8efSChristian Marangi val &= ~INTR_MASK; 71*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_INTR_EN); 72*e53ca8efSChristian Marangi 73*e53ca8efSChristian Marangi return 0; 74*e53ca8efSChristian Marangi } 75*e53ca8efSChristian Marangi 76*e53ca8efSChristian Marangi static int airoha_trng_init(struct hwrng *rng) 77*e53ca8efSChristian Marangi { 78*e53ca8efSChristian Marangi struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); 79*e53ca8efSChristian Marangi int ret; 80*e53ca8efSChristian Marangi u32 val; 81*e53ca8efSChristian Marangi 82*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); 83*e53ca8efSChristian Marangi val |= RNG_EN; 84*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); 85*e53ca8efSChristian Marangi 86*e53ca8efSChristian Marangi /* Set out of SW Reset */ 87*e53ca8efSChristian Marangi airoha_trng_irq_unmask(trng); 88*e53ca8efSChristian Marangi writel(0, trng->base + TRNG_HEALTH_TEST_SW_RST); 89*e53ca8efSChristian Marangi 90*e53ca8efSChristian Marangi ret = wait_for_completion_timeout(&trng->rng_op_done, BUSY_LOOP_TIMEOUT); 91*e53ca8efSChristian Marangi if (ret <= 0) { 92*e53ca8efSChristian Marangi dev_err(trng->dev, "Timeout waiting for Health Check\n"); 93*e53ca8efSChristian Marangi airoha_trng_irq_mask(trng); 94*e53ca8efSChristian Marangi return -ENODEV; 95*e53ca8efSChristian Marangi } 96*e53ca8efSChristian Marangi 97*e53ca8efSChristian Marangi /* Check if Health Test Failed */ 98*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_HEALTH_TEST_STATUS); 99*e53ca8efSChristian Marangi if (val & (RST_STARTUP_AP_TEST_FAIL | RST_STARTUP_RC_TEST_FAIL)) { 100*e53ca8efSChristian Marangi dev_err(trng->dev, "Health Check fail: %s test fail\n", 101*e53ca8efSChristian Marangi val & RST_STARTUP_AP_TEST_FAIL ? "AP" : "RC"); 102*e53ca8efSChristian Marangi return -ENODEV; 103*e53ca8efSChristian Marangi } 104*e53ca8efSChristian Marangi 105*e53ca8efSChristian Marangi /* Check if IP is ready */ 106*e53ca8efSChristian Marangi ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, 107*e53ca8efSChristian Marangi val & SAMPLE_RDY, 10, 1000); 108*e53ca8efSChristian Marangi if (ret < 0) { 109*e53ca8efSChristian Marangi dev_err(trng->dev, "Timeout waiting for IP ready"); 110*e53ca8efSChristian Marangi return -ENODEV; 111*e53ca8efSChristian Marangi } 112*e53ca8efSChristian Marangi 113*e53ca8efSChristian Marangi /* CNT_TRANS must be 0x80 for IP to be considered ready */ 114*e53ca8efSChristian Marangi ret = readl_poll_timeout(trng->base + TRNG_IP_RDY, val, 115*e53ca8efSChristian Marangi FIELD_GET(CNT_TRANS, val) == TRNG_CNT_TRANS_VALID, 116*e53ca8efSChristian Marangi 10, 1000); 117*e53ca8efSChristian Marangi if (ret < 0) { 118*e53ca8efSChristian Marangi dev_err(trng->dev, "Timeout waiting for IP ready"); 119*e53ca8efSChristian Marangi return -ENODEV; 120*e53ca8efSChristian Marangi } 121*e53ca8efSChristian Marangi 122*e53ca8efSChristian Marangi return 0; 123*e53ca8efSChristian Marangi } 124*e53ca8efSChristian Marangi 125*e53ca8efSChristian Marangi static void airoha_trng_cleanup(struct hwrng *rng) 126*e53ca8efSChristian Marangi { 127*e53ca8efSChristian Marangi struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); 128*e53ca8efSChristian Marangi u32 val; 129*e53ca8efSChristian Marangi 130*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); 131*e53ca8efSChristian Marangi val &= ~RNG_EN; 132*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); 133*e53ca8efSChristian Marangi 134*e53ca8efSChristian Marangi /* Put it in SW Reset */ 135*e53ca8efSChristian Marangi writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); 136*e53ca8efSChristian Marangi } 137*e53ca8efSChristian Marangi 138*e53ca8efSChristian Marangi static int airoha_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait) 139*e53ca8efSChristian Marangi { 140*e53ca8efSChristian Marangi struct airoha_trng *trng = container_of(rng, struct airoha_trng, rng); 141*e53ca8efSChristian Marangi u32 *data = buf; 142*e53ca8efSChristian Marangi u32 status; 143*e53ca8efSChristian Marangi int ret; 144*e53ca8efSChristian Marangi 145*e53ca8efSChristian Marangi ret = readl_poll_timeout(trng->base + TRNG_HEALTH_TEST_STATUS, status, 146*e53ca8efSChristian Marangi status & RAW_DATA_VALID, 10, 1000); 147*e53ca8efSChristian Marangi if (ret < 0) { 148*e53ca8efSChristian Marangi dev_err(trng->dev, "Timeout waiting for TRNG RAW Data valid\n"); 149*e53ca8efSChristian Marangi return ret; 150*e53ca8efSChristian Marangi } 151*e53ca8efSChristian Marangi 152*e53ca8efSChristian Marangi *data = readl(trng->base + TRNG_RAW_DATA_OUT); 153*e53ca8efSChristian Marangi 154*e53ca8efSChristian Marangi return 4; 155*e53ca8efSChristian Marangi } 156*e53ca8efSChristian Marangi 157*e53ca8efSChristian Marangi static irqreturn_t airoha_trng_irq(int irq, void *priv) 158*e53ca8efSChristian Marangi { 159*e53ca8efSChristian Marangi struct airoha_trng *trng = (struct airoha_trng *)priv; 160*e53ca8efSChristian Marangi 161*e53ca8efSChristian Marangi airoha_trng_irq_mask(trng); 162*e53ca8efSChristian Marangi /* Just complete the task, we will read the value later */ 163*e53ca8efSChristian Marangi complete(&trng->rng_op_done); 164*e53ca8efSChristian Marangi 165*e53ca8efSChristian Marangi return IRQ_HANDLED; 166*e53ca8efSChristian Marangi } 167*e53ca8efSChristian Marangi 168*e53ca8efSChristian Marangi static int airoha_trng_probe(struct platform_device *pdev) 169*e53ca8efSChristian Marangi { 170*e53ca8efSChristian Marangi struct device *dev = &pdev->dev; 171*e53ca8efSChristian Marangi struct airoha_trng *trng; 172*e53ca8efSChristian Marangi int irq, ret; 173*e53ca8efSChristian Marangi u32 val; 174*e53ca8efSChristian Marangi 175*e53ca8efSChristian Marangi trng = devm_kzalloc(dev, sizeof(*trng), GFP_KERNEL); 176*e53ca8efSChristian Marangi if (!trng) 177*e53ca8efSChristian Marangi return -ENOMEM; 178*e53ca8efSChristian Marangi 179*e53ca8efSChristian Marangi trng->base = devm_platform_ioremap_resource(pdev, 0); 180*e53ca8efSChristian Marangi if (IS_ERR(trng->base)) 181*e53ca8efSChristian Marangi return PTR_ERR(trng->base); 182*e53ca8efSChristian Marangi 183*e53ca8efSChristian Marangi irq = platform_get_irq(pdev, 0); 184*e53ca8efSChristian Marangi if (irq < 0) 185*e53ca8efSChristian Marangi return irq; 186*e53ca8efSChristian Marangi 187*e53ca8efSChristian Marangi airoha_trng_irq_mask(trng); 188*e53ca8efSChristian Marangi ret = devm_request_irq(&pdev->dev, irq, airoha_trng_irq, 0, 189*e53ca8efSChristian Marangi pdev->name, (void *)trng); 190*e53ca8efSChristian Marangi if (ret) { 191*e53ca8efSChristian Marangi dev_err(dev, "Can't get interrupt working.\n"); 192*e53ca8efSChristian Marangi return ret; 193*e53ca8efSChristian Marangi } 194*e53ca8efSChristian Marangi 195*e53ca8efSChristian Marangi init_completion(&trng->rng_op_done); 196*e53ca8efSChristian Marangi 197*e53ca8efSChristian Marangi /* Enable interrupt for SW reset Health Check */ 198*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_INTR_EN); 199*e53ca8efSChristian Marangi val |= RST_STARTUP_INITR_EN; 200*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_INTR_EN); 201*e53ca8efSChristian Marangi 202*e53ca8efSChristian Marangi /* Set output to raw data */ 203*e53ca8efSChristian Marangi val = readl(trng->base + TRNG_NS_SEK_AND_DAT_EN); 204*e53ca8efSChristian Marangi val |= RAW_DATA_EN; 205*e53ca8efSChristian Marangi writel(val, trng->base + TRNG_NS_SEK_AND_DAT_EN); 206*e53ca8efSChristian Marangi 207*e53ca8efSChristian Marangi /* Put it in SW Reset */ 208*e53ca8efSChristian Marangi writel(SW_RST, trng->base + TRNG_HEALTH_TEST_SW_RST); 209*e53ca8efSChristian Marangi 210*e53ca8efSChristian Marangi trng->dev = dev; 211*e53ca8efSChristian Marangi trng->rng.name = pdev->name; 212*e53ca8efSChristian Marangi trng->rng.init = airoha_trng_init; 213*e53ca8efSChristian Marangi trng->rng.cleanup = airoha_trng_cleanup; 214*e53ca8efSChristian Marangi trng->rng.read = airoha_trng_read; 215*e53ca8efSChristian Marangi 216*e53ca8efSChristian Marangi ret = devm_hwrng_register(dev, &trng->rng); 217*e53ca8efSChristian Marangi if (ret) { 218*e53ca8efSChristian Marangi dev_err(dev, "failed to register rng device: %d\n", ret); 219*e53ca8efSChristian Marangi return ret; 220*e53ca8efSChristian Marangi } 221*e53ca8efSChristian Marangi 222*e53ca8efSChristian Marangi return 0; 223*e53ca8efSChristian Marangi } 224*e53ca8efSChristian Marangi 225*e53ca8efSChristian Marangi static const struct of_device_id airoha_trng_of_match[] = { 226*e53ca8efSChristian Marangi { .compatible = "airoha,en7581-trng", }, 227*e53ca8efSChristian Marangi {}, 228*e53ca8efSChristian Marangi }; 229*e53ca8efSChristian Marangi MODULE_DEVICE_TABLE(of, airoha_trng_of_match); 230*e53ca8efSChristian Marangi 231*e53ca8efSChristian Marangi static struct platform_driver airoha_trng_driver = { 232*e53ca8efSChristian Marangi .driver = { 233*e53ca8efSChristian Marangi .name = "airoha-trng", 234*e53ca8efSChristian Marangi .of_match_table = airoha_trng_of_match, 235*e53ca8efSChristian Marangi }, 236*e53ca8efSChristian Marangi .probe = airoha_trng_probe, 237*e53ca8efSChristian Marangi }; 238*e53ca8efSChristian Marangi 239*e53ca8efSChristian Marangi module_platform_driver(airoha_trng_driver); 240*e53ca8efSChristian Marangi 241*e53ca8efSChristian Marangi MODULE_LICENSE("GPL"); 242*e53ca8efSChristian Marangi MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>"); 243*e53ca8efSChristian Marangi MODULE_DESCRIPTION("Airoha True Random Number Generator driver"); 244