Path: blob/master/drivers/char/hw_random/tx4939-rng.c
15111 views
/*1* RNG driver for TX4939 Random Number Generators (RNG)2*3* Copyright (C) 2009 Atsushi Nemoto <[email protected]>4*5* This file is subject to the terms and conditions of the GNU General Public6* License. See the file "COPYING" in the main directory of this archive7* for more details.8*/9#include <linux/module.h>10#include <linux/kernel.h>11#include <linux/init.h>12#include <linux/delay.h>13#include <linux/io.h>14#include <linux/platform_device.h>15#include <linux/hw_random.h>16#include <linux/gfp.h>1718#define TX4939_RNG_RCSR 0x0000000019#define TX4939_RNG_ROR(n) (0x00000018 + (n) * 8)2021#define TX4939_RNG_RCSR_INTE 0x0000000822#define TX4939_RNG_RCSR_RST 0x0000000423#define TX4939_RNG_RCSR_FIN 0x0000000224#define TX4939_RNG_RCSR_ST 0x000000012526struct tx4939_rng {27struct hwrng rng;28void __iomem *base;29u64 databuf[3];30unsigned int data_avail;31};3233static void rng_io_start(void)34{35#ifndef CONFIG_64BIT36/*37* readq is reading a 64-bit register using a 64-bit load. On38* a 32-bit kernel however interrupts or any other processor39* exception would clobber the upper 32-bit of the processor40* register so interrupts need to be disabled.41*/42local_irq_disable();43#endif44}4546static void rng_io_end(void)47{48#ifndef CONFIG_64BIT49local_irq_enable();50#endif51}5253static u64 read_rng(void __iomem *base, unsigned int offset)54{55return ____raw_readq(base + offset);56}5758static void write_rng(u64 val, void __iomem *base, unsigned int offset)59{60return ____raw_writeq(val, base + offset);61}6263static int tx4939_rng_data_present(struct hwrng *rng, int wait)64{65struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);66int i;6768if (rngdev->data_avail)69return rngdev->data_avail;70for (i = 0; i < 20; i++) {71rng_io_start();72if (!(read_rng(rngdev->base, TX4939_RNG_RCSR)73& TX4939_RNG_RCSR_ST)) {74rngdev->databuf[0] =75read_rng(rngdev->base, TX4939_RNG_ROR(0));76rngdev->databuf[1] =77read_rng(rngdev->base, TX4939_RNG_ROR(1));78rngdev->databuf[2] =79read_rng(rngdev->base, TX4939_RNG_ROR(2));80rngdev->data_avail =81sizeof(rngdev->databuf) / sizeof(u32);82/* Start RNG */83write_rng(TX4939_RNG_RCSR_ST,84rngdev->base, TX4939_RNG_RCSR);85wait = 0;86}87rng_io_end();88if (!wait)89break;90/* 90 bus clock cycles by default for generation */91ndelay(90 * 5);92}93return rngdev->data_avail;94}9596static int tx4939_rng_data_read(struct hwrng *rng, u32 *buffer)97{98struct tx4939_rng *rngdev = container_of(rng, struct tx4939_rng, rng);99100rngdev->data_avail--;101*buffer = *((u32 *)&rngdev->databuf + rngdev->data_avail);102return sizeof(u32);103}104105static int __init tx4939_rng_probe(struct platform_device *dev)106{107struct tx4939_rng *rngdev;108struct resource *r;109int i;110111r = platform_get_resource(dev, IORESOURCE_MEM, 0);112if (!r)113return -EBUSY;114rngdev = devm_kzalloc(&dev->dev, sizeof(*rngdev), GFP_KERNEL);115if (!rngdev)116return -ENOMEM;117if (!devm_request_mem_region(&dev->dev, r->start, resource_size(r),118dev_name(&dev->dev)))119return -EBUSY;120rngdev->base = devm_ioremap(&dev->dev, r->start, resource_size(r));121if (!rngdev->base)122return -EBUSY;123124rngdev->rng.name = dev_name(&dev->dev);125rngdev->rng.data_present = tx4939_rng_data_present;126rngdev->rng.data_read = tx4939_rng_data_read;127128rng_io_start();129/* Reset RNG */130write_rng(TX4939_RNG_RCSR_RST, rngdev->base, TX4939_RNG_RCSR);131write_rng(0, rngdev->base, TX4939_RNG_RCSR);132/* Start RNG */133write_rng(TX4939_RNG_RCSR_ST, rngdev->base, TX4939_RNG_RCSR);134rng_io_end();135/*136* Drop first two results. From the datasheet:137* The quality of the random numbers generated immediately138* after reset can be insufficient. Therefore, do not use139* random numbers obtained from the first and second140* generations; use the ones from the third or subsequent141* generation.142*/143for (i = 0; i < 2; i++) {144rngdev->data_avail = 0;145if (!tx4939_rng_data_present(&rngdev->rng, 1))146return -EIO;147}148149platform_set_drvdata(dev, rngdev);150return hwrng_register(&rngdev->rng);151}152153static int __exit tx4939_rng_remove(struct platform_device *dev)154{155struct tx4939_rng *rngdev = platform_get_drvdata(dev);156157hwrng_unregister(&rngdev->rng);158platform_set_drvdata(dev, NULL);159return 0;160}161162static struct platform_driver tx4939_rng_driver = {163.driver = {164.name = "tx4939-rng",165.owner = THIS_MODULE,166},167.remove = tx4939_rng_remove,168};169170static int __init tx4939rng_init(void)171{172return platform_driver_probe(&tx4939_rng_driver, tx4939_rng_probe);173}174175static void __exit tx4939rng_exit(void)176{177platform_driver_unregister(&tx4939_rng_driver);178}179180module_init(tx4939rng_init);181module_exit(tx4939rng_exit);182183MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for TX4939");184MODULE_LICENSE("GPL");185186187