18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PIC32 RNG driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Joshua Henderson <joshua.henderson@microchip.com> 68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/clk.h> 108c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 138c2ecf20Sopenharmony_ci#include <linux/io.h> 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define RNGCON 0x04 228c2ecf20Sopenharmony_ci#define TRNGEN BIT(8) 238c2ecf20Sopenharmony_ci#define PRNGEN BIT(9) 248c2ecf20Sopenharmony_ci#define PRNGCONT BIT(10) 258c2ecf20Sopenharmony_ci#define TRNGMOD BIT(11) 268c2ecf20Sopenharmony_ci#define SEEDLOAD BIT(12) 278c2ecf20Sopenharmony_ci#define RNGPOLY1 0x08 288c2ecf20Sopenharmony_ci#define RNGPOLY2 0x0C 298c2ecf20Sopenharmony_ci#define RNGNUMGEN1 0x10 308c2ecf20Sopenharmony_ci#define RNGNUMGEN2 0x14 318c2ecf20Sopenharmony_ci#define RNGSEED1 0x18 328c2ecf20Sopenharmony_ci#define RNGSEED2 0x1C 338c2ecf20Sopenharmony_ci#define RNGRCNT 0x20 348c2ecf20Sopenharmony_ci#define RCNT_MASK 0x7F 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct pic32_rng { 378c2ecf20Sopenharmony_ci void __iomem *base; 388c2ecf20Sopenharmony_ci struct hwrng rng; 398c2ecf20Sopenharmony_ci struct clk *clk; 408c2ecf20Sopenharmony_ci}; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * The TRNG can generate up to 24Mbps. This is a timeout that should be safe 448c2ecf20Sopenharmony_ci * enough given the instructions in the loop and that the TRNG may not always 458c2ecf20Sopenharmony_ci * be at maximum rate. 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#define RNG_TIMEOUT 500 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic int pic32_rng_read(struct hwrng *rng, void *buf, size_t max, 508c2ecf20Sopenharmony_ci bool wait) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); 538c2ecf20Sopenharmony_ci u64 *data = buf; 548c2ecf20Sopenharmony_ci u32 t; 558c2ecf20Sopenharmony_ci unsigned int timeout = RNG_TIMEOUT; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci do { 588c2ecf20Sopenharmony_ci t = readl(priv->base + RNGRCNT) & RCNT_MASK; 598c2ecf20Sopenharmony_ci if (t == 64) { 608c2ecf20Sopenharmony_ci /* TRNG value comes through the seed registers */ 618c2ecf20Sopenharmony_ci *data = ((u64)readl(priv->base + RNGSEED2) << 32) + 628c2ecf20Sopenharmony_ci readl(priv->base + RNGSEED1); 638c2ecf20Sopenharmony_ci return 8; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci } while (wait && --timeout); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return -EIO; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int pic32_rng_probe(struct platform_device *pdev) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci struct pic32_rng *priv; 738c2ecf20Sopenharmony_ci u32 v; 748c2ecf20Sopenharmony_ci int ret; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 778c2ecf20Sopenharmony_ci if (!priv) 788c2ecf20Sopenharmony_ci return -ENOMEM; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci priv->base = devm_platform_ioremap_resource(pdev, 0); 818c2ecf20Sopenharmony_ci if (IS_ERR(priv->base)) 828c2ecf20Sopenharmony_ci return PTR_ERR(priv->base); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(&pdev->dev, NULL); 858c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) 868c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 898c2ecf20Sopenharmony_ci if (ret) 908c2ecf20Sopenharmony_ci return ret; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* enable TRNG in enhanced mode */ 938c2ecf20Sopenharmony_ci v = TRNGEN | TRNGMOD; 948c2ecf20Sopenharmony_ci writel(v, priv->base + RNGCON); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci priv->rng.name = pdev->name; 978c2ecf20Sopenharmony_ci priv->rng.read = pic32_rng_read; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci ret = hwrng_register(&priv->rng); 1008c2ecf20Sopenharmony_ci if (ret) 1018c2ecf20Sopenharmony_ci goto err_register; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cierr_register: 1088c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int pic32_rng_remove(struct platform_device *pdev) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct pic32_rng *rng = platform_get_drvdata(pdev); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci hwrng_unregister(&rng->rng); 1178c2ecf20Sopenharmony_ci writel(0, rng->base + RNGCON); 1188c2ecf20Sopenharmony_ci clk_disable_unprepare(rng->clk); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic const struct of_device_id pic32_rng_of_match[] __maybe_unused = { 1238c2ecf20Sopenharmony_ci { .compatible = "microchip,pic32mzda-rng", }, 1248c2ecf20Sopenharmony_ci { /* sentinel */ } 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, pic32_rng_of_match); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic struct platform_driver pic32_rng_driver = { 1298c2ecf20Sopenharmony_ci .probe = pic32_rng_probe, 1308c2ecf20Sopenharmony_ci .remove = pic32_rng_remove, 1318c2ecf20Sopenharmony_ci .driver = { 1328c2ecf20Sopenharmony_ci .name = "pic32-rng", 1338c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(pic32_rng_of_match), 1348c2ecf20Sopenharmony_ci }, 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cimodule_platform_driver(pic32_rng_driver); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); 1418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Microchip PIC32 RNG Driver"); 142