xref: /openbmc/linux/drivers/char/hw_random/omap3-rom-rng.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
11c6b7c21SPali Rohár /*
21c6b7c21SPali Rohár  * omap3-rom-rng.c - RNG driver for TI OMAP3 CPU family
31c6b7c21SPali Rohár  *
41c6b7c21SPali Rohár  * Copyright (C) 2009 Nokia Corporation
51c6b7c21SPali Rohár  * Author: Juha Yrjola <juha.yrjola@solidboot.com>
61c6b7c21SPali Rohár  *
7149ed3d4SPali Rohár  * Copyright (C) 2013 Pali Rohár <pali@kernel.org>
81c6b7c21SPali Rohár  *
91c6b7c21SPali Rohár  * This file is licensed under  the terms of the GNU General Public
101c6b7c21SPali Rohár  * License version 2. This program is licensed "as is" without any
111c6b7c21SPali Rohár  * warranty of any kind, whether express or implied.
121c6b7c21SPali Rohár  */
131c6b7c21SPali Rohár 
141c6b7c21SPali Rohár #include <linux/module.h>
151c6b7c21SPali Rohár #include <linux/init.h>
161c6b7c21SPali Rohár #include <linux/random.h>
171c6b7c21SPali Rohár #include <linux/hw_random.h>
184c13ac1cSAaro Koskinen #include <linux/workqueue.h>
191c6b7c21SPali Rohár #include <linux/clk.h>
201c6b7c21SPali Rohár #include <linux/err.h>
21ba02b352SHerbert Xu #include <linux/io.h>
220c0ef9eaSTony Lindgren #include <linux/of.h>
231c6b7c21SPali Rohár #include <linux/platform_device.h>
248d9d4bdcSTony Lindgren #include <linux/pm_runtime.h>
251c6b7c21SPali Rohár 
261c6b7c21SPali Rohár #define RNG_RESET			0x01
271c6b7c21SPali Rohár #define RNG_GEN_PRNG_HW_INIT		0x02
281c6b7c21SPali Rohár #define RNG_GEN_HW			0x08
291c6b7c21SPali Rohár 
30b8d665aeSTony Lindgren struct omap_rom_rng {
31b8d665aeSTony Lindgren 	struct clk *clk;
32b8d665aeSTony Lindgren 	struct device *dev;
33b8d665aeSTony Lindgren 	struct hwrng ops;
348d9d4bdcSTony Lindgren 	u32 (*rom_rng_call)(u32 ptr, u32 count, u32 flag);
35b8d665aeSTony Lindgren };
36b8d665aeSTony Lindgren 
omap3_rom_rng_read(struct hwrng * rng,void * data,size_t max,bool w)37161040fcSPrasannaKumar Muralidharan static int omap3_rom_rng_read(struct hwrng *rng, void *data, size_t max, bool w)
381c6b7c21SPali Rohár {
398d9d4bdcSTony Lindgren 	struct omap_rom_rng *ddata;
408d9d4bdcSTony Lindgren 	u32 ptr;
411c6b7c21SPali Rohár 	int r;
421c6b7c21SPali Rohár 
438d9d4bdcSTony Lindgren 	ddata = (struct omap_rom_rng *)rng->priv;
448d9d4bdcSTony Lindgren 
458d9d4bdcSTony Lindgren 	r = pm_runtime_get_sync(ddata->dev);
468d9d4bdcSTony Lindgren 	if (r < 0) {
478d9d4bdcSTony Lindgren 		pm_runtime_put_noidle(ddata->dev);
488d9d4bdcSTony Lindgren 
498d9d4bdcSTony Lindgren 		return r;
508d9d4bdcSTony Lindgren 	}
518d9d4bdcSTony Lindgren 
528d9d4bdcSTony Lindgren 	ptr = virt_to_phys(data);
538d9d4bdcSTony Lindgren 	r = ddata->rom_rng_call(ptr, 4, RNG_GEN_HW);
548d9d4bdcSTony Lindgren 	if (r != 0)
558d9d4bdcSTony Lindgren 		r = -EINVAL;
568d9d4bdcSTony Lindgren 	else
578d9d4bdcSTony Lindgren 		r = 4;
588d9d4bdcSTony Lindgren 
598d9d4bdcSTony Lindgren 	pm_runtime_mark_last_busy(ddata->dev);
608d9d4bdcSTony Lindgren 	pm_runtime_put_autosuspend(ddata->dev);
618d9d4bdcSTony Lindgren 
628d9d4bdcSTony Lindgren 	return r;
638d9d4bdcSTony Lindgren }
648d9d4bdcSTony Lindgren 
omap_rom_rng_runtime_suspend(struct device * dev)65fbbfb3f8SArnd Bergmann static int __maybe_unused omap_rom_rng_runtime_suspend(struct device *dev)
668d9d4bdcSTony Lindgren {
678d9d4bdcSTony Lindgren 	struct omap_rom_rng *ddata;
688d9d4bdcSTony Lindgren 	int r;
698d9d4bdcSTony Lindgren 
708d9d4bdcSTony Lindgren 	ddata = dev_get_drvdata(dev);
718d9d4bdcSTony Lindgren 
728d9d4bdcSTony Lindgren 	r = ddata->rom_rng_call(0, 0, RNG_RESET);
738d9d4bdcSTony Lindgren 	if (r != 0)
748d9d4bdcSTony Lindgren 		dev_err(dev, "reset failed: %d\n", r);
758d9d4bdcSTony Lindgren 
768d9d4bdcSTony Lindgren 	clk_disable_unprepare(ddata->clk);
778d9d4bdcSTony Lindgren 
788d9d4bdcSTony Lindgren 	return 0;
798d9d4bdcSTony Lindgren }
808d9d4bdcSTony Lindgren 
omap_rom_rng_runtime_resume(struct device * dev)81fbbfb3f8SArnd Bergmann static int __maybe_unused omap_rom_rng_runtime_resume(struct device *dev)
828d9d4bdcSTony Lindgren {
838d9d4bdcSTony Lindgren 	struct omap_rom_rng *ddata;
848d9d4bdcSTony Lindgren 	int r;
858d9d4bdcSTony Lindgren 
868d9d4bdcSTony Lindgren 	ddata = dev_get_drvdata(dev);
878d9d4bdcSTony Lindgren 
888d9d4bdcSTony Lindgren 	r = clk_prepare_enable(ddata->clk);
891c6b7c21SPali Rohár 	if (r < 0)
901c6b7c21SPali Rohár 		return r;
918d9d4bdcSTony Lindgren 
928d9d4bdcSTony Lindgren 	r = ddata->rom_rng_call(0, 0, RNG_GEN_PRNG_HW_INIT);
938d9d4bdcSTony Lindgren 	if (r != 0) {
94*e4e62bbcSYang Yingliang 		clk_disable_unprepare(ddata->clk);
958d9d4bdcSTony Lindgren 		dev_err(dev, "HW init failed: %d\n", r);
968d9d4bdcSTony Lindgren 
978d9d4bdcSTony Lindgren 		return -EIO;
988d9d4bdcSTony Lindgren 	}
998d9d4bdcSTony Lindgren 
1008d9d4bdcSTony Lindgren 	return 0;
1011c6b7c21SPali Rohár }
1021c6b7c21SPali Rohár 
omap_rom_rng_finish(void * data)103bac5c49eSTony Lindgren static void omap_rom_rng_finish(void *data)
104bac5c49eSTony Lindgren {
105bac5c49eSTony Lindgren 	struct omap_rom_rng *ddata = data;
106bac5c49eSTony Lindgren 
107bac5c49eSTony Lindgren 	pm_runtime_dont_use_autosuspend(ddata->dev);
108bac5c49eSTony Lindgren 	pm_runtime_disable(ddata->dev);
109bac5c49eSTony Lindgren }
110bac5c49eSTony Lindgren 
omap3_rom_rng_probe(struct platform_device * pdev)1111c6b7c21SPali Rohár static int omap3_rom_rng_probe(struct platform_device *pdev)
1121c6b7c21SPali Rohár {
113b8d665aeSTony Lindgren 	struct omap_rom_rng *ddata;
114986130bfSArvind Yadav 	int ret = 0;
115986130bfSArvind Yadav 
116b8d665aeSTony Lindgren 	ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
117b8d665aeSTony Lindgren 	if (!ddata)
118b8d665aeSTony Lindgren 		return -ENOMEM;
119b8d665aeSTony Lindgren 
120b8d665aeSTony Lindgren 	ddata->dev = &pdev->dev;
121b8d665aeSTony Lindgren 	ddata->ops.priv = (unsigned long)ddata;
122b8d665aeSTony Lindgren 	ddata->ops.name = "omap3-rom";
123b8d665aeSTony Lindgren 	ddata->ops.read = of_device_get_match_data(&pdev->dev);
124b8d665aeSTony Lindgren 	ddata->ops.quality = 900;
125b8d665aeSTony Lindgren 	if (!ddata->ops.read) {
1260c0ef9eaSTony Lindgren 		dev_err(&pdev->dev, "missing rom code handler\n");
1270c0ef9eaSTony Lindgren 
1280c0ef9eaSTony Lindgren 		return -ENODEV;
1290c0ef9eaSTony Lindgren 	}
130b8d665aeSTony Lindgren 	dev_set_drvdata(ddata->dev, ddata);
1311c6b7c21SPali Rohár 
1328d9d4bdcSTony Lindgren 	ddata->rom_rng_call = pdev->dev.platform_data;
1338d9d4bdcSTony Lindgren 	if (!ddata->rom_rng_call) {
134b8d665aeSTony Lindgren 		dev_err(ddata->dev, "rom_rng_call is NULL\n");
1351c6b7c21SPali Rohár 		return -EINVAL;
1361c6b7c21SPali Rohár 	}
1371c6b7c21SPali Rohár 
138b8d665aeSTony Lindgren 	ddata->clk = devm_clk_get(ddata->dev, "ick");
139b8d665aeSTony Lindgren 	if (IS_ERR(ddata->clk)) {
140b8d665aeSTony Lindgren 		dev_err(ddata->dev, "unable to get RNG clock\n");
141b8d665aeSTony Lindgren 		return PTR_ERR(ddata->clk);
1421c6b7c21SPali Rohár 	}
1431c6b7c21SPali Rohár 
144bac5c49eSTony Lindgren 	pm_runtime_enable(&pdev->dev);
145bac5c49eSTony Lindgren 	pm_runtime_set_autosuspend_delay(&pdev->dev, 500);
146bac5c49eSTony Lindgren 	pm_runtime_use_autosuspend(&pdev->dev);
1478d9d4bdcSTony Lindgren 
148bac5c49eSTony Lindgren 	ret = devm_add_action_or_reset(ddata->dev, omap_rom_rng_finish,
149bac5c49eSTony Lindgren 				       ddata);
150bac5c49eSTony Lindgren 	if (ret)
151986130bfSArvind Yadav 		return ret;
1521c6b7c21SPali Rohár 
153bac5c49eSTony Lindgren 	return devm_hwrng_register(ddata->dev, &ddata->ops);
1541c6b7c21SPali Rohár }
1551c6b7c21SPali Rohár 
1560c0ef9eaSTony Lindgren static const struct of_device_id omap_rom_rng_match[] = {
1570c0ef9eaSTony Lindgren 	{ .compatible = "nokia,n900-rom-rng", .data = omap3_rom_rng_read, },
1580c0ef9eaSTony Lindgren 	{ /* sentinel */ },
1590c0ef9eaSTony Lindgren };
1600c0ef9eaSTony Lindgren MODULE_DEVICE_TABLE(of, omap_rom_rng_match);
1610c0ef9eaSTony Lindgren 
1628d9d4bdcSTony Lindgren static const struct dev_pm_ops omap_rom_rng_pm_ops = {
1638d9d4bdcSTony Lindgren 	SET_SYSTEM_SLEEP_PM_OPS(omap_rom_rng_runtime_suspend,
1648d9d4bdcSTony Lindgren 				omap_rom_rng_runtime_resume)
1658d9d4bdcSTony Lindgren };
1668d9d4bdcSTony Lindgren 
1671c6b7c21SPali Rohár static struct platform_driver omap3_rom_rng_driver = {
1681c6b7c21SPali Rohár 	.driver = {
1691c6b7c21SPali Rohár 		.name		= "omap3-rom-rng",
1700c0ef9eaSTony Lindgren 		.of_match_table = omap_rom_rng_match,
1718d9d4bdcSTony Lindgren 		.pm = &omap_rom_rng_pm_ops,
1721c6b7c21SPali Rohár 	},
1731c6b7c21SPali Rohár 	.probe		= omap3_rom_rng_probe,
1741c6b7c21SPali Rohár };
1751c6b7c21SPali Rohár 
1761c6b7c21SPali Rohár module_platform_driver(omap3_rom_rng_driver);
1771c6b7c21SPali Rohár 
1781c6b7c21SPali Rohár MODULE_ALIAS("platform:omap3-rom-rng");
1791c6b7c21SPali Rohár MODULE_AUTHOR("Juha Yrjola");
180149ed3d4SPali Rohár MODULE_AUTHOR("Pali Rohár <pali@kernel.org>");
1811c6b7c21SPali Rohár MODULE_LICENSE("GPL");
182