121 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * PIC32 RNG driver4 *5 * Joshua Henderson <joshua.henderson@microchip.com>6 * Copyright (C) 2016 Microchip Technology Inc. All rights reserved.7 */8 9#include <linux/clk.h>10#include <linux/clkdev.h>11#include <linux/err.h>12#include <linux/hw_random.h>13#include <linux/io.h>14#include <linux/kernel.h>15#include <linux/mod_devicetable.h>16#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/slab.h>19 20#define RNGCON 0x0421#define TRNGEN BIT(8)22#define TRNGMOD BIT(11)23#define RNGSEED1 0x1824#define RNGSEED2 0x1C25#define RNGRCNT 0x2026#define RCNT_MASK 0x7F27 28struct pic32_rng {29 void __iomem *base;30 struct hwrng rng;31};32 33/*34 * The TRNG can generate up to 24Mbps. This is a timeout that should be safe35 * enough given the instructions in the loop and that the TRNG may not always36 * be at maximum rate.37 */38#define RNG_TIMEOUT 50039 40static int pic32_rng_init(struct hwrng *rng)41{42 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng);43 44 /* enable TRNG in enhanced mode */45 writel(TRNGEN | TRNGMOD, priv->base + RNGCON);46 return 0;47}48 49static int pic32_rng_read(struct hwrng *rng, void *buf, size_t max,50 bool wait)51{52 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng);53 u64 *data = buf;54 u32 t;55 unsigned int timeout = RNG_TIMEOUT;56 57 do {58 t = readl(priv->base + RNGRCNT) & RCNT_MASK;59 if (t == 64) {60 /* TRNG value comes through the seed registers */61 *data = ((u64)readl(priv->base + RNGSEED2) << 32) +62 readl(priv->base + RNGSEED1);63 return 8;64 }65 } while (wait && --timeout);66 67 return -EIO;68}69 70static void pic32_rng_cleanup(struct hwrng *rng)71{72 struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng);73 74 writel(0, priv->base + RNGCON);75}76 77static int pic32_rng_probe(struct platform_device *pdev)78{79 struct pic32_rng *priv;80 struct clk *clk;81 82 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);83 if (!priv)84 return -ENOMEM;85 86 priv->base = devm_platform_ioremap_resource(pdev, 0);87 if (IS_ERR(priv->base))88 return PTR_ERR(priv->base);89 90 clk = devm_clk_get_enabled(&pdev->dev, NULL);91 if (IS_ERR(clk))92 return PTR_ERR(clk);93 94 priv->rng.name = pdev->name;95 priv->rng.init = pic32_rng_init;96 priv->rng.read = pic32_rng_read;97 priv->rng.cleanup = pic32_rng_cleanup;98 99 return devm_hwrng_register(&pdev->dev, &priv->rng);100}101 102static const struct of_device_id pic32_rng_of_match[] __maybe_unused = {103 { .compatible = "microchip,pic32mzda-rng", },104 { /* sentinel */ }105};106MODULE_DEVICE_TABLE(of, pic32_rng_of_match);107 108static struct platform_driver pic32_rng_driver = {109 .probe = pic32_rng_probe,110 .driver = {111 .name = "pic32-rng",112 .of_match_table = pic32_rng_of_match,113 },114};115 116module_platform_driver(pic32_rng_driver);117 118MODULE_LICENSE("GPL");119MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");120MODULE_DESCRIPTION("Microchip PIC32 RNG Driver");121