linux/drivers/char/hw_random/tx4939-rng.c
<<
>>
Prefs
   1/*
   2 * RNG driver for TX4939 Random Number Generators (RNG)
   3 *
   4 * Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file "COPYING" in the main directory of this archive
   8 * for more details.
   9 */
  10#include <linux/module.h>
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/delay.h>
  14#include <linux/io.h>
  15#include <linux/platform_device.h>
  16#include <linux/hw_random.h>
  17
  18#define TX4939_RNG_RCSR         0x00000000
  19#define TX4939_RNG_ROR(n)       (0x00000018 + (n) * 8)
  20
  21#define TX4939_RNG_RCSR_INTE    0x00000008
  22#define TX4939_RNG_RCSR_RST     0x00000004
  23#define TX4939_RNG_RCSR_FIN     0x00000002
  24#define TX4939_RNG_RCSR_ST      0x00000001
  25
  26struct tx4939_rng {
  27        struct hwrng rng;
  28        void __iomem *base;
  29        u64 databuf[3];
  30        unsigned int data_avail;
  31};
  32
  33static void rng_io_start(void)
  34{
  35#ifndef CONFIG_64BIT
  36        /*
  37         * readq is reading a 64-bit register using a 64-bit load.  On
  38         * a 32-bit kernel however interrupts or any other processor
  39         * exception would clobber the upper 32-bit of the processor
  40         * register so interrupts need to be disabled.
  41         */
  42        local_irq_disable();
  43#endif
  44}
  45
  46static void rng_io_end(void)
  47{
  48#ifndef CONFIG_64BIT
  49        local_irq_enable();
  50#endif
  51}
  52
  53static u64 read_rng(void __iomem *base, unsigned int offset)
  54{
  55        return ____raw_readq(base + offset);
  56}
  57
  58static void write_rng(u64 val, void __iomem *base, unsigned int offset)
  59{
  60        return ____raw_writeq(val, base + offset);
  61}
  62
  63static int tx4939_rng_data_present(struct hwrng *rng, int wait)
  64{
  65        struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);
  66        int i;
  67
  68        if (rngdev->data_avail)
  69                return rngdev->data_avail;
  70        for (i = 0; i < 20; i++) {
  71                rng_io_start();
  72                if (!(read_rng(rngdev->base, TX4939_RNG_RCSR)
  73                      & TX4939_RNG_RCSR_ST)) {
  74                        rngdev->databuf[0] =
  75                                read_rng(rngdev->base, TX4939_RNG_ROR(0));
  76                        rngdev->databuf[1] =
  77                                read_rng(rngdev->base, TX4939_RNG_ROR(1));
  78                        rngdev->databuf[2] =
  79                                read_rng(rngdev->base, TX4939_RNG_ROR(2));
  80                        rngdev->data_avail =
  81                                sizeof(rngdev->databuf) / sizeof(u32);
  82                        /* Start RNG */
  83                        write_rng(TX4939_RNG_RCSR_ST,
  84                                  rngdev->base, TX4939_RNG_RCSR);
  85                        wait = 0;
  86                }
  87                rng_io_end();
  88                if (!wait)
  89                        break;
  90                /* 90 bus clock cycles by default for generation */
  91                ndelay(90 * 5);
  92        }
  93        return rngdev->data_avail;
  94}
  95
  96static int tx4939_rng_data_read(struct hwrng *rng, u32 *buffer)
  97{
  98        struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);
  99
 100        rngdev->data_avail--;
 101        *buffer = *((u32 *)&rngdev->databuf + rngdev->data_avail);
 102        return sizeof(u32);
 103}
 104
 105static int __init tx4939_rng_probe(struct platform_device *dev)
 106{
 107        struct tx4939_rng *rngdev;
 108        struct resource *r;
 109        int i;
 110
 111        r = platform_get_resource(dev, IORESOURCE_MEM, 0);
 112        if (!r)
 113                return -EBUSY;
 114        rngdev = devm_kzalloc(&dev->dev, sizeof(*rngdev), GFP_KERNEL);
 115        if (!rngdev)
 116                return -ENOMEM;
 117        if (!devm_request_mem_region(&dev->dev, r->start, resource_size(r),
 118                                     dev_name(&dev->dev)))
 119                return -EBUSY;
 120        rngdev->base = devm_ioremap(&dev->dev, r->start, resource_size(r));
 121        if (!rngdev->base)
 122                return -EBUSY;
 123
 124        rngdev->rng.name = dev_name(&dev->dev);
 125        rngdev->rng.data_present = tx4939_rng_data_present;
 126        rngdev->rng.data_read = tx4939_rng_data_read;
 127
 128        rng_io_start();
 129        /* Reset RNG */
 130        write_rng(TX4939_RNG_RCSR_RST, rngdev->base, TX4939_RNG_RCSR);
 131        write_rng(0, rngdev->base, TX4939_RNG_RCSR);
 132        /* Start RNG */
 133        write_rng(TX4939_RNG_RCSR_ST, rngdev->base, TX4939_RNG_RCSR);
 134        rng_io_end();
 135        /*
 136         * Drop first two results.  From the datasheet:
 137         * The quality of the random numbers generated immediately
 138         * after reset can be insufficient.  Therefore, do not use
 139         * random numbers obtained from the first and second
 140         * generations; use the ones from the third or subsequent
 141         * generation.
 142         */
 143        for (i = 0; i < 2; i++) {
 144                rngdev->data_avail = 0;
 145                if (!tx4939_rng_data_present(&rngdev->rng, 1))
 146                        return -EIO;
 147        }
 148
 149        platform_set_drvdata(dev, rngdev);
 150        return hwrng_register(&rngdev->rng);
 151}
 152
 153static int __exit tx4939_rng_remove(struct platform_device *dev)
 154{
 155        struct tx4939_rng *rngdev = platform_get_drvdata(dev);
 156
 157        hwrng_unregister(&rngdev->rng);
 158        platform_set_drvdata(dev, NULL);
 159        return 0;
 160}
 161
 162static struct platform_driver tx4939_rng_driver = {
 163        .driver         = {
 164                .name   = "tx4939-rng",
 165                .owner  = THIS_MODULE,
 166        },
 167        .remove = tx4939_rng_remove,
 168};
 169
 170static int __init tx4939rng_init(void)
 171{
 172        return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe);
 173}
 174
 175static void __exit tx4939rng_exit(void)
 176{
 177        platform_driver_unregister(&tx4939_rng_driver);
 178}
 179
 180module_init(tx4939rng_init);
 181module_exit(tx4939rng_exit);
 182
 183MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");
 184MODULE_LICENSE("GPL");
 185
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.