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