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