Merge tag 'kvm-s390-next-20141204' of git://git.kernel.org/pub/scm/linux/kernel/git...
[deliverable/linux.git] / drivers / char / hw_random / atmel-rng.c
CommitLineData
677d3e2f
PK
1/*
2 * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
3 *
4 * This file is licensed under the terms of the GNU General Public
5 * License version 2. This program is licensed "as is" without any
6 * warranty of any kind, whether express or implied.
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/err.h>
13#include <linux/clk.h>
14#include <linux/io.h>
15#include <linux/hw_random.h>
16#include <linux/platform_device.h>
17
18#define TRNG_CR 0x00
19#define TRNG_ISR 0x1c
20#define TRNG_ODATA 0x50
21
22#define TRNG_KEY 0x524e4700 /* RNG */
23
24struct atmel_trng {
25 struct clk *clk;
26 void __iomem *base;
27 struct hwrng rng;
28};
29
30static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
31 bool wait)
32{
33 struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
34 u32 *data = buf;
35
36 /* data ready? */
c475c06f 37 if (readl(trng->base + TRNG_ISR) & 1) {
677d3e2f 38 *data = readl(trng->base + TRNG_ODATA);
121daad8
PK
39 /*
40 ensure data ready is only set again AFTER the next data
41 word is ready in case it got set between checking ISR
42 and reading ODATA, so we don't risk re-reading the
43 same word
44 */
45 readl(trng->base + TRNG_ISR);
677d3e2f
PK
46 return 4;
47 } else
48 return 0;
49}
50
51static int atmel_trng_probe(struct platform_device *pdev)
52{
53 struct atmel_trng *trng;
54 struct resource *res;
55 int ret;
56
677d3e2f
PK
57 trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
58 if (!trng)
59 return -ENOMEM;
60
bfaff75b
JH
61 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
62 trng->base = devm_ioremap_resource(&pdev->dev, res);
63 if (IS_ERR(trng->base))
64 return PTR_ERR(trng->base);
677d3e2f 65
0c0becd0 66 trng->clk = devm_clk_get(&pdev->dev, NULL);
677d3e2f
PK
67 if (IS_ERR(trng->clk))
68 return PTR_ERR(trng->clk);
69
70 ret = clk_enable(trng->clk);
71 if (ret)
0c0becd0 72 return ret;
677d3e2f
PK
73
74 writel(TRNG_KEY | 1, trng->base + TRNG_CR);
75 trng->rng.name = pdev->name;
76 trng->rng.read = atmel_trng_read;
77
78 ret = hwrng_register(&trng->rng);
79 if (ret)
80 goto err_register;
81
82 platform_set_drvdata(pdev, trng);
83
84 return 0;
85
86err_register:
87 clk_disable(trng->clk);
677d3e2f
PK
88 return ret;
89}
90
39af33fc 91static int atmel_trng_remove(struct platform_device *pdev)
677d3e2f
PK
92{
93 struct atmel_trng *trng = platform_get_drvdata(pdev);
94
95 hwrng_unregister(&trng->rng);
96
97 writel(TRNG_KEY, trng->base + TRNG_CR);
98 clk_disable(trng->clk);
677d3e2f 99
677d3e2f
PK
100 return 0;
101}
102
103#ifdef CONFIG_PM
104static int atmel_trng_suspend(struct device *dev)
105{
106 struct atmel_trng *trng = dev_get_drvdata(dev);
107
108 clk_disable(trng->clk);
109
110 return 0;
111}
112
113static int atmel_trng_resume(struct device *dev)
114{
115 struct atmel_trng *trng = dev_get_drvdata(dev);
116
117 return clk_enable(trng->clk);
118}
119
120static const struct dev_pm_ops atmel_trng_pm_ops = {
121 .suspend = atmel_trng_suspend,
122 .resume = atmel_trng_resume,
123};
124#endif /* CONFIG_PM */
125
126static struct platform_driver atmel_trng_driver = {
127 .probe = atmel_trng_probe,
bcd2982a 128 .remove = atmel_trng_remove,
677d3e2f
PK
129 .driver = {
130 .name = "atmel-trng",
131 .owner = THIS_MODULE,
132#ifdef CONFIG_PM
133 .pm = &atmel_trng_pm_ops,
134#endif /* CONFIG_PM */
135 },
136};
137
b21cb324 138module_platform_driver(atmel_trng_driver);
677d3e2f
PK
139
140MODULE_LICENSE("GPL");
141MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
142MODULE_DESCRIPTION("Atmel true random number generator driver");
This page took 0.452426 seconds and 5 git commands to generate.