1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * PIC32 RNG driver 4 * 5 * Joshua Henderson <joshua.henderson@microchip.com> 6 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/clkdev.h> 11 #include <linux/err.h> 12 #include <linux/hw_random.h> 13 #include <linux/io.h> 14 #include <linux/kernel.h> 15 #include <linux/mod_devicetable.h> 16 #include <linux/module.h> 17 #include <linux/platform_device.h> 18 #include <linux/slab.h> 19 20 #define RNGCON 0x04 21 #define TRNGEN BIT(8) 22 #define TRNGMOD BIT(11) 23 #define RNGSEED1 0x18 24 #define RNGSEED2 0x1C 25 #define RNGRCNT 0x20 26 #define RCNT_MASK 0x7F 27 28 struct pic32_rng { 29 void __iomem *base; 30 struct hwrng rng; 31 }; 32 33 /* 34 * The TRNG can generate up to 24Mbps. This is a timeout that should be safe 35 * enough given the instructions in the loop and that the TRNG may not always 36 * be at maximum rate. 37 */ 38 #define RNG_TIMEOUT 500 39 40 static int pic32_rng_init(struct hwrng *rng) 41 { 42 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 43 44 /* enable TRNG in enhanced mode */ 45 writel(TRNGEN | TRNGMOD, priv->base + RNGCON); 46 return 0; 47 } 48 49 static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, 50 bool wait) 51 { 52 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 53 u64 *data = buf; 54 u32 t; 55 unsigned int timeout = RNG_TIMEOUT; 56 57 do { 58 t = readl(priv->base + RNGRCNT) & RCNT_MASK; 59 if (t == 64) { 60 /* TRNG value comes through the seed registers */ 61 *data = ((u64)readl(priv->base + RNGSEED2) << 32) + 62 readl(priv->base + RNGSEED1); 63 return 8; 64 } 65 } while (wait && --timeout); 66 67 return -EIO; 68 } 69 70 static void pic32_rng_cleanup(struct hwrng *rng) 71 { 72 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 73 74 writel(0, priv->base + RNGCON); 75 } 76 77 static int pic32_rng_probe(struct platform_device *pdev) 78 { 79 struct pic32_rng *priv; 80 struct clk *clk; 81 82 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 83 if (!priv) 84 return -ENOMEM; 85 86 priv->base = devm_platform_ioremap_resource(pdev, 0); 87 if (IS_ERR(priv->base)) 88 return PTR_ERR(priv->base); 89 90 clk = devm_clk_get_enabled(&pdev->dev, NULL); 91 if (IS_ERR(clk)) 92 return PTR_ERR(clk); 93 94 priv->rng.name = pdev->name; 95 priv->rng.init = pic32_rng_init; 96 priv->rng.read = pic32_rng_read; 97 priv->rng.cleanup = pic32_rng_cleanup; 98 99 return devm_hwrng_register(&pdev->dev, &priv->rng); 100 } 101 102 static const struct of_device_id pic32_rng_of_match[] __maybe_unused = { 103 { .compatible = "microchip,pic32mzda-rng", }, 104 { /* sentinel */ } 105 }; 106 MODULE_DEVICE_TABLE(of, pic32_rng_of_match); 107 108 static struct platform_driver pic32_rng_driver = { 109 .probe = pic32_rng_probe, 110 .driver = { 111 .name = "pic32-rng", 112 .of_match_table = pic32_rng_of_match, 113 }, 114 }; 115 116 module_platform_driver(pic32_rng_driver); 117 118 MODULE_LICENSE("GPL"); 119 MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 120 MODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); 121