Root/drivers/char/hw_random/omap-rng.c

1/*
2 * omap-rng.c - RNG driver for TI OMAP CPU family
3 *
4 * Author: Deepak Saxena <dsaxena@plexity.net>
5 *
6 * Copyright 2005 (c) MontaVista Software, Inc.
7 *
8 * Mostly based on original driver:
9 *
10 * Copyright (C) 2005 Nokia Corporation
11 * Author: Juha Yrjölä <juha.yrjola@nokia.com>
12 *
13 * This file is licensed under the terms of the GNU General Public
14 * License version 2. This program is licensed "as is" without any
15 * warranty of any kind, whether express or implied.
16 */
17
18#include <linux/module.h>
19#include <linux/init.h>
20#include <linux/random.h>
21#include <linux/err.h>
22#include <linux/platform_device.h>
23#include <linux/hw_random.h>
24#include <linux/delay.h>
25#include <linux/slab.h>
26#include <linux/pm_runtime.h>
27
28#include <asm/io.h>
29
30#define RNG_OUT_REG 0x00 /* Output register */
31#define RNG_STAT_REG 0x04 /* Status register
32                            [0] = STAT_BUSY */
33#define RNG_ALARM_REG 0x24 /* Alarm register
34                            [7:0] = ALARM_COUNTER */
35#define RNG_CONFIG_REG 0x28 /* Configuration register
36                            [11:6] = RESET_COUNT
37                            [5:3] = RING2_DELAY
38                            [2:0] = RING1_DELAY */
39#define RNG_REV_REG 0x3c /* Revision register
40                            [7:0] = REV_NB */
41#define RNG_MASK_REG 0x40 /* Mask and reset register
42                            [2] = IT_EN
43                            [1] = SOFTRESET
44                            [0] = AUTOIDLE */
45#define RNG_SYSSTATUS 0x44 /* System status
46                            [0] = RESETDONE */
47
48/**
49 * struct omap_rng_private_data - RNG IP block-specific data
50 * @base: virtual address of the beginning of the RNG IP block registers
51 * @mem_res: struct resource * for the IP block registers physical memory
52 */
53struct omap_rng_private_data {
54    void __iomem *base;
55    struct resource *mem_res;
56};
57
58static inline u32 omap_rng_read_reg(struct omap_rng_private_data *priv, int reg)
59{
60    return __raw_readl(priv->base + reg);
61}
62
63static inline void omap_rng_write_reg(struct omap_rng_private_data *priv,
64                      int reg, u32 val)
65{
66    __raw_writel(val, priv->base + reg);
67}
68
69static int omap_rng_data_present(struct hwrng *rng, int wait)
70{
71    struct omap_rng_private_data *priv;
72    int data, i;
73
74    priv = (struct omap_rng_private_data *)rng->priv;
75
76    for (i = 0; i < 20; i++) {
77        data = omap_rng_read_reg(priv, RNG_STAT_REG) ? 0 : 1;
78        if (data || !wait)
79            break;
80        /* RNG produces data fast enough (2+ MBit/sec, even
81         * during "rngtest" loads, that these delays don't
82         * seem to trigger. We *could* use the RNG IRQ, but
83         * that'd be higher overhead ... so why bother?
84         */
85        udelay(10);
86    }
87    return data;
88}
89
90static int omap_rng_data_read(struct hwrng *rng, u32 *data)
91{
92    struct omap_rng_private_data *priv;
93
94    priv = (struct omap_rng_private_data *)rng->priv;
95
96    *data = omap_rng_read_reg(priv, RNG_OUT_REG);
97
98    return sizeof(u32);
99}
100
101static struct hwrng omap_rng_ops = {
102    .name = "omap",
103    .data_present = omap_rng_data_present,
104    .data_read = omap_rng_data_read,
105};
106
107static int omap_rng_probe(struct platform_device *pdev)
108{
109    struct omap_rng_private_data *priv;
110    int ret;
111
112    priv = kzalloc(sizeof(struct omap_rng_private_data), GFP_KERNEL);
113    if (!priv) {
114        dev_err(&pdev->dev, "could not allocate memory\n");
115        return -ENOMEM;
116    };
117
118    omap_rng_ops.priv = (unsigned long)priv;
119    dev_set_drvdata(&pdev->dev, priv);
120
121    priv->mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
122    if (!priv->mem_res) {
123        ret = -ENOENT;
124        goto err_ioremap;
125    }
126
127    priv->base = devm_ioremap_resource(&pdev->dev, priv->mem_res);
128    if (IS_ERR(priv->base)) {
129        ret = PTR_ERR(priv->base);
130        goto err_ioremap;
131    }
132    dev_set_drvdata(&pdev->dev, priv);
133
134    pm_runtime_enable(&pdev->dev);
135    pm_runtime_get_sync(&pdev->dev);
136
137    ret = hwrng_register(&omap_rng_ops);
138    if (ret)
139        goto err_register;
140
141    dev_info(&pdev->dev, "OMAP Random Number Generator ver. %02x\n",
142         omap_rng_read_reg(priv, RNG_REV_REG));
143
144    omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
145
146    return 0;
147
148err_register:
149    priv->base = NULL;
150    pm_runtime_disable(&pdev->dev);
151err_ioremap:
152    kfree(priv);
153
154    return ret;
155}
156
157static int __exit omap_rng_remove(struct platform_device *pdev)
158{
159    struct omap_rng_private_data *priv = dev_get_drvdata(&pdev->dev);
160
161    hwrng_unregister(&omap_rng_ops);
162
163    omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
164
165    pm_runtime_put_sync(&pdev->dev);
166    pm_runtime_disable(&pdev->dev);
167
168    release_mem_region(priv->mem_res->start, resource_size(priv->mem_res));
169
170    kfree(priv);
171
172    return 0;
173}
174
175#ifdef CONFIG_PM_SLEEP
176
177static int omap_rng_suspend(struct device *dev)
178{
179    struct omap_rng_private_data *priv = dev_get_drvdata(dev);
180
181    omap_rng_write_reg(priv, RNG_MASK_REG, 0x0);
182    pm_runtime_put_sync(dev);
183
184    return 0;
185}
186
187static int omap_rng_resume(struct device *dev)
188{
189    struct omap_rng_private_data *priv = dev_get_drvdata(dev);
190
191    pm_runtime_get_sync(dev);
192    omap_rng_write_reg(priv, RNG_MASK_REG, 0x1);
193
194    return 0;
195}
196
197static SIMPLE_DEV_PM_OPS(omap_rng_pm, omap_rng_suspend, omap_rng_resume);
198#define OMAP_RNG_PM (&omap_rng_pm)
199
200#else
201
202#define OMAP_RNG_PM NULL
203
204#endif
205
206/* work with hotplug and coldplug */
207MODULE_ALIAS("platform:omap_rng");
208
209static struct platform_driver omap_rng_driver = {
210    .driver = {
211        .name = "omap_rng",
212        .owner = THIS_MODULE,
213        .pm = OMAP_RNG_PM,
214    },
215    .probe = omap_rng_probe,
216    .remove = __exit_p(omap_rng_remove),
217};
218
219static int __init omap_rng_init(void)
220{
221    return platform_driver_register(&omap_rng_driver);
222}
223
224static void __exit omap_rng_exit(void)
225{
226    platform_driver_unregister(&omap_rng_driver);
227}
228
229module_init(omap_rng_init);
230module_exit(omap_rng_exit);
231
232MODULE_AUTHOR("Deepak Saxena (and others)");
233MODULE_LICENSE("GPL");
234

Archive Download this file



interactive