1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * RNG driver for Freescale RNGA |
4 | * |
5 | * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. |
6 | * Author: Alan Carvalho de Assis <acassis@gmail.com> |
7 | */ |
8 | |
9 | /* |
10 | * |
11 | * This driver is based on other RNG drivers. |
12 | */ |
13 | |
14 | #include <linux/clk.h> |
15 | #include <linux/delay.h> |
16 | #include <linux/hw_random.h> |
17 | #include <linux/io.h> |
18 | #include <linux/module.h> |
19 | #include <linux/of.h> |
20 | #include <linux/platform_device.h> |
21 | |
22 | /* RNGA Registers */ |
23 | #define RNGA_CONTROL 0x00 |
24 | #define RNGA_STATUS 0x04 |
25 | #define RNGA_ENTROPY 0x08 |
26 | #define RNGA_OUTPUT_FIFO 0x0c |
27 | #define RNGA_MODE 0x10 |
28 | #define RNGA_VERIFICATION_CONTROL 0x14 |
29 | #define RNGA_OSC_CONTROL_COUNTER 0x18 |
30 | #define RNGA_OSC1_COUNTER 0x1c |
31 | #define RNGA_OSC2_COUNTER 0x20 |
32 | #define RNGA_OSC_COUNTER_STATUS 0x24 |
33 | |
34 | /* RNGA Registers Range */ |
35 | #define RNG_ADDR_RANGE 0x28 |
36 | |
37 | /* RNGA Control Register */ |
38 | #define RNGA_CONTROL_SLEEP 0x00000010 |
39 | #define RNGA_CONTROL_CLEAR_INT 0x00000008 |
40 | #define RNGA_CONTROL_MASK_INTS 0x00000004 |
41 | #define RNGA_CONTROL_HIGH_ASSURANCE 0x00000002 |
42 | #define RNGA_CONTROL_GO 0x00000001 |
43 | |
44 | #define RNGA_STATUS_LEVEL_MASK 0x0000ff00 |
45 | |
46 | /* RNGA Status Register */ |
47 | #define RNGA_STATUS_OSC_DEAD 0x80000000 |
48 | #define RNGA_STATUS_SLEEP 0x00000010 |
49 | #define RNGA_STATUS_ERROR_INT 0x00000008 |
50 | #define RNGA_STATUS_FIFO_UNDERFLOW 0x00000004 |
51 | #define RNGA_STATUS_LAST_READ_STATUS 0x00000002 |
52 | #define RNGA_STATUS_SECURITY_VIOLATION 0x00000001 |
53 | |
54 | struct mxc_rng { |
55 | struct device *dev; |
56 | struct hwrng rng; |
57 | void __iomem *mem; |
58 | struct clk *clk; |
59 | }; |
60 | |
61 | static int mxc_rnga_data_present(struct hwrng *rng, int wait) |
62 | { |
63 | int i; |
64 | struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); |
65 | |
66 | for (i = 0; i < 20; i++) { |
67 | /* how many random numbers are in FIFO? [0-16] */ |
68 | int level = (__raw_readl(addr: mxc_rng->mem + RNGA_STATUS) & |
69 | RNGA_STATUS_LEVEL_MASK) >> 8; |
70 | if (level || !wait) |
71 | return !!level; |
72 | udelay(10); |
73 | } |
74 | return 0; |
75 | } |
76 | |
77 | static int mxc_rnga_data_read(struct hwrng *rng, u32 * data) |
78 | { |
79 | int err; |
80 | u32 ctrl; |
81 | struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); |
82 | |
83 | /* retrieve a random number from FIFO */ |
84 | *data = __raw_readl(addr: mxc_rng->mem + RNGA_OUTPUT_FIFO); |
85 | |
86 | /* some error while reading this random number? */ |
87 | err = __raw_readl(addr: mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT; |
88 | |
89 | /* if error: clear error interrupt, but doesn't return random number */ |
90 | if (err) { |
91 | dev_dbg(mxc_rng->dev, "Error while reading random number!\n" ); |
92 | ctrl = __raw_readl(addr: mxc_rng->mem + RNGA_CONTROL); |
93 | __raw_writel(val: ctrl | RNGA_CONTROL_CLEAR_INT, |
94 | addr: mxc_rng->mem + RNGA_CONTROL); |
95 | return 0; |
96 | } else |
97 | return 4; |
98 | } |
99 | |
100 | static int mxc_rnga_init(struct hwrng *rng) |
101 | { |
102 | u32 ctrl, osc; |
103 | struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); |
104 | |
105 | /* wake up */ |
106 | ctrl = __raw_readl(addr: mxc_rng->mem + RNGA_CONTROL); |
107 | __raw_writel(val: ctrl & ~RNGA_CONTROL_SLEEP, addr: mxc_rng->mem + RNGA_CONTROL); |
108 | |
109 | /* verify if oscillator is working */ |
110 | osc = __raw_readl(addr: mxc_rng->mem + RNGA_STATUS); |
111 | if (osc & RNGA_STATUS_OSC_DEAD) { |
112 | dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n" ); |
113 | return -ENODEV; |
114 | } |
115 | |
116 | /* go running */ |
117 | ctrl = __raw_readl(addr: mxc_rng->mem + RNGA_CONTROL); |
118 | __raw_writel(val: ctrl | RNGA_CONTROL_GO, addr: mxc_rng->mem + RNGA_CONTROL); |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static void mxc_rnga_cleanup(struct hwrng *rng) |
124 | { |
125 | u32 ctrl; |
126 | struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng); |
127 | |
128 | ctrl = __raw_readl(addr: mxc_rng->mem + RNGA_CONTROL); |
129 | |
130 | /* stop rnga */ |
131 | __raw_writel(val: ctrl & ~RNGA_CONTROL_GO, addr: mxc_rng->mem + RNGA_CONTROL); |
132 | } |
133 | |
134 | static int __init mxc_rnga_probe(struct platform_device *pdev) |
135 | { |
136 | int err; |
137 | struct mxc_rng *mxc_rng; |
138 | |
139 | mxc_rng = devm_kzalloc(dev: &pdev->dev, size: sizeof(*mxc_rng), GFP_KERNEL); |
140 | if (!mxc_rng) |
141 | return -ENOMEM; |
142 | |
143 | mxc_rng->dev = &pdev->dev; |
144 | mxc_rng->rng.name = "mxc-rnga" ; |
145 | mxc_rng->rng.init = mxc_rnga_init; |
146 | mxc_rng->rng.cleanup = mxc_rnga_cleanup; |
147 | mxc_rng->rng.data_present = mxc_rnga_data_present; |
148 | mxc_rng->rng.data_read = mxc_rnga_data_read; |
149 | |
150 | mxc_rng->clk = devm_clk_get(dev: &pdev->dev, NULL); |
151 | if (IS_ERR(ptr: mxc_rng->clk)) { |
152 | dev_err(&pdev->dev, "Could not get rng_clk!\n" ); |
153 | return PTR_ERR(ptr: mxc_rng->clk); |
154 | } |
155 | |
156 | err = clk_prepare_enable(clk: mxc_rng->clk); |
157 | if (err) |
158 | return err; |
159 | |
160 | mxc_rng->mem = devm_platform_ioremap_resource(pdev, index: 0); |
161 | if (IS_ERR(ptr: mxc_rng->mem)) { |
162 | err = PTR_ERR(ptr: mxc_rng->mem); |
163 | goto err_ioremap; |
164 | } |
165 | |
166 | err = hwrng_register(rng: &mxc_rng->rng); |
167 | if (err) { |
168 | dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n" , err); |
169 | goto err_ioremap; |
170 | } |
171 | |
172 | return 0; |
173 | |
174 | err_ioremap: |
175 | clk_disable_unprepare(clk: mxc_rng->clk); |
176 | return err; |
177 | } |
178 | |
179 | static int __exit mxc_rnga_remove(struct platform_device *pdev) |
180 | { |
181 | struct mxc_rng *mxc_rng = platform_get_drvdata(pdev); |
182 | |
183 | hwrng_unregister(rng: &mxc_rng->rng); |
184 | |
185 | clk_disable_unprepare(clk: mxc_rng->clk); |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | static const struct of_device_id mxc_rnga_of_match[] = { |
191 | { .compatible = "fsl,imx21-rnga" , }, |
192 | { .compatible = "fsl,imx31-rnga" , }, |
193 | { /* sentinel */ }, |
194 | }; |
195 | MODULE_DEVICE_TABLE(of, mxc_rnga_of_match); |
196 | |
197 | static struct platform_driver mxc_rnga_driver = { |
198 | .driver = { |
199 | .name = "mxc_rnga" , |
200 | .of_match_table = mxc_rnga_of_match, |
201 | }, |
202 | .remove = __exit_p(mxc_rnga_remove), |
203 | }; |
204 | |
205 | module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe); |
206 | |
207 | MODULE_AUTHOR("Freescale Semiconductor, Inc." ); |
208 | MODULE_DESCRIPTION("H/W RNGA driver for i.MX" ); |
209 | MODULE_LICENSE("GPL" ); |
210 | |