1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * PIC32 RNG driver |
4 | * |
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 0x04 |
21 | #define TRNGEN BIT(8) |
22 | #define TRNGMOD BIT(11) |
23 | #define RNGSEED1 0x18 |
24 | #define RNGSEED2 0x1C |
25 | #define RNGRCNT 0x20 |
26 | #define RCNT_MASK 0x7F |
27 | |
28 | struct 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 safe |
35 | * enough given the instructions in the loop and that the TRNG may not always |
36 | * be at maximum rate. |
37 | */ |
38 | #define RNG_TIMEOUT 500 |
39 | |
40 | static 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, addr: priv->base + RNGCON); |
46 | return 0; |
47 | } |
48 | |
49 | static 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(addr: priv->base + RNGRCNT) & RCNT_MASK; |
59 | if (t == 64) { |
60 | /* TRNG value comes through the seed registers */ |
61 | *data = ((u64)readl(addr: priv->base + RNGSEED2) << 32) + |
62 | readl(addr: priv->base + RNGSEED1); |
63 | return 8; |
64 | } |
65 | } while (wait && --timeout); |
66 | |
67 | return -EIO; |
68 | } |
69 | |
70 | static void pic32_rng_cleanup(struct hwrng *rng) |
71 | { |
72 | struct pic32_rng *priv = container_of(rng, struct pic32_rng, rng); |
73 | |
74 | writel(val: 0, addr: priv->base + RNGCON); |
75 | } |
76 | |
77 | static int pic32_rng_probe(struct platform_device *pdev) |
78 | { |
79 | struct pic32_rng *priv; |
80 | struct clk *clk; |
81 | |
82 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
83 | if (!priv) |
84 | return -ENOMEM; |
85 | |
86 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
87 | if (IS_ERR(ptr: priv->base)) |
88 | return PTR_ERR(ptr: priv->base); |
89 | |
90 | clk = devm_clk_get_enabled(dev: &pdev->dev, NULL); |
91 | if (IS_ERR(ptr: clk)) |
92 | return PTR_ERR(ptr: 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(dev: &pdev->dev, rng: &priv->rng); |
100 | } |
101 | |
102 | static const struct of_device_id pic32_rng_of_match[] __maybe_unused = { |
103 | { .compatible = "microchip,pic32mzda-rng" , }, |
104 | { /* sentinel */ } |
105 | }; |
106 | MODULE_DEVICE_TABLE(of, pic32_rng_of_match); |
107 | |
108 | static 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 | |
116 | module_platform_driver(pic32_rng_driver); |
117 | |
118 | MODULE_LICENSE("GPL" ); |
119 | MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>" ); |
120 | MODULE_DESCRIPTION("Microchip PIC32 RNG Driver" ); |
121 | |