Commit | Line | Data |
---|---|---|
1c6b7c21 PR |
1 | /* |
2 | * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family | |
3 | * | |
4 | * Copyright (C) 2009 Nokia Corporation | |
5 | * Author: Juha Yrjola <juha.yrjola@solidboot.com> | |
6 | * | |
149ed3d4 | 7 | * Copyright (C) 2013 Pali Rohár <pali@kernel.org> |
1c6b7c21 PR |
8 | * |
9 | * This file is licensed under the terms of the GNU General Public | |
10 | * License version 2. This program is licensed "as is" without any | |
11 | * warranty of any kind, whether express or implied. | |
12 | */ | |
13 | ||
1c6b7c21 PR |
14 | #include <linux/module.h> |
15 | #include <linux/init.h> | |
16 | #include <linux/random.h> | |
17 | #include <linux/hw_random.h> | |
4c13ac1c | 18 | #include <linux/workqueue.h> |
1c6b7c21 PR |
19 | #include <linux/clk.h> |
20 | #include <linux/err.h> | |
ba02b352 | 21 | #include <linux/io.h> |
0c0ef9ea TL |
22 | #include <linux/of.h> |
23 | #include <linux/of_device.h> | |
1c6b7c21 | 24 | #include <linux/platform_device.h> |
8d9d4bdc | 25 | #include <linux/pm_runtime.h> |
1c6b7c21 PR |
26 | |
27 | #define RNG_RESET 0x01 | |
28 | #define RNG_GEN_PRNG_HW_INIT 0x02 | |
29 | #define RNG_GEN_HW 0x08 | |
30 | ||
b8d665ae TL |
31 | struct omap_rom_rng { |
32 | struct clk *clk; | |
33 | struct device *dev; | |
34 | struct hwrng ops; | |
8d9d4bdc | 35 | u32 (*rom_rng_call)(u32 ptr, u32 count, u32 flag); |
b8d665ae TL |
36 | }; |
37 | ||
8d9d4bdc | 38 | static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w) |
1c6b7c21 | 39 | { |
8d9d4bdc TL |
40 | struct omap_rom_rng *ddata; |
41 | u32 ptr; | |
1c6b7c21 PR |
42 | int r; |
43 | ||
8d9d4bdc TL |
44 | ddata = (struct omap_rom_rng *)rng->priv; |
45 | ||
46 | r = pm_runtime_get_sync(ddata->dev); | |
47 | if (r < 0) { | |
48 | pm_runtime_put_noidle(ddata->dev); | |
49 | ||
50 | return r; | |
1c6b7c21 | 51 | } |
8d9d4bdc TL |
52 | |
53 | ptr = virt_to_phys(data); | |
54 | r = ddata->rom_rng_call(ptr, 4, RNG_GEN_HW); | |
55 | if (r != 0) | |
56 | r = -EINVAL; | |
57 | else | |
58 | r = 4; | |
59 | ||
60 | pm_runtime_mark_last_busy(ddata->dev); | |
61 | pm_runtime_put_autosuspend(ddata->dev); | |
62 | ||
63 | return r; | |
1c6b7c21 PR |
64 | } |
65 | ||
fbbfb3f8 | 66 | static int __maybe_unused omap_rom_rng_runtime_suspend(struct device *dev) |
1c6b7c21 | 67 | { |
8d9d4bdc TL |
68 | struct omap_rom_rng *ddata; |
69 | int r; | |
1c6b7c21 | 70 | |
8d9d4bdc | 71 | ddata = dev_get_drvdata(dev); |
1c6b7c21 | 72 | |
8d9d4bdc | 73 | r = ddata->rom_rng_call(0, 0, RNG_RESET); |
1c6b7c21 | 74 | if (r != 0) |
8d9d4bdc TL |
75 | dev_err(dev, "reset failed: %d\n", r); |
76 | ||
77 | clk_disable_unprepare(ddata->clk); | |
78 | ||
1c6b7c21 PR |
79 | return 0; |
80 | } | |
81 | ||
fbbfb3f8 | 82 | static int __maybe_unused omap_rom_rng_runtime_resume(struct device *dev) |
1c6b7c21 | 83 | { |
8d9d4bdc | 84 | struct omap_rom_rng *ddata; |
1c6b7c21 PR |
85 | int r; |
86 | ||
8d9d4bdc TL |
87 | ddata = dev_get_drvdata(dev); |
88 | ||
89 | r = clk_prepare_enable(ddata->clk); | |
1c6b7c21 PR |
90 | if (r < 0) |
91 | return r; | |
8d9d4bdc TL |
92 | |
93 | r = ddata->rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT); | |
94 | if (r != 0) { | |
95 | clk_disable(ddata->clk); | |
96 | dev_err(dev, "HW init failed: %d\n", r); | |
97 | ||
98 | return -EIO; | |
99 | } | |
100 | ||
101 | return 0; | |
1c6b7c21 PR |
102 | } |
103 | ||
bac5c49e TL |
104 | static void omap_rom_rng_finish(void *data) |
105 | { | |
106 | struct omap_rom_rng *ddata = data; | |
107 | ||
108 | pm_runtime_dont_use_autosuspend(ddata->dev); | |
109 | pm_runtime_disable(ddata->dev); | |
110 | } | |
111 | ||
1c6b7c21 PR |
112 | static int omap3_rom_rng_probe(struct platform_device *pdev) |
113 | { | |
b8d665ae | 114 | struct omap_rom_rng *ddata; |
986130bf AY |
115 | int ret = 0; |
116 | ||
b8d665ae TL |
117 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); |
118 | if (!ddata) | |
119 | return -ENOMEM; | |
120 | ||
121 | ddata->dev = &pdev->dev; | |
122 | ddata->ops.priv = (unsigned long)ddata; | |
123 | ddata->ops.name = "omap3-rom"; | |
124 | ddata->ops.read = of_device_get_match_data(&pdev->dev); | |
125 | ddata->ops.quality = 900; | |
126 | if (!ddata->ops.read) { | |
0c0ef9ea TL |
127 | dev_err(&pdev->dev, "missing rom code handler\n"); |
128 | ||
129 | return -ENODEV; | |
130 | } | |
b8d665ae | 131 | dev_set_drvdata(ddata->dev, ddata); |
1c6b7c21 | 132 | |
8d9d4bdc TL |
133 | ddata->rom_rng_call = pdev->dev.platform_data; |
134 | if (!ddata->rom_rng_call) { | |
b8d665ae | 135 | dev_err(ddata->dev, "rom_rng_call is NULL\n"); |
1c6b7c21 PR |
136 | return -EINVAL; |
137 | } | |
138 | ||
b8d665ae TL |
139 | ddata->clk = devm_clk_get(ddata->dev, "ick"); |
140 | if (IS_ERR(ddata->clk)) { | |
141 | dev_err(ddata->dev, "unable to get RNG clock\n"); | |
142 | return PTR_ERR(ddata->clk); | |
1c6b7c21 PR |
143 | } |
144 | ||
bac5c49e TL |
145 | pm_runtime_enable(&pdev->dev); |
146 | pm_runtime_set_autosuspend_delay(&pdev->dev, 500); | |
147 | pm_runtime_use_autosuspend(&pdev->dev); | |
b8d665ae | 148 | |
bac5c49e TL |
149 | ret = devm_add_action_or_reset(ddata->dev, omap_rom_rng_finish, |
150 | ddata); | |
151 | if (ret) | |
152 | return ret; | |
8d9d4bdc | 153 | |
bac5c49e | 154 | return devm_hwrng_register(ddata->dev, &ddata->ops); |
1c6b7c21 PR |
155 | } |
156 | ||
0c0ef9ea TL |
157 | static const struct of_device_id omap_rom_rng_match[] = { |
158 | { .compatible = "nokia,n900-rom-rng", .data = omap3_rom_rng_read, }, | |
159 | { /* sentinel */ }, | |
160 | }; | |
161 | MODULE_DEVICE_TABLE(of, omap_rom_rng_match); | |
162 | ||
8d9d4bdc TL |
163 | static const struct dev_pm_ops omap_rom_rng_pm_ops = { |
164 | SET_SYSTEM_SLEEP_PM_OPS(omap_rom_rng_runtime_suspend, | |
165 | omap_rom_rng_runtime_resume) | |
166 | }; | |
167 | ||
1c6b7c21 PR |
168 | static struct platform_driver omap3_rom_rng_driver = { |
169 | .driver = { | |
170 | .name = "omap3-rom-rng", | |
0c0ef9ea | 171 | .of_match_table = omap_rom_rng_match, |
8d9d4bdc | 172 | .pm = &omap_rom_rng_pm_ops, |
1c6b7c21 PR |
173 | }, |
174 | .probe = omap3_rom_rng_probe, | |
1c6b7c21 PR |
175 | }; |
176 | ||
177 | module_platform_driver(omap3_rom_rng_driver); | |
178 | ||
179 | MODULE_ALIAS("platform:omap3-rom-rng"); | |
180 | MODULE_AUTHOR("Juha Yrjola"); | |
149ed3d4 | 181 | MODULE_AUTHOR("Pali Rohár <pali@kernel.org>"); |
1c6b7c21 | 182 | MODULE_LICENSE("GPL"); |