104dc82e1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2730d02e2SJoshua Henderson /* 3730d02e2SJoshua Henderson * PIC32 RNG driver 4730d02e2SJoshua Henderson * 5730d02e2SJoshua Henderson * Joshua Henderson <joshua.henderson@microchip.com> 6730d02e2SJoshua Henderson * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 7730d02e2SJoshua Henderson */ 8730d02e2SJoshua Henderson 9730d02e2SJoshua Henderson #include <linux/clk.h> 10730d02e2SJoshua Henderson #include <linux/clkdev.h> 11730d02e2SJoshua Henderson #include <linux/err.h> 12730d02e2SJoshua Henderson #include <linux/hw_random.h> 13730d02e2SJoshua Henderson #include <linux/io.h> 14730d02e2SJoshua Henderson #include <linux/kernel.h> 15*0788257aSRob Herring #include <linux/mod_devicetable.h> 16730d02e2SJoshua Henderson #include <linux/module.h> 17730d02e2SJoshua Henderson #include <linux/platform_device.h> 18730d02e2SJoshua Henderson #include <linux/slab.h> 19730d02e2SJoshua Henderson 20730d02e2SJoshua Henderson #define RNGCON 0x04 21730d02e2SJoshua Henderson #define TRNGEN BIT(8) 22730d02e2SJoshua Henderson #define TRNGMOD BIT(11) 23730d02e2SJoshua Henderson #define RNGSEED1 0x18 24730d02e2SJoshua Henderson #define RNGSEED2 0x1C 25730d02e2SJoshua Henderson #define RNGRCNT 0x20 26730d02e2SJoshua Henderson #define RCNT_MASK 0x7F 27730d02e2SJoshua Henderson 28730d02e2SJoshua Henderson struct pic32_rng { 29730d02e2SJoshua Henderson void __iomem *base; 30730d02e2SJoshua Henderson struct hwrng rng; 31730d02e2SJoshua Henderson }; 32730d02e2SJoshua Henderson 33730d02e2SJoshua Henderson /* 34730d02e2SJoshua Henderson * The TRNG can generate up to 24Mbps. This is a timeout that should be safe 35730d02e2SJoshua Henderson * enough given the instructions in the loop and that the TRNG may not always 36730d02e2SJoshua Henderson * be at maximum rate. 37730d02e2SJoshua Henderson */ 38730d02e2SJoshua Henderson #define RNG_TIMEOUT 500 39730d02e2SJoshua Henderson 40ac0042faSMartin Kaiser static int pic32_rng_init(struct hwrng *rng) 41ac0042faSMartin Kaiser { 42ac0042faSMartin Kaiser struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 43ac0042faSMartin Kaiser 44ac0042faSMartin Kaiser /* enable TRNG in enhanced mode */ 45ac0042faSMartin Kaiser writel(TRNGEN | TRNGMOD, priv->base + RNGCON); 46ac0042faSMartin Kaiser return 0; 47ac0042faSMartin Kaiser } 48ac0042faSMartin Kaiser 49730d02e2SJoshua Henderson static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, 50730d02e2SJoshua Henderson bool wait) 51730d02e2SJoshua Henderson { 52730d02e2SJoshua Henderson struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 53730d02e2SJoshua Henderson u64 *data = buf; 54730d02e2SJoshua Henderson u32 t; 55730d02e2SJoshua Henderson unsigned int timeout = RNG_TIMEOUT; 56730d02e2SJoshua Henderson 57730d02e2SJoshua Henderson do { 58730d02e2SJoshua Henderson t = readl(priv->base + RNGRCNT) & RCNT_MASK; 59730d02e2SJoshua Henderson if (t == 64) { 60730d02e2SJoshua Henderson /* TRNG value comes through the seed registers */ 61730d02e2SJoshua Henderson *data = ((u64)readl(priv->base + RNGSEED2) << 32) + 62730d02e2SJoshua Henderson readl(priv->base + RNGSEED1); 63730d02e2SJoshua Henderson return 8; 64730d02e2SJoshua Henderson } 65730d02e2SJoshua Henderson } while (wait && --timeout); 66730d02e2SJoshua Henderson 67730d02e2SJoshua Henderson return -EIO; 68730d02e2SJoshua Henderson } 69730d02e2SJoshua Henderson 70ac0042faSMartin Kaiser static void pic32_rng_cleanup(struct hwrng *rng) 71ac0042faSMartin Kaiser { 72ac0042faSMartin Kaiser struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 73ac0042faSMartin Kaiser 74ac0042faSMartin Kaiser writel(0, priv->base + RNGCON); 75ac0042faSMartin Kaiser } 76ac0042faSMartin Kaiser 77730d02e2SJoshua Henderson static int pic32_rng_probe(struct platform_device *pdev) 78730d02e2SJoshua Henderson { 79730d02e2SJoshua Henderson struct pic32_rng *priv; 806755ad74SMartin Kaiser struct clk *clk; 81730d02e2SJoshua Henderson 82730d02e2SJoshua Henderson priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 83730d02e2SJoshua Henderson if (!priv) 84730d02e2SJoshua Henderson return -ENOMEM; 85730d02e2SJoshua Henderson 8664b7bf13SYueHaibing priv->base = devm_platform_ioremap_resource(pdev, 0); 87730d02e2SJoshua Henderson if (IS_ERR(priv->base)) 88730d02e2SJoshua Henderson return PTR_ERR(priv->base); 89730d02e2SJoshua Henderson 906755ad74SMartin Kaiser clk = devm_clk_get_enabled(&pdev->dev, NULL); 916755ad74SMartin Kaiser if (IS_ERR(clk)) 926755ad74SMartin Kaiser return PTR_ERR(clk); 93730d02e2SJoshua Henderson 94730d02e2SJoshua Henderson priv->rng.name = pdev->name; 95ac0042faSMartin Kaiser priv->rng.init = pic32_rng_init; 96730d02e2SJoshua Henderson priv->rng.read = pic32_rng_read; 97ac0042faSMartin Kaiser priv->rng.cleanup = pic32_rng_cleanup; 98730d02e2SJoshua Henderson 99ac0042faSMartin Kaiser return devm_hwrng_register(&pdev->dev, &priv->rng); 100730d02e2SJoshua Henderson } 101730d02e2SJoshua Henderson 102e05231a5SHerbert Xu static const struct of_device_id pic32_rng_of_match[] __maybe_unused = { 103730d02e2SJoshua Henderson { .compatible = "microchip,pic32mzda-rng", }, 104730d02e2SJoshua Henderson { /* sentinel */ } 105730d02e2SJoshua Henderson }; 106730d02e2SJoshua Henderson MODULE_DEVICE_TABLE(of, pic32_rng_of_match); 107730d02e2SJoshua Henderson 108730d02e2SJoshua Henderson static struct platform_driver pic32_rng_driver = { 109730d02e2SJoshua Henderson .probe = pic32_rng_probe, 110730d02e2SJoshua Henderson .driver = { 111730d02e2SJoshua Henderson .name = "pic32-rng", 112*0788257aSRob Herring .of_match_table = pic32_rng_of_match, 113730d02e2SJoshua Henderson }, 114730d02e2SJoshua Henderson }; 115730d02e2SJoshua Henderson 116730d02e2SJoshua Henderson module_platform_driver(pic32_rng_driver); 117730d02e2SJoshua Henderson 118730d02e2SJoshua Henderson MODULE_LICENSE("GPL"); 119730d02e2SJoshua Henderson MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 120730d02e2SJoshua Henderson MODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); 121