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");
v5.14.15
  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/hw_random.h>
 17#include <linux/of_device.h>
 18#include <linux/platform_device.h>
 
 19
 20#define TRNG_CR		0x00
 21#define TRNG_MR		0x04
 22#define TRNG_ISR	0x1c
 
 23#define TRNG_ODATA	0x50
 24
 25#define TRNG_KEY	0x524e4700 /* RNG */
 26
 27#define TRNG_HALFR	BIT(0) /* generate RN every 168 cycles */
 28
 29struct atmel_trng_data {
 30	bool has_half_rate;
 31};
 32
 33struct atmel_trng {
 34	struct clk *clk;
 35	void __iomem *base;
 36	struct hwrng rng;
 
 37};
 38
 
 
 
 
 
 
 
 
 
 
 
 
 39static int atmel_trng_read(struct hwrng *rng, void *buf, size_t max,
 40			   bool wait)
 41{
 42	struct atmel_trng *trng = container_of(rng, struct atmel_trng, rng);
 43	u32 *data = buf;
 
 44
 45	/* data ready? */
 46	if (readl(trng->base + TRNG_ISR) & 1) {
 47		*data = readl(trng->base + TRNG_ODATA);
 48		/*
 49		  ensure data ready is only set again AFTER the next data
 50		  word is ready in case it got set between checking ISR
 51		  and reading ODATA, so we don't risk re-reading the
 52		  same word
 53		*/
 54		readl(trng->base + TRNG_ISR);
 55		return 4;
 56	} else
 57		return 0;
 
 
 
 
 
 
 
 
 
 
 58}
 59
 60static void atmel_trng_enable(struct atmel_trng *trng)
 61{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 62	writel(TRNG_KEY | 1, trng->base + TRNG_CR);
 
 
 63}
 64
 65static void atmel_trng_disable(struct atmel_trng *trng)
 66{
 67	writel(TRNG_KEY, trng->base + TRNG_CR);
 
 68}
 69
 70static int atmel_trng_probe(struct platform_device *pdev)
 71{
 72	struct atmel_trng *trng;
 73	const struct atmel_trng_data *data;
 74	int ret;
 75
 76	trng = devm_kzalloc(&pdev->dev, sizeof(*trng), GFP_KERNEL);
 77	if (!trng)
 78		return -ENOMEM;
 79
 80	trng->base = devm_platform_ioremap_resource(pdev, 0);
 81	if (IS_ERR(trng->base))
 82		return PTR_ERR(trng->base);
 83
 84	trng->clk = devm_clk_get(&pdev->dev, NULL);
 85	if (IS_ERR(trng->clk))
 86		return PTR_ERR(trng->clk);
 87	data = of_device_get_match_data(&pdev->dev);
 88	if (!data)
 89		return -ENODEV;
 90
 91	if (data->has_half_rate) {
 92		unsigned long rate = clk_get_rate(trng->clk);
 
 
 
 93
 94		/* if peripheral clk is above 100MHz, set HALFR */
 95		if (rate > 100000000)
 96			writel(TRNG_HALFR, trng->base + TRNG_MR);
 97	}
 98
 99	ret = clk_prepare_enable(trng->clk);
100	if (ret)
101		return ret;
 
102
103	atmel_trng_enable(trng);
104	trng->rng.name = pdev->name;
105	trng->rng.read = atmel_trng_read;
106
107	ret = devm_hwrng_register(&pdev->dev, &trng->rng);
108	if (ret)
109		goto err_register;
110
111	platform_set_drvdata(pdev, trng);
112
113	return 0;
 
114
115err_register:
116	clk_disable_unprepare(trng->clk);
117	return ret;
118}
119
120static int atmel_trng_remove(struct platform_device *pdev)
121{
122	struct atmel_trng *trng = platform_get_drvdata(pdev);
123
124
125	atmel_trng_disable(trng);
126	clk_disable_unprepare(trng->clk);
127
128	return 0;
129}
130
131#ifdef CONFIG_PM
132static int atmel_trng_suspend(struct device *dev)
133{
134	struct atmel_trng *trng = dev_get_drvdata(dev);
135
136	atmel_trng_disable(trng);
137	clk_disable_unprepare(trng->clk);
138
139	return 0;
140}
141
142static int atmel_trng_resume(struct device *dev)
143{
144	struct atmel_trng *trng = dev_get_drvdata(dev);
145	int ret;
146
147	ret = clk_prepare_enable(trng->clk);
148	if (ret)
149		return ret;
150
151	atmel_trng_enable(trng);
152
153	return 0;
154}
155
156static const struct dev_pm_ops atmel_trng_pm_ops = {
157	.suspend	= atmel_trng_suspend,
158	.resume		= atmel_trng_resume,
 
 
159};
160#endif /* CONFIG_PM */
161
162static const struct atmel_trng_data at91sam9g45_config = {
163	.has_half_rate = false,
164};
165
166static const struct atmel_trng_data sam9x60_config = {
167	.has_half_rate = true,
168};
169
170static const struct of_device_id atmel_trng_dt_ids[] = {
171	{
172		.compatible = "atmel,at91sam9g45-trng",
173		.data = &at91sam9g45_config,
174	}, {
175		.compatible = "microchip,sam9x60-trng",
176		.data = &sam9x60_config,
177	}, {
178		/* sentinel */
179	}
180};
181MODULE_DEVICE_TABLE(of, atmel_trng_dt_ids);
182
183static struct platform_driver atmel_trng_driver = {
184	.probe		= atmel_trng_probe,
185	.remove		= atmel_trng_remove,
186	.driver		= {
187		.name	= "atmel-trng",
188#ifdef CONFIG_PM
189		.pm	= &atmel_trng_pm_ops,
190#endif /* CONFIG_PM */
191		.of_match_table = atmel_trng_dt_ids,
192	},
193};
194
195module_platform_driver(atmel_trng_driver);
196
197MODULE_LICENSE("GPL");
198MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>");
199MODULE_DESCRIPTION("Atmel true random number generator driver");