1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * drivers/char/hw_random/timeriomem-rng.c |
4 | * |
5 | * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk> |
6 | * |
7 | * Derived from drivers/char/hw_random/omap-rng.c |
8 | * Copyright 2005 (c) MontaVista Software, Inc. |
9 | * Author: Deepak Saxena <dsaxena@plexity.net> |
10 | * |
11 | * Overview: |
12 | * This driver is useful for platforms that have an IO range that provides |
13 | * periodic random data from a single IO memory address. All the platform |
14 | * has to do is provide the address and 'wait time' that new data becomes |
15 | * available. |
16 | * |
17 | * TODO: add support for reading sizes other than 32bits and masking |
18 | */ |
19 | |
20 | #include <linux/completion.h> |
21 | #include <linux/delay.h> |
22 | #include <linux/hrtimer.h> |
23 | #include <linux/hw_random.h> |
24 | #include <linux/io.h> |
25 | #include <linux/ktime.h> |
26 | #include <linux/module.h> |
27 | #include <linux/of.h> |
28 | #include <linux/platform_device.h> |
29 | #include <linux/slab.h> |
30 | #include <linux/time.h> |
31 | #include <linux/timeriomem-rng.h> |
32 | |
33 | struct timeriomem_rng_private { |
34 | void __iomem *io_base; |
35 | ktime_t period; |
36 | unsigned int present:1; |
37 | |
38 | struct hrtimer timer; |
39 | struct completion completion; |
40 | |
41 | struct hwrng rng_ops; |
42 | }; |
43 | |
44 | static int timeriomem_rng_read(struct hwrng *hwrng, void *data, |
45 | size_t max, bool wait) |
46 | { |
47 | struct timeriomem_rng_private *priv = |
48 | container_of(hwrng, struct timeriomem_rng_private, rng_ops); |
49 | int retval = 0; |
50 | int period_us = ktime_to_us(kt: priv->period); |
51 | |
52 | /* |
53 | * There may not have been enough time for new data to be generated |
54 | * since the last request. If the caller doesn't want to wait, let them |
55 | * bail out. Otherwise, wait for the completion. If the new data has |
56 | * already been generated, the completion should already be available. |
57 | */ |
58 | if (!wait && !priv->present) |
59 | return 0; |
60 | |
61 | wait_for_completion(&priv->completion); |
62 | |
63 | do { |
64 | /* |
65 | * After the first read, all additional reads will need to wait |
66 | * for the RNG to generate new data. Since the period can have |
67 | * a wide range of values (1us to 1s have been observed), allow |
68 | * for 1% tolerance in the sleep time rather than a fixed value. |
69 | */ |
70 | if (retval > 0) |
71 | usleep_range(min: period_us, |
72 | max: period_us + max(1, period_us / 100)); |
73 | |
74 | *(u32 *)data = readl(addr: priv->io_base); |
75 | retval += sizeof(u32); |
76 | data += sizeof(u32); |
77 | max -= sizeof(u32); |
78 | } while (wait && max > sizeof(u32)); |
79 | |
80 | /* |
81 | * Block any new callers until the RNG has had time to generate new |
82 | * data. |
83 | */ |
84 | priv->present = 0; |
85 | reinit_completion(x: &priv->completion); |
86 | hrtimer_forward_now(timer: &priv->timer, interval: priv->period); |
87 | hrtimer_restart(timer: &priv->timer); |
88 | |
89 | return retval; |
90 | } |
91 | |
92 | static enum hrtimer_restart timeriomem_rng_trigger(struct hrtimer *timer) |
93 | { |
94 | struct timeriomem_rng_private *priv |
95 | = container_of(timer, struct timeriomem_rng_private, timer); |
96 | |
97 | priv->present = 1; |
98 | complete(&priv->completion); |
99 | |
100 | return HRTIMER_NORESTART; |
101 | } |
102 | |
103 | static int timeriomem_rng_probe(struct platform_device *pdev) |
104 | { |
105 | struct timeriomem_rng_data *pdata = pdev->dev.platform_data; |
106 | struct timeriomem_rng_private *priv; |
107 | struct resource *res; |
108 | int err = 0; |
109 | int period; |
110 | |
111 | if (!pdev->dev.of_node && !pdata) { |
112 | dev_err(&pdev->dev, "timeriomem_rng_data is missing\n" ); |
113 | return -EINVAL; |
114 | } |
115 | |
116 | /* Allocate memory for the device structure (and zero it) */ |
117 | priv = devm_kzalloc(dev: &pdev->dev, |
118 | size: sizeof(struct timeriomem_rng_private), GFP_KERNEL); |
119 | if (!priv) |
120 | return -ENOMEM; |
121 | |
122 | platform_set_drvdata(pdev, data: priv); |
123 | |
124 | priv->io_base = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
125 | if (IS_ERR(ptr: priv->io_base)) |
126 | return PTR_ERR(ptr: priv->io_base); |
127 | |
128 | if (res->start % 4 != 0 || resource_size(res) < 4) { |
129 | dev_err(&pdev->dev, |
130 | "address must be at least four bytes wide and 32-bit aligned\n" ); |
131 | return -EINVAL; |
132 | } |
133 | |
134 | if (pdev->dev.of_node) { |
135 | int i; |
136 | |
137 | if (!of_property_read_u32(np: pdev->dev.of_node, |
138 | propname: "period" , out_value: &i)) |
139 | period = i; |
140 | else { |
141 | dev_err(&pdev->dev, "missing period\n" ); |
142 | return -EINVAL; |
143 | } |
144 | |
145 | if (!of_property_read_u32(np: pdev->dev.of_node, |
146 | propname: "quality" , out_value: &i)) |
147 | priv->rng_ops.quality = i; |
148 | } else { |
149 | period = pdata->period; |
150 | priv->rng_ops.quality = pdata->quality; |
151 | } |
152 | |
153 | priv->period = ns_to_ktime(ns: period * NSEC_PER_USEC); |
154 | init_completion(x: &priv->completion); |
155 | hrtimer_init(timer: &priv->timer, CLOCK_MONOTONIC, mode: HRTIMER_MODE_ABS); |
156 | priv->timer.function = timeriomem_rng_trigger; |
157 | |
158 | priv->rng_ops.name = dev_name(dev: &pdev->dev); |
159 | priv->rng_ops.read = timeriomem_rng_read; |
160 | |
161 | /* Assume random data is already available. */ |
162 | priv->present = 1; |
163 | complete(&priv->completion); |
164 | |
165 | err = devm_hwrng_register(dev: &pdev->dev, rng: &priv->rng_ops); |
166 | if (err) { |
167 | dev_err(&pdev->dev, "problem registering\n" ); |
168 | return err; |
169 | } |
170 | |
171 | dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n" , |
172 | priv->io_base, period); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | static int timeriomem_rng_remove(struct platform_device *pdev) |
178 | { |
179 | struct timeriomem_rng_private *priv = platform_get_drvdata(pdev); |
180 | |
181 | hrtimer_cancel(timer: &priv->timer); |
182 | |
183 | return 0; |
184 | } |
185 | |
186 | static const struct of_device_id timeriomem_rng_match[] = { |
187 | { .compatible = "timeriomem_rng" }, |
188 | {}, |
189 | }; |
190 | MODULE_DEVICE_TABLE(of, timeriomem_rng_match); |
191 | |
192 | static struct platform_driver timeriomem_rng_driver = { |
193 | .driver = { |
194 | .name = "timeriomem_rng" , |
195 | .of_match_table = timeriomem_rng_match, |
196 | }, |
197 | .probe = timeriomem_rng_probe, |
198 | .remove = timeriomem_rng_remove, |
199 | }; |
200 | |
201 | module_platform_driver(timeriomem_rng_driver); |
202 | |
203 | MODULE_LICENSE("GPL" ); |
204 | MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>" ); |
205 | MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver" ); |
206 | |