xref: /openbmc/linux/drivers/char/hw_random/mxc-rnga.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1fcaf2036SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
245001e92SAlan Carvalho de Assis /*
345001e92SAlan Carvalho de Assis  * RNG driver for Freescale RNGA
445001e92SAlan Carvalho de Assis  *
545001e92SAlan Carvalho de Assis  * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
645001e92SAlan Carvalho de Assis  * Author: Alan Carvalho de Assis <acassis@gmail.com>
745001e92SAlan Carvalho de Assis  */
845001e92SAlan Carvalho de Assis 
945001e92SAlan Carvalho de Assis /*
1045001e92SAlan Carvalho de Assis  *
1145001e92SAlan Carvalho de Assis  * This driver is based on other RNG drivers.
1245001e92SAlan Carvalho de Assis  */
1345001e92SAlan Carvalho de Assis 
1445001e92SAlan Carvalho de Assis #include <linux/clk.h>
1536211890SBenoît Thébaudeau #include <linux/delay.h>
16a6ab6402SVladimir Zapolskiy #include <linux/hw_random.h>
1745001e92SAlan Carvalho de Assis #include <linux/io.h>
18a6ab6402SVladimir Zapolskiy #include <linux/module.h>
19a6ab6402SVladimir Zapolskiy #include <linux/of.h>
20a6ab6402SVladimir Zapolskiy #include <linux/platform_device.h>
2145001e92SAlan Carvalho de Assis 
2245001e92SAlan Carvalho de Assis /* RNGA Registers */
2345001e92SAlan Carvalho de Assis #define RNGA_CONTROL			0x00
2445001e92SAlan Carvalho de Assis #define RNGA_STATUS			0x04
2545001e92SAlan Carvalho de Assis #define RNGA_ENTROPY			0x08
2645001e92SAlan Carvalho de Assis #define RNGA_OUTPUT_FIFO		0x0c
2745001e92SAlan Carvalho de Assis #define RNGA_MODE			0x10
2845001e92SAlan Carvalho de Assis #define RNGA_VERIFICATION_CONTROL	0x14
2945001e92SAlan Carvalho de Assis #define RNGA_OSC_CONTROL_COUNTER	0x18
3045001e92SAlan Carvalho de Assis #define RNGA_OSC1_COUNTER		0x1c
3145001e92SAlan Carvalho de Assis #define RNGA_OSC2_COUNTER		0x20
3245001e92SAlan Carvalho de Assis #define RNGA_OSC_COUNTER_STATUS		0x24
3345001e92SAlan Carvalho de Assis 
3445001e92SAlan Carvalho de Assis /* RNGA Registers Range */
3545001e92SAlan Carvalho de Assis #define RNG_ADDR_RANGE			0x28
3645001e92SAlan Carvalho de Assis 
3745001e92SAlan Carvalho de Assis /* RNGA Control Register */
3845001e92SAlan Carvalho de Assis #define RNGA_CONTROL_SLEEP		0x00000010
3945001e92SAlan Carvalho de Assis #define RNGA_CONTROL_CLEAR_INT		0x00000008
4045001e92SAlan Carvalho de Assis #define RNGA_CONTROL_MASK_INTS		0x00000004
4145001e92SAlan Carvalho de Assis #define RNGA_CONTROL_HIGH_ASSURANCE	0x00000002
4245001e92SAlan Carvalho de Assis #define RNGA_CONTROL_GO			0x00000001
4345001e92SAlan Carvalho de Assis 
4445001e92SAlan Carvalho de Assis #define RNGA_STATUS_LEVEL_MASK		0x0000ff00
4545001e92SAlan Carvalho de Assis 
4645001e92SAlan Carvalho de Assis /* RNGA Status Register */
4745001e92SAlan Carvalho de Assis #define RNGA_STATUS_OSC_DEAD		0x80000000
4845001e92SAlan Carvalho de Assis #define RNGA_STATUS_SLEEP		0x00000010
4945001e92SAlan Carvalho de Assis #define RNGA_STATUS_ERROR_INT		0x00000008
5045001e92SAlan Carvalho de Assis #define RNGA_STATUS_FIFO_UNDERFLOW	0x00000004
5145001e92SAlan Carvalho de Assis #define RNGA_STATUS_LAST_READ_STATUS	0x00000002
5245001e92SAlan Carvalho de Assis #define RNGA_STATUS_SECURITY_VIOLATION	0x00000001
5345001e92SAlan Carvalho de Assis 
54821873abSFabio Estevam struct mxc_rng {
55821873abSFabio Estevam 	struct device *dev;
56821873abSFabio Estevam 	struct hwrng rng;
57821873abSFabio Estevam 	void __iomem *mem;
58821873abSFabio Estevam 	struct clk *clk;
59821873abSFabio Estevam };
6045001e92SAlan Carvalho de Assis 
mxc_rnga_data_present(struct hwrng * rng,int wait)6136211890SBenoît Thébaudeau static int mxc_rnga_data_present(struct hwrng *rng, int wait)
6245001e92SAlan Carvalho de Assis {
6336211890SBenoît Thébaudeau 	int i;
64821873abSFabio Estevam 	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
6545001e92SAlan Carvalho de Assis 
6636211890SBenoît Thébaudeau 	for (i = 0; i < 20; i++) {
6736211890SBenoît Thébaudeau 		/* how many random numbers are in FIFO? [0-16] */
68821873abSFabio Estevam 		int level = (__raw_readl(mxc_rng->mem + RNGA_STATUS) &
6936211890SBenoît Thébaudeau 				RNGA_STATUS_LEVEL_MASK) >> 8;
7036211890SBenoît Thébaudeau 		if (level || !wait)
7136211890SBenoît Thébaudeau 			return !!level;
7236211890SBenoît Thébaudeau 		udelay(10);
7336211890SBenoît Thébaudeau 	}
7436211890SBenoît Thébaudeau 	return 0;
7545001e92SAlan Carvalho de Assis }
7645001e92SAlan Carvalho de Assis 
mxc_rnga_data_read(struct hwrng * rng,u32 * data)7745001e92SAlan Carvalho de Assis static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
7845001e92SAlan Carvalho de Assis {
7945001e92SAlan Carvalho de Assis 	int err;
8045001e92SAlan Carvalho de Assis 	u32 ctrl;
81821873abSFabio Estevam 	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
8245001e92SAlan Carvalho de Assis 
8345001e92SAlan Carvalho de Assis 	/* retrieve a random number from FIFO */
84821873abSFabio Estevam 	*data = __raw_readl(mxc_rng->mem + RNGA_OUTPUT_FIFO);
8545001e92SAlan Carvalho de Assis 
8645001e92SAlan Carvalho de Assis 	/* some error while reading this random number? */
87821873abSFabio Estevam 	err = __raw_readl(mxc_rng->mem + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
8845001e92SAlan Carvalho de Assis 
8945001e92SAlan Carvalho de Assis 	/* if error: clear error interrupt, but doesn't return random number */
9045001e92SAlan Carvalho de Assis 	if (err) {
91821873abSFabio Estevam 		dev_dbg(mxc_rng->dev, "Error while reading random number!\n");
92821873abSFabio Estevam 		ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
9345001e92SAlan Carvalho de Assis 		__raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
94821873abSFabio Estevam 					mxc_rng->mem + RNGA_CONTROL);
9545001e92SAlan Carvalho de Assis 		return 0;
9645001e92SAlan Carvalho de Assis 	} else
9745001e92SAlan Carvalho de Assis 		return 4;
9845001e92SAlan Carvalho de Assis }
9945001e92SAlan Carvalho de Assis 
mxc_rnga_init(struct hwrng * rng)10045001e92SAlan Carvalho de Assis static int mxc_rnga_init(struct hwrng *rng)
10145001e92SAlan Carvalho de Assis {
10245001e92SAlan Carvalho de Assis 	u32 ctrl, osc;
103821873abSFabio Estevam 	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
10445001e92SAlan Carvalho de Assis 
10545001e92SAlan Carvalho de Assis 	/* wake up */
106821873abSFabio Estevam 	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
107821873abSFabio Estevam 	__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, mxc_rng->mem + RNGA_CONTROL);
10845001e92SAlan Carvalho de Assis 
10945001e92SAlan Carvalho de Assis 	/* verify if oscillator is working */
110821873abSFabio Estevam 	osc = __raw_readl(mxc_rng->mem + RNGA_STATUS);
11145001e92SAlan Carvalho de Assis 	if (osc & RNGA_STATUS_OSC_DEAD) {
112821873abSFabio Estevam 		dev_err(mxc_rng->dev, "RNGA Oscillator is dead!\n");
11345001e92SAlan Carvalho de Assis 		return -ENODEV;
11445001e92SAlan Carvalho de Assis 	}
11545001e92SAlan Carvalho de Assis 
11645001e92SAlan Carvalho de Assis 	/* go running */
117821873abSFabio Estevam 	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
118821873abSFabio Estevam 	__raw_writel(ctrl | RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);
11945001e92SAlan Carvalho de Assis 
12045001e92SAlan Carvalho de Assis 	return 0;
12145001e92SAlan Carvalho de Assis }
12245001e92SAlan Carvalho de Assis 
mxc_rnga_cleanup(struct hwrng * rng)12345001e92SAlan Carvalho de Assis static void mxc_rnga_cleanup(struct hwrng *rng)
12445001e92SAlan Carvalho de Assis {
12545001e92SAlan Carvalho de Assis 	u32 ctrl;
126821873abSFabio Estevam 	struct mxc_rng *mxc_rng = container_of(rng, struct mxc_rng, rng);
12745001e92SAlan Carvalho de Assis 
128821873abSFabio Estevam 	ctrl = __raw_readl(mxc_rng->mem + RNGA_CONTROL);
12945001e92SAlan Carvalho de Assis 
13045001e92SAlan Carvalho de Assis 	/* stop rnga */
131821873abSFabio Estevam 	__raw_writel(ctrl & ~RNGA_CONTROL_GO, mxc_rng->mem + RNGA_CONTROL);
13245001e92SAlan Carvalho de Assis }
13345001e92SAlan Carvalho de Assis 
mxc_rnga_probe(struct platform_device * pdev)13445001e92SAlan Carvalho de Assis static int __init mxc_rnga_probe(struct platform_device *pdev)
13545001e92SAlan Carvalho de Assis {
136c09e2cc6SFabio Estevam 	int err;
137821873abSFabio Estevam 	struct mxc_rng *mxc_rng;
13845001e92SAlan Carvalho de Assis 
13905db0ad8SFabio Estevam 	mxc_rng = devm_kzalloc(&pdev->dev, sizeof(*mxc_rng), GFP_KERNEL);
140821873abSFabio Estevam 	if (!mxc_rng)
141821873abSFabio Estevam 		return -ENOMEM;
14245001e92SAlan Carvalho de Assis 
143821873abSFabio Estevam 	mxc_rng->dev = &pdev->dev;
144821873abSFabio Estevam 	mxc_rng->rng.name = "mxc-rnga";
145821873abSFabio Estevam 	mxc_rng->rng.init = mxc_rnga_init;
146*03ace9b1SJulia Lawall 	mxc_rng->rng.cleanup = mxc_rnga_cleanup;
147*03ace9b1SJulia Lawall 	mxc_rng->rng.data_present = mxc_rnga_data_present;
148*03ace9b1SJulia Lawall 	mxc_rng->rng.data_read = mxc_rnga_data_read;
149821873abSFabio Estevam 
150821873abSFabio Estevam 	mxc_rng->clk = devm_clk_get(&pdev->dev, NULL);
151821873abSFabio Estevam 	if (IS_ERR(mxc_rng->clk)) {
15245001e92SAlan Carvalho de Assis 		dev_err(&pdev->dev, "Could not get rng_clk!\n");
1531bf2138eSFabio Estevam 		return PTR_ERR(mxc_rng->clk);
15445001e92SAlan Carvalho de Assis 	}
15545001e92SAlan Carvalho de Assis 
1569e01d0c6SFabio Estevam 	err = clk_prepare_enable(mxc_rng->clk);
1579e01d0c6SFabio Estevam 	if (err)
1581bf2138eSFabio Estevam 		return err;
15945001e92SAlan Carvalho de Assis 
160f2f1d75aSAnson Huang 	mxc_rng->mem = devm_platform_ioremap_resource(pdev, 0);
161264878e6SFabio Estevam 	if (IS_ERR(mxc_rng->mem)) {
162264878e6SFabio Estevam 		err = PTR_ERR(mxc_rng->mem);
16345001e92SAlan Carvalho de Assis 		goto err_ioremap;
16445001e92SAlan Carvalho de Assis 	}
16545001e92SAlan Carvalho de Assis 
166821873abSFabio Estevam 	err = hwrng_register(&mxc_rng->rng);
16745001e92SAlan Carvalho de Assis 	if (err) {
16845001e92SAlan Carvalho de Assis 		dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
169821873abSFabio Estevam 		goto err_ioremap;
17045001e92SAlan Carvalho de Assis 	}
17145001e92SAlan Carvalho de Assis 
17245001e92SAlan Carvalho de Assis 	return 0;
17345001e92SAlan Carvalho de Assis 
17445001e92SAlan Carvalho de Assis err_ioremap:
175821873abSFabio Estevam 	clk_disable_unprepare(mxc_rng->clk);
17645001e92SAlan Carvalho de Assis 	return err;
17745001e92SAlan Carvalho de Assis }
17845001e92SAlan Carvalho de Assis 
mxc_rnga_remove(struct platform_device * pdev)17945001e92SAlan Carvalho de Assis static int __exit mxc_rnga_remove(struct platform_device *pdev)
18045001e92SAlan Carvalho de Assis {
181821873abSFabio Estevam 	struct mxc_rng *mxc_rng = platform_get_drvdata(pdev);
18245001e92SAlan Carvalho de Assis 
183821873abSFabio Estevam 	hwrng_unregister(&mxc_rng->rng);
18445001e92SAlan Carvalho de Assis 
185821873abSFabio Estevam 	clk_disable_unprepare(mxc_rng->clk);
18645001e92SAlan Carvalho de Assis 
18745001e92SAlan Carvalho de Assis 	return 0;
18845001e92SAlan Carvalho de Assis }
18945001e92SAlan Carvalho de Assis 
190a6ab6402SVladimir Zapolskiy static const struct of_device_id mxc_rnga_of_match[] = {
191a6ab6402SVladimir Zapolskiy 	{ .compatible = "fsl,imx21-rnga", },
192a6ab6402SVladimir Zapolskiy 	{ .compatible = "fsl,imx31-rnga", },
193a6ab6402SVladimir Zapolskiy 	{ /* sentinel */ },
194a6ab6402SVladimir Zapolskiy };
195a6ab6402SVladimir Zapolskiy MODULE_DEVICE_TABLE(of, mxc_rnga_of_match);
196a6ab6402SVladimir Zapolskiy 
19745001e92SAlan Carvalho de Assis static struct platform_driver mxc_rnga_driver = {
19845001e92SAlan Carvalho de Assis 	.driver = {
19945001e92SAlan Carvalho de Assis 		.name = "mxc_rnga",
200a6ab6402SVladimir Zapolskiy 		.of_match_table = mxc_rnga_of_match,
20145001e92SAlan Carvalho de Assis 	},
20245001e92SAlan Carvalho de Assis 	.remove = __exit_p(mxc_rnga_remove),
20345001e92SAlan Carvalho de Assis };
20445001e92SAlan Carvalho de Assis 
205e7c2199fSFabio Porcedda module_platform_driver_probe(mxc_rnga_driver, mxc_rnga_probe);
20645001e92SAlan Carvalho de Assis 
20745001e92SAlan Carvalho de Assis MODULE_AUTHOR("Freescale Semiconductor, Inc.");
20845001e92SAlan Carvalho de Assis MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
20945001e92SAlan Carvalho de Assis MODULE_LICENSE("GPL");
210