blob: b03a4ec4d8406531351c4213c7e7812421e2e793 [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>
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -070025#include <mach/msm_iomap.h>
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -070026#include <mach/socinfo.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027
28#define DRIVER_NAME "msm_rng"
29
30/* Device specific register offsets */
31#define PRNG_DATA_OUT_OFFSET 0x0000
32#define PRNG_STATUS_OFFSET 0x0004
33#define PRNG_LFSR_CFG_OFFSET 0x0100
34#define PRNG_CONFIG_OFFSET 0x0104
35
36/* Device specific register masks and config values */
37#define PRNG_LFSR_CFG_MASK 0xFFFF0000
38#define PRNG_LFSR_CFG_CLOCKS 0x0000DDDD
39#define PRNG_CONFIG_MASK 0xFFFFFFFD
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -070040#define PRNG_HW_ENABLE 0x00000002
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070041
42#define MAX_HW_FIFO_DEPTH 16 /* FIFO is 16 words deep */
43#define MAX_HW_FIFO_SIZE (MAX_HW_FIFO_DEPTH * 4) /* FIFO is 32 bits wide */
44
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -070045
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070046struct msm_rng_device {
47 struct platform_device *pdev;
48 void __iomem *base;
49 struct clk *prng_clk;
50};
51
52static int msm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
53{
54 struct msm_rng_device *msm_rng_dev;
55 struct platform_device *pdev;
56 void __iomem *base;
57 size_t maxsize;
58 size_t currsize = 0;
59 unsigned long val;
60 unsigned long *retdata = data;
61 int ret;
62
63 msm_rng_dev = (struct msm_rng_device *)rng->priv;
64 pdev = msm_rng_dev->pdev;
65 base = msm_rng_dev->base;
66
67 /* calculate max size bytes to transfer back to caller */
68 maxsize = min_t(size_t, MAX_HW_FIFO_SIZE, max);
69
70 /* no room for word data */
71 if (maxsize < 4)
72 return 0;
73
74 /* enable PRNG clock */
75 ret = clk_enable(msm_rng_dev->prng_clk);
76 if (ret) {
77 dev_err(&pdev->dev, "failed to enable clock in callback\n");
78 return 0;
79 }
80
81 /* read random data from h/w */
82 do {
83 /* check status bit if data is available */
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -070084 if (!(readl_relaxed(base + PRNG_STATUS_OFFSET) & 0x00000001))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085 break; /* no data to read so just bail */
86
87 /* read FIFO */
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -070088 val = readl_relaxed(base + PRNG_DATA_OUT_OFFSET);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070089 if (!val)
90 break; /* no data to read so just bail */
91
92 /* write data back to callers pointer */
93 *(retdata++) = val;
94 currsize += 4;
95
96 /* make sure we stay on 32bit boundary */
97 if ((maxsize - currsize) < 4)
98 break;
99 } while (currsize < maxsize);
100
101 /* vote to turn off clock */
102 clk_disable(msm_rng_dev->prng_clk);
103
104 return currsize;
105}
106
107static struct hwrng msm_rng = {
108 .name = DRIVER_NAME,
109 .read = msm_rng_read,
110};
111
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700112static int __devinit msm_rng_enable_hw(struct msm_rng_device *msm_rng_dev)
113{
114 unsigned long val = 0;
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700115 unsigned long reg_val = 0;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700116 int ret = 0;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700117
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700118 /* Enable the PRNG CLK */
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700119 ret = clk_enable(msm_rng_dev->prng_clk);
120 if (ret) {
121 dev_err(&(msm_rng_dev->pdev)->dev,
122 "failed to enable clock in probe\n");
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700123 return -EPERM;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700124 }
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700125 val = readl_relaxed(msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET) &
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700126 PRNG_LFSR_CFG_MASK;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700127 val |= PRNG_LFSR_CFG_MASK;
128 writel_relaxed(val, msm_rng_dev->base + PRNG_LFSR_CFG_OFFSET);
129
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700130 /* The PRNG CONFIG register should be first written before reading */
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700131 mb();
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700132
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700133 /* Enable PRNG h/w only if it is NOT ON */
134 val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET) &
135 PRNG_HW_ENABLE;
136 /* PRNG H/W is not ON */
137 if (val != PRNG_HW_ENABLE) {
138 reg_val = readl_relaxed(msm_rng_dev->base + PRNG_CONFIG_OFFSET)
139 & PRNG_CONFIG_MASK;
140 reg_val |= PRNG_HW_ENABLE;
141 writel_relaxed(reg_val, msm_rng_dev->base + PRNG_CONFIG_OFFSET);
142
143 /* The PRNG clk should be disabled only after we enable the
144 * PRNG h/w by writing to the PRNG CONFIG register.
145 */
146 mb();
147 }
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700148
149 clk_disable(msm_rng_dev->prng_clk);
150
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700151 return 0;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700152}
153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154static int __devinit msm_rng_probe(struct platform_device *pdev)
155{
156 struct resource *res;
157 struct msm_rng_device *msm_rng_dev = NULL;
158 void __iomem *base = NULL;
159 int error = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700160
161 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
162 if (res == NULL) {
163 dev_err(&pdev->dev, "invalid address\n");
164 error = -EFAULT;
165 goto err_exit;
166 }
167
168 msm_rng_dev = kzalloc(sizeof(msm_rng_dev), GFP_KERNEL);
169 if (!msm_rng_dev) {
170 dev_err(&pdev->dev, "cannot allocate memory\n");
171 error = -ENOMEM;
172 goto err_exit;
173 }
174
175 base = ioremap(res->start, resource_size(res));
176 if (!base) {
177 dev_err(&pdev->dev, "ioremap failed\n");
178 error = -ENOMEM;
179 goto err_iomap;
180 }
181 msm_rng_dev->base = base;
182
183 /* create a handle for clock control */
Matt Wagantallc1205292011-08-11 17:19:31 -0700184 msm_rng_dev->prng_clk = clk_get(&pdev->dev, "core_clk");
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700185 if (IS_ERR(msm_rng_dev->prng_clk)) {
186 dev_err(&pdev->dev, "failed to register clock source\n");
187 error = -EPERM;
188 goto err_clk_get;
189 }
190
191 /* save away pdev and register driver data */
192 msm_rng_dev->pdev = pdev;
193 platform_set_drvdata(pdev, msm_rng_dev);
194
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700195 /* Enable rng h/w */
Ramesh Masavarapu2f963db2011-10-20 15:33:50 -0700196 error = msm_rng_enable_hw(msm_rng_dev);
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700197
198 if (error)
199 goto rollback_clk;
200
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700201 /* register with hwrng framework */
202 msm_rng.priv = (unsigned long) msm_rng_dev;
203 error = hwrng_register(&msm_rng);
204 if (error) {
205 dev_err(&pdev->dev, "failed to register hwrng\n");
206 error = -EPERM;
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700207 goto rollback_clk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208 }
209
210 return 0;
211
Ramesh Masavarapu5ad37392011-10-10 10:44:10 -0700212rollback_clk:
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 clk_put(msm_rng_dev->prng_clk);
214err_clk_get:
215 iounmap(msm_rng_dev->base);
216err_iomap:
217 kfree(msm_rng_dev);
218err_exit:
219 return error;
220}
221
222static int __devexit msm_rng_remove(struct platform_device *pdev)
223{
224 struct msm_rng_device *msm_rng_dev = platform_get_drvdata(pdev);
225
226 hwrng_unregister(&msm_rng);
227 clk_put(msm_rng_dev->prng_clk);
228 iounmap(msm_rng_dev->base);
229 platform_set_drvdata(pdev, NULL);
230 kfree(msm_rng_dev);
231 return 0;
232}
233
234static struct platform_driver rng_driver = {
235 .probe = msm_rng_probe,
236 .remove = __devexit_p(msm_rng_remove),
237 .driver = {
238 .name = DRIVER_NAME,
239 .owner = THIS_MODULE,
240 }
241};
242
243static int __init msm_rng_init(void)
244{
245 return platform_driver_register(&rng_driver);
246}
247
248module_init(msm_rng_init);
249
250static void __exit msm_rng_exit(void)
251{
252 platform_driver_unregister(&rng_driver);
253}
254
255module_exit(msm_rng_exit);
256
257MODULE_AUTHOR("Code Aurora Forum");
258MODULE_DESCRIPTION("Qualcomm MSM Random Number Driver");
259MODULE_LICENSE("GPL v2");