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