Path: blob/master/drivers/char/hw_random/mxc-rnga.c
15111 views
/*1* RNG driver for Freescale RNGA2*3* Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.4* Author: Alan Carvalho de Assis <[email protected]>5*/67/*8* The code contained herein is licensed under the GNU General Public9* License. You may obtain a copy of the GNU General Public License10* Version 2 or later at the following locations:11*12* http://www.opensource.org/licenses/gpl-license.html13* http://www.gnu.org/copyleft/gpl.html14*15* This driver is based on other RNG drivers.16*/1718#include <linux/module.h>19#include <linux/init.h>20#include <linux/kernel.h>21#include <linux/clk.h>22#include <linux/err.h>23#include <linux/ioport.h>24#include <linux/platform_device.h>25#include <linux/hw_random.h>26#include <linux/io.h>2728/* RNGA Registers */29#define RNGA_CONTROL 0x0030#define RNGA_STATUS 0x0431#define RNGA_ENTROPY 0x0832#define RNGA_OUTPUT_FIFO 0x0c33#define RNGA_MODE 0x1034#define RNGA_VERIFICATION_CONTROL 0x1435#define RNGA_OSC_CONTROL_COUNTER 0x1836#define RNGA_OSC1_COUNTER 0x1c37#define RNGA_OSC2_COUNTER 0x2038#define RNGA_OSC_COUNTER_STATUS 0x243940/* RNGA Registers Range */41#define RNG_ADDR_RANGE 0x284243/* RNGA Control Register */44#define RNGA_CONTROL_SLEEP 0x0000001045#define RNGA_CONTROL_CLEAR_INT 0x0000000846#define RNGA_CONTROL_MASK_INTS 0x0000000447#define RNGA_CONTROL_HIGH_ASSURANCE 0x0000000248#define RNGA_CONTROL_GO 0x000000014950#define RNGA_STATUS_LEVEL_MASK 0x0000ff005152/* RNGA Status Register */53#define RNGA_STATUS_OSC_DEAD 0x8000000054#define RNGA_STATUS_SLEEP 0x0000001055#define RNGA_STATUS_ERROR_INT 0x0000000856#define RNGA_STATUS_FIFO_UNDERFLOW 0x0000000457#define RNGA_STATUS_LAST_READ_STATUS 0x0000000258#define RNGA_STATUS_SECURITY_VIOLATION 0x000000015960static struct platform_device *rng_dev;6162static int mxc_rnga_data_present(struct hwrng *rng)63{64int level;65void __iomem *rng_base = (void __iomem *)rng->priv;6667/* how many random numbers is in FIFO? [0-16] */68level = ((__raw_readl(rng_base + RNGA_STATUS) &69RNGA_STATUS_LEVEL_MASK) >> 8);7071return level > 0 ? 1 : 0;72}7374static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)75{76int err;77u32 ctrl;78void __iomem *rng_base = (void __iomem *)rng->priv;7980/* retrieve a random number from FIFO */81*data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);8283/* some error while reading this random number? */84err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;8586/* if error: clear error interrupt, but doesn't return random number */87if (err) {88dev_dbg(&rng_dev->dev, "Error while reading random number!\n");89ctrl = __raw_readl(rng_base + RNGA_CONTROL);90__raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,91rng_base + RNGA_CONTROL);92return 0;93} else94return 4;95}9697static int mxc_rnga_init(struct hwrng *rng)98{99u32 ctrl, osc;100void __iomem *rng_base = (void __iomem *)rng->priv;101102/* wake up */103ctrl = __raw_readl(rng_base + RNGA_CONTROL);104__raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);105106/* verify if oscillator is working */107osc = __raw_readl(rng_base + RNGA_STATUS);108if (osc & RNGA_STATUS_OSC_DEAD) {109dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");110return -ENODEV;111}112113/* go running */114ctrl = __raw_readl(rng_base + RNGA_CONTROL);115__raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);116117return 0;118}119120static void mxc_rnga_cleanup(struct hwrng *rng)121{122u32 ctrl;123void __iomem *rng_base = (void __iomem *)rng->priv;124125ctrl = __raw_readl(rng_base + RNGA_CONTROL);126127/* stop rnga */128__raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);129}130131static struct hwrng mxc_rnga = {132.name = "mxc-rnga",133.init = mxc_rnga_init,134.cleanup = mxc_rnga_cleanup,135.data_present = mxc_rnga_data_present,136.data_read = mxc_rnga_data_read137};138139static int __init mxc_rnga_probe(struct platform_device *pdev)140{141int err = -ENODEV;142struct clk *clk;143struct resource *res, *mem;144void __iomem *rng_base = NULL;145146if (rng_dev)147return -EBUSY;148149clk = clk_get(&pdev->dev, "rng");150if (IS_ERR(clk)) {151dev_err(&pdev->dev, "Could not get rng_clk!\n");152err = PTR_ERR(clk);153goto out;154}155156clk_enable(clk);157158res = platform_get_resource(pdev, IORESOURCE_MEM, 0);159if (!res) {160err = -ENOENT;161goto err_region;162}163164mem = request_mem_region(res->start, resource_size(res), pdev->name);165if (mem == NULL) {166err = -EBUSY;167goto err_region;168}169170rng_base = ioremap(res->start, resource_size(res));171if (!rng_base) {172err = -ENOMEM;173goto err_ioremap;174}175176mxc_rnga.priv = (unsigned long)rng_base;177178err = hwrng_register(&mxc_rnga);179if (err) {180dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);181goto err_register;182}183184rng_dev = pdev;185186dev_info(&pdev->dev, "MXC RNGA Registered.\n");187188return 0;189190err_register:191iounmap(rng_base);192rng_base = NULL;193194err_ioremap:195release_mem_region(res->start, resource_size(res));196197err_region:198clk_disable(clk);199clk_put(clk);200201out:202return err;203}204205static int __exit mxc_rnga_remove(struct platform_device *pdev)206{207struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);208void __iomem *rng_base = (void __iomem *)mxc_rnga.priv;209struct clk *clk = clk_get(&pdev->dev, "rng");210211hwrng_unregister(&mxc_rnga);212213iounmap(rng_base);214215release_mem_region(res->start, resource_size(res));216217clk_disable(clk);218clk_put(clk);219220return 0;221}222223static struct platform_driver mxc_rnga_driver = {224.driver = {225.name = "mxc_rnga",226.owner = THIS_MODULE,227},228.remove = __exit_p(mxc_rnga_remove),229};230231static int __init mod_init(void)232{233return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);234}235236static void __exit mod_exit(void)237{238platform_driver_unregister(&mxc_rnga_driver);239}240241module_init(mod_init);242module_exit(mod_exit);243244MODULE_AUTHOR("Freescale Semiconductor, Inc.");245MODULE_DESCRIPTION("H/W RNGA driver for i.MX");246MODULE_LICENSE("GPL");247248249