xref: /openbmc/linux/drivers/char/hw_random/atmel-rng.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1  /*
2   * Copyright (c) 2011 Peter Korsgaard <jacmet@sunsite.dk>
3   *
4   * This file is licensed under  the terms of the GNU General Public
5   * License version 2. This program is licensed "as is" without any
6   * warranty of any kind, whether express or implied.
7   */
8  
9  #include <linux/kernel.h>
10  #include <linux/module.h>
11  #include <linux/mod_devicetable.h>
12  #include <linux/slab.h>
13  #include <linux/err.h>
14  #include <linux/clk.h>
15  #include <linux/io.h>
16  #include <linux/iopoll.h>
17  #include <linux/hw_random.h>
18  #include <linux/of.h>
19  #include <linux/platform_device.h>
20  #include <linux/pm_runtime.h>
21  
22  #define TRNG_CR		0x00
23  #define TRNG_MR		0x04
24  #define TRNG_ISR	0x1c
25  #define TRNG_ISR_DATRDY	BIT(0)
26  #define TRNG_ODATA	0x50
27  
28  #define TRNG_KEY	0x524e4700 /* RNG */
29  
30  #define TRNG_HALFR	BIT(0) /* generate RN every 168 cycles */
31  
32  struct atmel_trng_data {
33  	bool has_half_rate;
34  };
35  
36  struct atmel_trng {
37  	struct clk *clk;
38  	void __iomem *base;
39  	struct hwrng rng;
40  	bool has_half_rate;
41  };
42  
atmel_trng_wait_ready(struct atmel_trng * trng,bool wait)43  static bool atmel_trng_wait_ready(struct atmel_trng *trng, bool wait)
44  {
45  	int ready;
46  
47  	ready = readl(trng->base + TRNG_ISR) & TRNG_ISR_DATRDY;
48  	if (!ready && wait)
49  		readl_poll_timeout(trng->base + TRNG_ISR, ready,
50  				   ready & TRNG_ISR_DATRDY, 1000, 20000);
51  
52  	return !!ready;
53  }
54  
atmel_trng_read(struct hwrng * rng,void * buf,size_t max,bool wait)55  static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
56  			   bool wait)
57  {
58  	struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
59  	u32 *data = buf;
60  	int ret;
61  
62  	ret = pm_runtime_get_sync((struct device *)trng->rng.priv);
63  	if (ret < 0) {
64  		pm_runtime_put_sync((struct device *)trng->rng.priv);
65  		return ret;
66  	}
67  
68  	ret = atmel_trng_wait_ready(trng, wait);
69  	if (!ret)
70  		goto out;
71  
72  	*data = readl(trng->base + TRNG_ODATA);
73  	/*
74  	 * ensure data ready is only set again AFTER the next data word is ready
75  	 * in case it got set between checking ISR and reading ODATA, so we
76  	 * don't risk re-reading the same word
77  	 */
78  	readl(trng->base + TRNG_ISR);
79  	ret = 4;
80  
81  out:
82  	pm_runtime_mark_last_busy((struct device *)trng->rng.priv);
83  	pm_runtime_put_sync_autosuspend((struct device *)trng->rng.priv);
84  	return ret;
85  }
86  
atmel_trng_init(struct atmel_trng * trng)87  static int atmel_trng_init(struct atmel_trng *trng)
88  {
89  	unsigned long rate;
90  	int ret;
91  
92  	ret = clk_prepare_enable(trng->clk);
93  	if (ret)
94  		return ret;
95  
96  	if (trng->has_half_rate) {
97  		rate = clk_get_rate(trng->clk);
98  
99  		/* if peripheral clk is above 100MHz, set HALFR */
100  		if (rate > 100000000)
101  			writel(TRNG_HALFR, trng->base + TRNG_MR);
102  	}
103  
104  	writel(TRNG_KEY | 1, trng->base + TRNG_CR);
105  
106  	return 0;
107  }
108  
atmel_trng_cleanup(struct atmel_trng * trng)109  static void atmel_trng_cleanup(struct atmel_trng *trng)
110  {
111  	writel(TRNG_KEY, trng->base + TRNG_CR);
112  	clk_disable_unprepare(trng->clk);
113  }
114  
atmel_trng_probe(struct platform_device * pdev)115  static int atmel_trng_probe(struct platform_device *pdev)
116  {
117  	struct atmel_trng *trng;
118  	const struct atmel_trng_data *data;
119  	int ret;
120  
121  	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
122  	if (!trng)
123  		return -ENOMEM;
124  
125  	trng->base = devm_platform_ioremap_resource(pdev, 0);
126  	if (IS_ERR(trng->base))
127  		return PTR_ERR(trng->base);
128  
129  	trng->clk = devm_clk_get(&pdev->dev, NULL);
130  	if (IS_ERR(trng->clk))
131  		return PTR_ERR(trng->clk);
132  	data = of_device_get_match_data(&pdev->dev);
133  	if (!data)
134  		return -ENODEV;
135  
136  	trng->has_half_rate = data->has_half_rate;
137  	trng->rng.name = pdev->name;
138  	trng->rng.read = atmel_trng_read;
139  	trng->rng.priv = (unsigned long)&pdev->dev;
140  	platform_set_drvdata(pdev, trng);
141  
142  #ifndef CONFIG_PM
143  	ret = atmel_trng_init(trng);
144  	if (ret)
145  		return ret;
146  #endif
147  
148  	pm_runtime_set_autosuspend_delay(&pdev->dev, 100);
149  	pm_runtime_use_autosuspend(&pdev->dev);
150  	pm_runtime_enable(&pdev->dev);
151  
152  	ret = devm_hwrng_register(&pdev->dev, &trng->rng);
153  	if (ret) {
154  		pm_runtime_disable(&pdev->dev);
155  		pm_runtime_set_suspended(&pdev->dev);
156  #ifndef CONFIG_PM
157  		atmel_trng_cleanup(trng);
158  #endif
159  	}
160  
161  	return ret;
162  }
163  
atmel_trng_remove(struct platform_device * pdev)164  static int atmel_trng_remove(struct platform_device *pdev)
165  {
166  	struct atmel_trng *trng = platform_get_drvdata(pdev);
167  
168  	atmel_trng_cleanup(trng);
169  	pm_runtime_disable(&pdev->dev);
170  	pm_runtime_set_suspended(&pdev->dev);
171  
172  	return 0;
173  }
174  
atmel_trng_runtime_suspend(struct device * dev)175  static int __maybe_unused atmel_trng_runtime_suspend(struct device *dev)
176  {
177  	struct atmel_trng *trng = dev_get_drvdata(dev);
178  
179  	atmel_trng_cleanup(trng);
180  
181  	return 0;
182  }
183  
atmel_trng_runtime_resume(struct device * dev)184  static int __maybe_unused atmel_trng_runtime_resume(struct device *dev)
185  {
186  	struct atmel_trng *trng = dev_get_drvdata(dev);
187  
188  	return atmel_trng_init(trng);
189  }
190  
191  static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {
192  	SET_RUNTIME_PM_OPS(atmel_trng_runtime_suspend,
193  			   atmel_trng_runtime_resume, NULL)
194  	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
195  				pm_runtime_force_resume)
196  };
197  
198  static const struct atmel_trng_data at91sam9g45_config = {
199  	.has_half_rate = false,
200  };
201  
202  static const struct atmel_trng_data sam9x60_config = {
203  	.has_half_rate = true,
204  };
205  
206  static const struct of_device_id atmel_trng_dt_ids[] = {
207  	{
208  		.compatible = "atmel,at91sam9g45-trng",
209  		.data = &at91sam9g45_config,
210  	}, {
211  		.compatible = "microchip,sam9x60-trng",
212  		.data = &sam9x60_config,
213  	}, {
214  		/* sentinel */
215  	}
216  };
217  MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
218  
219  static struct platform_driver atmel_trng_driver = {
220  	.probe		= atmel_trng_probe,
221  	.remove		= atmel_trng_remove,
222  	.driver		= {
223  		.name	= "atmel-trng",
224  		.pm	= pm_ptr(&atmel_trng_pm_ops),
225  		.of_match_table = atmel_trng_dt_ids,
226  	},
227  };
228  
229  module_platform_driver(atmel_trng_driver);
230  
231  MODULE_LICENSE("GPL");
232  MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
233  MODULE_DESCRIPTION("Atmel true random number generator driver");
234