1 /* 2 * PIC32 RNG driver 3 * 4 * Joshua Henderson <joshua.henderson@microchip.com> 5 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 6 * 7 * This program is free software; you can distribute it and/or modify it 8 * under the terms of the GNU General Public License (Version 2) as 9 * published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * for more details. 15 */ 16 17 #include <linux/clk.h> 18 #include <linux/clkdev.h> 19 #include <linux/err.h> 20 #include <linux/hw_random.h> 21 #include <linux/io.h> 22 #include <linux/kernel.h> 23 #include <linux/module.h> 24 #include <linux/of.h> 25 #include <linux/of_device.h> 26 #include <linux/platform_device.h> 27 #include <linux/slab.h> 28 29 #define RNGCON 0x04 30 #define TRNGEN BIT(8) 31 #define PRNGEN BIT(9) 32 #define PRNGCONT BIT(10) 33 #define TRNGMOD BIT(11) 34 #define SEEDLOAD BIT(12) 35 #define RNGPOLY1 0x08 36 #define RNGPOLY2 0x0C 37 #define RNGNUMGEN1 0x10 38 #define RNGNUMGEN2 0x14 39 #define RNGSEED1 0x18 40 #define RNGSEED2 0x1C 41 #define RNGRCNT 0x20 42 #define RCNT_MASK 0x7F 43 44 struct pic32_rng { 45 void __iomem *base; 46 struct hwrng rng; 47 struct clk *clk; 48 }; 49 50 /* 51 * The TRNG can generate up to 24Mbps. This is a timeout that should be safe 52 * enough given the instructions in the loop and that the TRNG may not always 53 * be at maximum rate. 54 */ 55 #define RNG_TIMEOUT 500 56 57 static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, 58 bool wait) 59 { 60 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 61 u64 *data = buf; 62 u32 t; 63 unsigned int timeout = RNG_TIMEOUT; 64 65 if (max < 8) 66 return 0; 67 68 do { 69 t = readl(priv->base + RNGRCNT) & RCNT_MASK; 70 if (t == 64) { 71 /* TRNG value comes through the seed registers */ 72 *data = ((u64)readl(priv->base + RNGSEED2) << 32) + 73 readl(priv->base + RNGSEED1); 74 return 8; 75 } 76 } while (wait && --timeout); 77 78 return -EIO; 79 } 80 81 static int pic32_rng_probe(struct platform_device *pdev) 82 { 83 struct pic32_rng *priv; 84 struct resource *res; 85 u32 v; 86 int ret; 87 88 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 89 if (!priv) 90 return -ENOMEM; 91 92 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 93 priv->base = devm_ioremap_resource(&pdev->dev, res); 94 if (IS_ERR(priv->base)) 95 return PTR_ERR(priv->base); 96 97 priv->clk = devm_clk_get(&pdev->dev, NULL); 98 if (IS_ERR(priv->clk)) 99 return PTR_ERR(priv->clk); 100 101 ret = clk_prepare_enable(priv->clk); 102 if (ret) 103 return ret; 104 105 /* enable TRNG in enhanced mode */ 106 v = TRNGEN | TRNGMOD; 107 writel(v, priv->base + RNGCON); 108 109 priv->rng.name = pdev->name; 110 priv->rng.read = pic32_rng_read; 111 112 ret = hwrng_register(&priv->rng); 113 if (ret) 114 goto err_register; 115 116 platform_set_drvdata(pdev, priv); 117 118 return 0; 119 120 err_register: 121 clk_disable_unprepare(priv->clk); 122 return ret; 123 } 124 125 static int pic32_rng_remove(struct platform_device *pdev) 126 { 127 struct pic32_rng *rng = platform_get_drvdata(pdev); 128 129 hwrng_unregister(&rng->rng); 130 writel(0, rng->base + RNGCON); 131 clk_disable_unprepare(rng->clk); 132 return 0; 133 } 134 135 static const struct of_device_id pic32_rng_of_match[] = { 136 { .compatible = "microchip,pic32mzda-rng", }, 137 { /* sentinel */ } 138 }; 139 MODULE_DEVICE_TABLE(of, pic32_rng_of_match); 140 141 static struct platform_driver pic32_rng_driver = { 142 .probe = pic32_rng_probe, 143 .remove = pic32_rng_remove, 144 .driver = { 145 .name = "pic32-rng", 146 .of_match_table = of_match_ptr(pic32_rng_of_match), 147 }, 148 }; 149 150 module_platform_driver(pic32_rng_driver); 151 152 MODULE_LICENSE("GPL"); 153 MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 154 MODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); 155