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> 15730d02e2SJoshua Henderson #include <linux/module.h> 16730d02e2SJoshua Henderson #include <linux/of.h> 17730d02e2SJoshua Henderson #include <linux/of_device.h> 18730d02e2SJoshua Henderson #include <linux/platform_device.h> 19730d02e2SJoshua Henderson #include <linux/slab.h> 20730d02e2SJoshua Henderson 21730d02e2SJoshua Henderson #define RNGCON 0x04 22730d02e2SJoshua Henderson #define TRNGEN BIT(8) 23730d02e2SJoshua Henderson #define PRNGEN BIT(9) 24730d02e2SJoshua Henderson #define PRNGCONT BIT(10) 25730d02e2SJoshua Henderson #define TRNGMOD BIT(11) 26730d02e2SJoshua Henderson #define SEEDLOAD BIT(12) 27730d02e2SJoshua Henderson #define RNGPOLY1 0x08 28730d02e2SJoshua Henderson #define RNGPOLY2 0x0C 29730d02e2SJoshua Henderson #define RNGNUMGEN1 0x10 30730d02e2SJoshua Henderson #define RNGNUMGEN2 0x14 31730d02e2SJoshua Henderson #define RNGSEED1 0x18 32730d02e2SJoshua Henderson #define RNGSEED2 0x1C 33730d02e2SJoshua Henderson #define RNGRCNT 0x20 34730d02e2SJoshua Henderson #define RCNT_MASK 0x7F 35730d02e2SJoshua Henderson 36730d02e2SJoshua Henderson struct pic32_rng { 37730d02e2SJoshua Henderson void __iomem *base; 38730d02e2SJoshua Henderson struct hwrng rng; 39730d02e2SJoshua Henderson struct clk *clk; 40730d02e2SJoshua Henderson }; 41730d02e2SJoshua Henderson 42730d02e2SJoshua Henderson /* 43730d02e2SJoshua Henderson * The TRNG can generate up to 24Mbps. This is a timeout that should be safe 44730d02e2SJoshua Henderson * enough given the instructions in the loop and that the TRNG may not always 45730d02e2SJoshua Henderson * be at maximum rate. 46730d02e2SJoshua Henderson */ 47730d02e2SJoshua Henderson #define RNG_TIMEOUT 500 48730d02e2SJoshua Henderson 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 70730d02e2SJoshua Henderson static int pic32_rng_probe(struct platform_device *pdev) 71730d02e2SJoshua Henderson { 72730d02e2SJoshua Henderson struct pic32_rng *priv; 73730d02e2SJoshua Henderson u32 v; 74730d02e2SJoshua Henderson int ret; 75730d02e2SJoshua Henderson 76730d02e2SJoshua Henderson priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 77730d02e2SJoshua Henderson if (!priv) 78730d02e2SJoshua Henderson return -ENOMEM; 79730d02e2SJoshua Henderson 8064b7bf13SYueHaibing priv->base = devm_platform_ioremap_resource(pdev, 0); 81730d02e2SJoshua Henderson if (IS_ERR(priv->base)) 82730d02e2SJoshua Henderson return PTR_ERR(priv->base); 83730d02e2SJoshua Henderson 84730d02e2SJoshua Henderson priv->clk = devm_clk_get(&pdev->dev, NULL); 85730d02e2SJoshua Henderson if (IS_ERR(priv->clk)) 86730d02e2SJoshua Henderson return PTR_ERR(priv->clk); 87730d02e2SJoshua Henderson 88730d02e2SJoshua Henderson ret = clk_prepare_enable(priv->clk); 89730d02e2SJoshua Henderson if (ret) 90730d02e2SJoshua Henderson return ret; 91730d02e2SJoshua Henderson 92730d02e2SJoshua Henderson /* enable TRNG in enhanced mode */ 93730d02e2SJoshua Henderson v = TRNGEN | TRNGMOD; 94730d02e2SJoshua Henderson writel(v, priv->base + RNGCON); 95730d02e2SJoshua Henderson 96730d02e2SJoshua Henderson priv->rng.name = pdev->name; 97730d02e2SJoshua Henderson priv->rng.read = pic32_rng_read; 98730d02e2SJoshua Henderson 99730d02e2SJoshua Henderson ret = hwrng_register(&priv->rng); 100730d02e2SJoshua Henderson if (ret) 101730d02e2SJoshua Henderson goto err_register; 102730d02e2SJoshua Henderson 103730d02e2SJoshua Henderson platform_set_drvdata(pdev, priv); 104730d02e2SJoshua Henderson 105730d02e2SJoshua Henderson return 0; 106730d02e2SJoshua Henderson 107730d02e2SJoshua Henderson err_register: 108730d02e2SJoshua Henderson clk_disable_unprepare(priv->clk); 109730d02e2SJoshua Henderson return ret; 110730d02e2SJoshua Henderson } 111730d02e2SJoshua Henderson 112730d02e2SJoshua Henderson static int pic32_rng_remove(struct platform_device *pdev) 113730d02e2SJoshua Henderson { 114730d02e2SJoshua Henderson struct pic32_rng *rng = platform_get_drvdata(pdev); 115730d02e2SJoshua Henderson 116730d02e2SJoshua Henderson hwrng_unregister(&rng->rng); 117730d02e2SJoshua Henderson writel(0, rng->base + RNGCON); 118730d02e2SJoshua Henderson clk_disable_unprepare(rng->clk); 119730d02e2SJoshua Henderson return 0; 120730d02e2SJoshua Henderson } 121730d02e2SJoshua Henderson 122*e05231a5SHerbert Xu static const struct of_device_id pic32_rng_of_match[] __maybe_unused = { 123730d02e2SJoshua Henderson { .compatible = "microchip,pic32mzda-rng", }, 124730d02e2SJoshua Henderson { /* sentinel */ } 125730d02e2SJoshua Henderson }; 126730d02e2SJoshua Henderson MODULE_DEVICE_TABLE(of, pic32_rng_of_match); 127730d02e2SJoshua Henderson 128730d02e2SJoshua Henderson static struct platform_driver pic32_rng_driver = { 129730d02e2SJoshua Henderson .probe = pic32_rng_probe, 130730d02e2SJoshua Henderson .remove = pic32_rng_remove, 131730d02e2SJoshua Henderson .driver = { 132730d02e2SJoshua Henderson .name = "pic32-rng", 133730d02e2SJoshua Henderson .of_match_table = of_match_ptr(pic32_rng_of_match), 134730d02e2SJoshua Henderson }, 135730d02e2SJoshua Henderson }; 136730d02e2SJoshua Henderson 137730d02e2SJoshua Henderson module_platform_driver(pic32_rng_driver); 138730d02e2SJoshua Henderson 139730d02e2SJoshua Henderson MODULE_LICENSE("GPL"); 140730d02e2SJoshua Henderson MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 141730d02e2SJoshua Henderson MODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); 142