Linux Audio

Check our new training course

Loading...
v6.8
  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
 32struct atmel_trng_data {
 33	bool has_half_rate;
 34};
 35
 36struct atmel_trng {
 37	struct clk *clk;
 38	void __iomem *base;
 39	struct hwrng rng;
 40	bool has_half_rate;
 41};
 42
 43static 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
 55static 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
 81out:
 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
 87static 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
109static 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
115static 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
164static void 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
173static int __maybe_unused atmel_trng_runtime_suspend(struct device *dev)
 
174{
175	struct atmel_trng *trng = dev_get_drvdata(dev);
176
177	atmel_trng_cleanup(trng);
178
179	return 0;
180}
181
182static int __maybe_unused atmel_trng_runtime_resume(struct device *dev)
183{
184	struct atmel_trng *trng = dev_get_drvdata(dev);
185
186	return atmel_trng_init(trng);
187}
188
189static const struct dev_pm_ops __maybe_unused atmel_trng_pm_ops = {
190	SET_RUNTIME_PM_OPS(atmel_trng_runtime_suspend,
191			   atmel_trng_runtime_resume, NULL)
192	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
193				pm_runtime_force_resume)
194};
195
196static const struct atmel_trng_data at91sam9g45_config = {
197	.has_half_rate = false,
198};
199
200static const struct atmel_trng_data sam9x60_config = {
201	.has_half_rate = true,
202};
 
203
204static const struct of_device_id atmel_trng_dt_ids[] = {
205	{
206		.compatible = "atmel,at91sam9g45-trng",
207		.data = &at91sam9g45_config,
208	}, {
209		.compatible = "microchip,sam9x60-trng",
210		.data = &sam9x60_config,
211	}, {
212		/* sentinel */
213	}
214};
215MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
216
217static struct platform_driver atmel_trng_driver = {
218	.probe		= atmel_trng_probe,
219	.remove_new	= atmel_trng_remove,
220	.driver		= {
221		.name	= "atmel-trng",
222		.pm	= pm_ptr(&atmel_trng_pm_ops),
 
 
223		.of_match_table = atmel_trng_dt_ids,
224	},
225};
226
227module_platform_driver(atmel_trng_driver);
228
229MODULE_LICENSE("GPL");
230MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
231MODULE_DESCRIPTION("Atmel true random number generator driver");
v4.6
  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/slab.h>
 12#include <linux/err.h>
 13#include <linux/clk.h>
 14#include <linux/io.h>
 
 15#include <linux/hw_random.h>
 
 16#include <linux/platform_device.h>
 
 17
 18#define TRNG_CR		0x00
 
 19#define TRNG_ISR	0x1c
 
 20#define TRNG_ODATA	0x50
 21
 22#define TRNG_KEY	0x524e4700 /* RNG */
 23
 
 
 
 
 
 
 24struct atmel_trng {
 25	struct clk *clk;
 26	void __iomem *base;
 27	struct hwrng rng;
 
 28};
 29
 
 
 
 
 
 
 
 
 
 
 
 
 30static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
 31			   bool wait)
 32{
 33	struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
 34	u32 *data = buf;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 35
 36	/* data ready? */
 37	if (readl(trng->base + TRNG_ISR) & 1) {
 38		*data = readl(trng->base + TRNG_ODATA);
 39		/*
 40		  ensure data ready is only set again AFTER the next data
 41		  word is ready in case it got set between checking ISR
 42		  and reading ODATA, so we don't risk re-reading the
 43		  same word
 44		*/
 45		readl(trng->base + TRNG_ISR);
 46		return 4;
 47	} else
 48		return 0;
 
 
 
 
 
 
 
 
 49}
 50
 51static int atmel_trng_probe(struct platform_device *pdev)
 52{
 53	struct atmel_trng *trng;
 54	struct resource *res;
 55	int ret;
 56
 57	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
 58	if (!trng)
 59		return -ENOMEM;
 60
 61	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 62	trng->base = devm_ioremap_resource(&pdev->dev, res);
 63	if (IS_ERR(trng->base))
 64		return PTR_ERR(trng->base);
 65
 66	trng->clk = devm_clk_get(&pdev->dev, NULL);
 67	if (IS_ERR(trng->clk))
 68		return PTR_ERR(trng->clk);
 
 
 
 69
 70	ret = clk_prepare_enable(trng->clk);
 71	if (ret)
 72		return ret;
 73
 74	writel(TRNG_KEY | 1, trng->base + TRNG_CR);
 75	trng->rng.name = pdev->name;
 76	trng->rng.read = atmel_trng_read;
 
 
 77
 78	ret = hwrng_register(&trng->rng);
 
 79	if (ret)
 80		goto err_register;
 
 81
 82	platform_set_drvdata(pdev, trng);
 
 
 
 
 
 
 
 
 
 
 
 83
 84	return 0;
 85
 86err_register:
 87	clk_disable(trng->clk);
 88	return ret;
 89}
 90
 91static int atmel_trng_remove(struct platform_device *pdev)
 92{
 93	struct atmel_trng *trng = platform_get_drvdata(pdev);
 94
 95	hwrng_unregister(&trng->rng);
 96
 97	writel(TRNG_KEY, trng->base + TRNG_CR);
 98	clk_disable_unprepare(trng->clk);
 99
100	return 0;
101}
102
103#ifdef CONFIG_PM
104static int atmel_trng_suspend(struct device *dev)
105{
106	struct atmel_trng *trng = dev_get_drvdata(dev);
107
108	clk_disable_unprepare(trng->clk);
109
110	return 0;
111}
112
113static int atmel_trng_resume(struct device *dev)
114{
115	struct atmel_trng *trng = dev_get_drvdata(dev);
116
117	return clk_prepare_enable(trng->clk);
118}
119
120static const struct dev_pm_ops atmel_trng_pm_ops = {
121	.suspend	= atmel_trng_suspend,
122	.resume		= atmel_trng_resume,
 
 
 
 
 
 
 
 
 
 
123};
124#endif /* CONFIG_PM */
125
126static const struct of_device_id atmel_trng_dt_ids[] = {
127	{ .compatible = "atmel,at91sam9g45-trng" },
128	{ /* sentinel */ }
 
 
 
 
 
 
 
129};
130MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
131
132static struct platform_driver atmel_trng_driver = {
133	.probe		= atmel_trng_probe,
134	.remove		= atmel_trng_remove,
135	.driver		= {
136		.name	= "atmel-trng",
137#ifdef CONFIG_PM
138		.pm	= &atmel_trng_pm_ops,
139#endif /* CONFIG_PM */
140		.of_match_table = atmel_trng_dt_ids,
141	},
142};
143
144module_platform_driver(atmel_trng_driver);
145
146MODULE_LICENSE("GPL");
147MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
148MODULE_DESCRIPTION("Atmel true random number generator driver");