18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Nomadik RNG support
48c2ecf20Sopenharmony_ci *  Copyright 2009 Alessandro Rubini
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/kernel.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/device.h>
108c2ecf20Sopenharmony_ci#include <linux/amba/bus.h>
118c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/clk.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistatic int nmk_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
178c2ecf20Sopenharmony_ci{
188c2ecf20Sopenharmony_ci	void __iomem *base = (void __iomem *)rng->priv;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	/*
218c2ecf20Sopenharmony_ci	 * The register is 32 bits and gives 16 random bits (low half).
228c2ecf20Sopenharmony_ci	 * A subsequent read will delay the core for 400ns, so we just read
238c2ecf20Sopenharmony_ci	 * once and accept the very unlikely very small delay, even if wait==0.
248c2ecf20Sopenharmony_ci	 */
258c2ecf20Sopenharmony_ci	*(u16 *)data = __raw_readl(base + 8) & 0xffff;
268c2ecf20Sopenharmony_ci	return 2;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci/* we have at most one RNG per machine, granted */
308c2ecf20Sopenharmony_cistatic struct hwrng nmk_rng = {
318c2ecf20Sopenharmony_ci	.name		= "nomadik",
328c2ecf20Sopenharmony_ci	.read		= nmk_rng_read,
338c2ecf20Sopenharmony_ci};
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic int nmk_rng_probe(struct amba_device *dev, const struct amba_id *id)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct clk *rng_clk;
388c2ecf20Sopenharmony_ci	void __iomem *base;
398c2ecf20Sopenharmony_ci	int ret;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	rng_clk = devm_clk_get_enabled(&dev->dev, NULL);
428c2ecf20Sopenharmony_ci	if (IS_ERR(rng_clk)) {
438c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "could not get rng clock\n");
448c2ecf20Sopenharmony_ci		ret = PTR_ERR(rng_clk);
458c2ecf20Sopenharmony_ci		return ret;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	ret = amba_request_regions(dev, dev->dev.init_name);
498c2ecf20Sopenharmony_ci	if (ret)
508c2ecf20Sopenharmony_ci		return ret;
518c2ecf20Sopenharmony_ci	ret = -ENOMEM;
528c2ecf20Sopenharmony_ci	base = devm_ioremap(&dev->dev, dev->res.start,
538c2ecf20Sopenharmony_ci			    resource_size(&dev->res));
548c2ecf20Sopenharmony_ci	if (!base)
558c2ecf20Sopenharmony_ci		goto out_release;
568c2ecf20Sopenharmony_ci	nmk_rng.priv = (unsigned long)base;
578c2ecf20Sopenharmony_ci	ret = devm_hwrng_register(&dev->dev, &nmk_rng);
588c2ecf20Sopenharmony_ci	if (ret)
598c2ecf20Sopenharmony_ci		goto out_release;
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ciout_release:
638c2ecf20Sopenharmony_ci	amba_release_regions(dev);
648c2ecf20Sopenharmony_ci	return ret;
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic void nmk_rng_remove(struct amba_device *dev)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	amba_release_regions(dev);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const struct amba_id nmk_rng_ids[] = {
738c2ecf20Sopenharmony_ci	{
748c2ecf20Sopenharmony_ci		.id	= 0x000805e1,
758c2ecf20Sopenharmony_ci		.mask	= 0x000fffff, /* top bits are rev and cfg: accept all */
768c2ecf20Sopenharmony_ci	},
778c2ecf20Sopenharmony_ci	{0, 0},
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(amba, nmk_rng_ids);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct amba_driver nmk_rng_driver = {
838c2ecf20Sopenharmony_ci	.drv = {
848c2ecf20Sopenharmony_ci		.owner = THIS_MODULE,
858c2ecf20Sopenharmony_ci		.name = "rng",
868c2ecf20Sopenharmony_ci		},
878c2ecf20Sopenharmony_ci	.probe = nmk_rng_probe,
888c2ecf20Sopenharmony_ci	.remove = nmk_rng_remove,
898c2ecf20Sopenharmony_ci	.id_table = nmk_rng_ids,
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cimodule_amba_driver(nmk_rng_driver);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
95