blob: 66fcaf23f41ac07d7eb922799b99b6ea134e93f8 [file] [log] [blame]
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001/*
2 * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/device.h>
18#include <linux/platform_device.h>
19#include <linux/hw_random.h>
20#include <linux/clk.h>
21#include <linux/slab.h>
22#include <linux/io.h>
23#include <linux/err.h>
24#include <linux/types.h>
25
26#define DRIVER_NAME "msm_rng"
27
28/* Device specific register offsets */
29#define PRNG_DATA_OUT_OFFSET 0x0000
30#define PRNG_STATUS_OFFSET 0x0004
31#define PRNG_LFSR_CFG_OFFSET 0x0100
32#define PRNG_CONFIG_OFFSET 0x0104
33
34/* Device specific register masks and config values */
35#define PRNG_LFSR_CFG_MASK 0xFFFF0000
36#define PRNG_LFSR_CFG_CLOCKS 0x0000DDDD
37#define PRNG_CONFIG_MASK 0xFFFFFFFD
38#define PRNG_CONFIG_ENABLE 0x00000002
39
40#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
41#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
42
43struct msm_rng_device {
44 struct platform_device *pdev;
45 void __iomem *base;
46 struct clk *prng_clk;
47};
48
49static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
50{
51 struct msm_rng_device *msm_rng_dev;
52 struct platform_device *pdev;
53 void __iomem *base;
54 size_t maxsize;
55 size_t currsize = 0;
56 unsigned long val;
57 unsigned long *retdata = data;
58 int ret;
59
60 msm_rng_dev = (struct msm_rng_device *)rng->priv;
61 pdev = msm_rng_dev->pdev;
62 base = msm_rng_dev->base;
63
64 /* calculate max size bytes to transfer back to caller */
65 maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
66
67 /* no room for word data */
68 if (maxsize < 4)
69 return 0;
70
71 /* enable PRNG clock */
72 ret = clk_enable(msm_rng_dev->prng_clk);
73 if (ret) {
74 dev_err(&pdev->dev, "failed to enable clock in callback\n");
75 return 0;
76 }
77
78 /* read random data from h/w */
79 do {
80 /* check status bit if data is available */
81 if (!(readl(base + PRNG_STATUS_OFFSET) & 0x00000001))
82 break; /* no data to read so just bail */
83
84 /* read FIFO */
85 val = readl(base + PRNG_DATA_OUT_OFFSET);
86 if (!val)
87 break; /* no data to read so just bail */
88
89 /* write data back to callers pointer */
90 *(retdata++) = val;
91 currsize += 4;
92
93 /* make sure we stay on 32bit boundary */
94 if ((maxsize - currsize) < 4)
95 break;
96 } while (currsize < maxsize);
97
98 /* vote to turn off clock */
99 clk_disable(msm_rng_dev->prng_clk);
100
101 return currsize;
102}
103
104static struct hwrng msm_rng = {
105 .name = DRIVER_NAME,
106 .read = msm_rng_read,
107};
108
109static int __devinit msm_rng_probe(struct platform_device *pdev)
110{
111 struct resource *res;
112 struct msm_rng_device *msm_rng_dev = NULL;
113 void __iomem *base = NULL;
114 int error = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700115
116 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
117 if (res == NULL) {
118 dev_err(&pdev->dev, "invalid address\n");
119 error = -EFAULT;
120 goto err_exit;
121 }
122
123 msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
124 if (!msm_rng_dev) {
125 dev_err(&pdev->dev, "cannot allocate memory\n");
126 error = -ENOMEM;
127 goto err_exit;
128 }
129
130 base = ioremap(res->start, resource_size(res));
131 if (!base) {
132 dev_err(&pdev->dev, "ioremap failed\n");
133 error = -ENOMEM;
134 goto err_iomap;
135 }
136 msm_rng_dev->base = base;
137
138 /* create a handle for clock control */
139 msm_rng_dev->prng_clk = clk_get(NULL, "prng_clk");
140 if (IS_ERR(msm_rng_dev->prng_clk)) {
141 dev_err(&pdev->dev, "failed to register clock source\n");
142 error = -EPERM;
143 goto err_clk_get;
144 }
145
146 /* save away pdev and register driver data */
147 msm_rng_dev->pdev = pdev;
148 platform_set_drvdata(pdev, msm_rng_dev);
149
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 /* register with hwrng framework */
151 msm_rng.priv = (unsigned long) msm_rng_dev;
152 error = hwrng_register(&msm_rng);
153 if (error) {
154 dev_err(&pdev->dev, "failed to register hwrng\n");
155 error = -EPERM;
156 goto err_hw_register;
157 }
158
159 return 0;
160
161err_hw_register:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700162 clk_put(msm_rng_dev->prng_clk);
163err_clk_get:
164 iounmap(msm_rng_dev->base);
165err_iomap:
166 kfree(msm_rng_dev);
167err_exit:
168 return error;
169}
170
171static int __devexit msm_rng_remove(struct platform_device *pdev)
172{
173 struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
174
175 hwrng_unregister(&msm_rng);
176 clk_put(msm_rng_dev->prng_clk);
177 iounmap(msm_rng_dev->base);
178 platform_set_drvdata(pdev, NULL);
179 kfree(msm_rng_dev);
180 return 0;
181}
182
183static struct platform_driver rng_driver = {
184 .probe = msm_rng_probe,
185 .remove = __devexit_p(msm_rng_remove),
186 .driver = {
187 .name = DRIVER_NAME,
188 .owner = THIS_MODULE,
189 }
190};
191
192static int __init msm_rng_init(void)
193{
194 return platform_driver_register(&rng_driver);
195}
196
197module_init(msm_rng_init);
198
199static void __exit msm_rng_exit(void)
200{
201 platform_driver_unregister(&rng_driver);
202}
203
204module_exit(msm_rng_exit);
205
206MODULE_AUTHOR("Code Aurora Forum");
207MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
208MODULE_LICENSE("GPL v2");