linux/drivers/char/hw_random/mxc-rnga.c
<<
>>
Prefs
   1/*
   2 * RNG driver for Freescale RNGA
   3 *
   4 * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
   5 * Author: Alan Carvalho de Assis <acassis@gmail.com>
   6 */
   7
   8/*
   9 * The code contained herein is licensed under the GNU General Public
  10 * License. You may obtain a copy of the GNU General Public License
  11 * Version 2 or later at the following locations:
  12 *
  13 * http://www.opensource.org/licenses/gpl-license.html
  14 * http://www.gnu.org/copyleft/gpl.html
  15 *
  16 * This driver is based on other RNG drivers.
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/init.h>
  21#include <linux/kernel.h>
  22#include <linux/clk.h>
  23#include <linux/err.h>
  24#include <linux/ioport.h>
  25#include <linux/platform_device.h>
  26#include <linux/hw_random.h>
  27#include <linux/io.h>
  28
  29/* RNGA Registers */
  30#define RNGA_CONTROL                    0x00
  31#define RNGA_STATUS                     0x04
  32#define RNGA_ENTROPY                    0x08
  33#define RNGA_OUTPUT_FIFO                0x0c
  34#define RNGA_MODE                       0x10
  35#define RNGA_VERIFICATION_CONTROL       0x14
  36#define RNGA_OSC_CONTROL_COUNTER        0x18
  37#define RNGA_OSC1_COUNTER               0x1c
  38#define RNGA_OSC2_COUNTER               0x20
  39#define RNGA_OSC_COUNTER_STATUS         0x24
  40
  41/* RNGA Registers Range */
  42#define RNG_ADDR_RANGE                  0x28
  43
  44/* RNGA Control Register */
  45#define RNGA_CONTROL_SLEEP              0x00000010
  46#define RNGA_CONTROL_CLEAR_INT          0x00000008
  47#define RNGA_CONTROL_MASK_INTS          0x00000004
  48#define RNGA_CONTROL_HIGH_ASSURANCE     0x00000002
  49#define RNGA_CONTROL_GO                 0x00000001
  50
  51#define RNGA_STATUS_LEVEL_MASK          0x0000ff00
  52
  53/* RNGA Status Register */
  54#define RNGA_STATUS_OSC_DEAD            0x80000000
  55#define RNGA_STATUS_SLEEP               0x00000010
  56#define RNGA_STATUS_ERROR_INT           0x00000008
  57#define RNGA_STATUS_FIFO_UNDERFLOW      0x00000004
  58#define RNGA_STATUS_LAST_READ_STATUS    0x00000002
  59#define RNGA_STATUS_SECURITY_VIOLATION  0x00000001
  60
  61static struct platform_device *rng_dev;
  62
  63static int mxc_rnga_data_present(struct hwrng *rng)
  64{
  65        int level;
  66        void __iomem *rng_base = (void __iomem *)rng->priv;
  67
  68        /* how many random numbers is in FIFO? [0-16] */
  69        level = ((__raw_readl(rng_base + RNGA_STATUS) &
  70                        RNGA_STATUS_LEVEL_MASK) >> 8);
  71
  72        return level > 0 ? 1 : 0;
  73}
  74
  75static int mxc_rnga_data_read(struct hwrng *rng, u32 * data)
  76{
  77        int err;
  78        u32 ctrl;
  79        void __iomem *rng_base = (void __iomem *)rng->priv;
  80
  81        /* retrieve a random number from FIFO */
  82        *data = __raw_readl(rng_base + RNGA_OUTPUT_FIFO);
  83
  84        /* some error while reading this random number? */
  85        err = __raw_readl(rng_base + RNGA_STATUS) & RNGA_STATUS_ERROR_INT;
  86
  87        /* if error: clear error interrupt, but doesn't return random number */
  88        if (err) {
  89                dev_dbg(&rng_dev->dev, "Error while reading random number!\n");
  90                ctrl = __raw_readl(rng_base + RNGA_CONTROL);
  91                __raw_writel(ctrl | RNGA_CONTROL_CLEAR_INT,
  92                                        rng_base + RNGA_CONTROL);
  93                return 0;
  94        } else
  95                return 4;
  96}
  97
  98static int mxc_rnga_init(struct hwrng *rng)
  99{
 100        u32 ctrl, osc;
 101        void __iomem *rng_base = (void __iomem *)rng->priv;
 102
 103        /* wake up */
 104        ctrl = __raw_readl(rng_base + RNGA_CONTROL);
 105        __raw_writel(ctrl & ~RNGA_CONTROL_SLEEP, rng_base + RNGA_CONTROL);
 106
 107        /* verify if oscillator is working */
 108        osc = __raw_readl(rng_base + RNGA_STATUS);
 109        if (osc & RNGA_STATUS_OSC_DEAD) {
 110                dev_err(&rng_dev->dev, "RNGA Oscillator is dead!\n");
 111                return -ENODEV;
 112        }
 113
 114        /* go running */
 115        ctrl = __raw_readl(rng_base + RNGA_CONTROL);
 116        __raw_writel(ctrl | RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
 117
 118        return 0;
 119}
 120
 121static void mxc_rnga_cleanup(struct hwrng *rng)
 122{
 123        u32 ctrl;
 124        void __iomem *rng_base = (void __iomem *)rng->priv;
 125
 126        ctrl = __raw_readl(rng_base + RNGA_CONTROL);
 127
 128        /* stop rnga */
 129        __raw_writel(ctrl & ~RNGA_CONTROL_GO, rng_base + RNGA_CONTROL);
 130}
 131
 132static struct hwrng mxc_rnga = {
 133        .name = "mxc-rnga",
 134        .init = mxc_rnga_init,
 135        .cleanup = mxc_rnga_cleanup,
 136        .data_present = mxc_rnga_data_present,
 137        .data_read = mxc_rnga_data_read
 138};
 139
 140static int __init mxc_rnga_probe(struct platform_device *pdev)
 141{
 142        int err = -ENODEV;
 143        struct clk *clk;
 144        struct resource *res, *mem;
 145        void __iomem *rng_base = NULL;
 146
 147        if (rng_dev)
 148                return -EBUSY;
 149
 150        clk = clk_get(&pdev->dev, "rng");
 151        if (IS_ERR(clk)) {
 152                dev_err(&pdev->dev, "Could not get rng_clk!\n");
 153                err = PTR_ERR(clk);
 154                goto out;
 155        }
 156
 157        clk_enable(clk);
 158
 159        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 160        if (!res) {
 161                err = -ENOENT;
 162                goto err_region;
 163        }
 164
 165        mem = request_mem_region(res->start, resource_size(res), pdev->name);
 166        if (mem == NULL) {
 167                err = -EBUSY;
 168                goto err_region;
 169        }
 170
 171        rng_base = ioremap(res->start, resource_size(res));
 172        if (!rng_base) {
 173                err = -ENOMEM;
 174                goto err_ioremap;
 175        }
 176
 177        mxc_rnga.priv = (unsigned long)rng_base;
 178
 179        err = hwrng_register(&mxc_rnga);
 180        if (err) {
 181                dev_err(&pdev->dev, "MXC RNGA registering failed (%d)\n", err);
 182                goto err_register;
 183        }
 184
 185        rng_dev = pdev;
 186
 187        dev_info(&pdev->dev, "MXC RNGA Registered.\n");
 188
 189        return 0;
 190
 191err_register:
 192        iounmap(rng_base);
 193        rng_base = NULL;
 194
 195err_ioremap:
 196        release_mem_region(res->start, resource_size(res));
 197
 198err_region:
 199        clk_disable(clk);
 200        clk_put(clk);
 201
 202out:
 203        return err;
 204}
 205
 206static int __exit mxc_rnga_remove(struct platform_device *pdev)
 207{
 208        struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 209        void __iomem *rng_base = (void __iomem *)mxc_rnga.priv;
 210        struct clk *clk = clk_get(&pdev->dev, "rng");
 211
 212        hwrng_unregister(&mxc_rnga);
 213
 214        iounmap(rng_base);
 215
 216        release_mem_region(res->start, resource_size(res));
 217
 218        clk_disable(clk);
 219        clk_put(clk);
 220
 221        return 0;
 222}
 223
 224static struct platform_driver mxc_rnga_driver = {
 225        .driver = {
 226                   .name = "mxc_rnga",
 227                   .owner = THIS_MODULE,
 228                   },
 229        .remove = __exit_p(mxc_rnga_remove),
 230};
 231
 232static int __init mod_init(void)
 233{
 234        return platform_driver_probe(&mxc_rnga_driver, mxc_rnga_probe);
 235}
 236
 237static void __exit mod_exit(void)
 238{
 239        platform_driver_unregister(&mxc_rnga_driver);
 240}
 241
 242module_init(mod_init);
 243module_exit(mod_exit);
 244
 245MODULE_AUTHOR("Freescale Semiconductor, Inc.");
 246MODULE_DESCRIPTION("H/W RNGA driver for i.MX");
 247MODULE_LICENSE("GPL");
 248