Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwrng: timeriomem - New driver

Some hardware platforms, the TS-7800[1] is one for example, can
supply the kernel with an entropy source, albeit a slow one for
TS-7800 users, by just reading a particular IO address. This
source must not be read above a certain rate otherwise the quality
suffers.

The driver is then hooked into by calling
platform_device_(register|add|del) passing a structure similar to:
------
static struct timeriomem_rng_data ts78xx_ts_rng_data = {
.address = (u32 *__iomem) TS_RNG,
.period = 1000000, /* one second */
};

static struct platform_device ts78xx_ts_rng_device = {
.name = "timeriomem_rng",
.id = -1,
.dev = {
.platform_data = &ts78xx_ts_rng_data,
},
.num_resources = 0,
};
------

[1] http://www.embeddedarm.com/products/board-detail.php?product=TS-7800

Signed-off-by: Alexander Clouter <alex@digriz.org.uk>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

authored by

Alexander Clouter and committed by
Herbert Xu
9c3c133b 0a2e821d

+187
+14
drivers/char/hw_random/Kconfig
··· 20 20 21 21 If unsure, say Y. 22 22 23 + config HW_RANDOM_TIMERIOMEM 24 + tristate "Timer IOMEM HW Random Number Generator support" 25 + depends on HW_RANDOM 26 + ---help--- 27 + This driver provides kernel-side support for a generic Random 28 + Number Generator used by reading a 'dumb' iomem address that 29 + is to be read no faster than, for example, once a second; 30 + the default FPGA bitstream on the TS-7800 has such functionality. 31 + 32 + To compile this driver as a module, choose M here: the 33 + module will be called timeriomem-rng. 34 + 35 + If unsure, say Y. 36 + 23 37 config HW_RANDOM_INTEL 24 38 tristate "Intel HW Random Number Generator support" 25 39 depends on HW_RANDOM && (X86 || IA64) && PCI
+1
drivers/char/hw_random/Makefile
··· 4 4 5 5 obj-$(CONFIG_HW_RANDOM) += rng-core.o 6 6 rng-core-y := core.o 7 + obj-$(CONFIG_HW_RANDOM_TIMERIOMEM) += timeriomem-rng.o 7 8 obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o 8 9 obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o 9 10 obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
+151
drivers/char/hw_random/timeriomem-rng.c
··· 1 + /* 2 + * drivers/char/hw_random/timeriomem-rng.c 3 + * 4 + * Copyright (C) 2009 Alexander Clouter <alex@digriz.org.uk> 5 + * 6 + * Derived from drivers/char/hw_random/omap-rng.c 7 + * Copyright 2005 (c) MontaVista Software, Inc. 8 + * Author: Deepak Saxena <dsaxena@plexity.net> 9 + * 10 + * This program is free software; you can redistribute it and/or modify 11 + * it under the terms of the GNU General Public License version 2 as 12 + * published by the Free Software Foundation. 13 + * 14 + * Overview: 15 + * This driver is useful for platforms that have an IO range that provides 16 + * periodic random data from a single IO memory address. All the platform 17 + * has to do is provide the address and 'wait time' that new data becomes 18 + * available. 19 + * 20 + * TODO: add support for reading sizes other than 32bits and masking 21 + */ 22 + 23 + #include <linux/module.h> 24 + #include <linux/kernel.h> 25 + #include <linux/platform_device.h> 26 + #include <linux/hw_random.h> 27 + #include <linux/io.h> 28 + #include <linux/timeriomem-rng.h> 29 + #include <linux/jiffies.h> 30 + #include <linux/sched.h> 31 + #include <linux/timer.h> 32 + #include <linux/completion.h> 33 + 34 + static struct timeriomem_rng_data *timeriomem_rng_data; 35 + 36 + static void timeriomem_rng_trigger(unsigned long); 37 + static DEFINE_TIMER(timeriomem_rng_timer, timeriomem_rng_trigger, 0, 0); 38 + 39 + /* 40 + * have data return 1, however return 0 if we have nothing 41 + */ 42 + static int timeriomem_rng_data_present(struct hwrng *rng, int wait) 43 + { 44 + if (rng->priv == 0) 45 + return 1; 46 + 47 + if (!wait || timeriomem_rng_data->present) 48 + return timeriomem_rng_data->present; 49 + 50 + wait_for_completion(&timeriomem_rng_data->completion); 51 + 52 + return 1; 53 + } 54 + 55 + static int timeriomem_rng_data_read(struct hwrng *rng, u32 *data) 56 + { 57 + unsigned long cur; 58 + s32 delay; 59 + 60 + *data = readl(timeriomem_rng_data->address); 61 + 62 + if (rng->priv != 0) { 63 + cur = jiffies; 64 + 65 + delay = cur - timeriomem_rng_timer.expires; 66 + delay = rng->priv - (delay % rng->priv); 67 + 68 + timeriomem_rng_timer.expires = cur + delay; 69 + timeriomem_rng_data->present = 0; 70 + 71 + init_completion(&timeriomem_rng_data->completion); 72 + add_timer(&timeriomem_rng_timer); 73 + } 74 + 75 + return 4; 76 + } 77 + 78 + static void timeriomem_rng_trigger(unsigned long dummy) 79 + { 80 + timeriomem_rng_data->present = 1; 81 + complete(&timeriomem_rng_data->completion); 82 + } 83 + 84 + static struct hwrng timeriomem_rng_ops = { 85 + .name = "timeriomem", 86 + .data_present = timeriomem_rng_data_present, 87 + .data_read = timeriomem_rng_data_read, 88 + .priv = 0, 89 + }; 90 + 91 + static int __init timeriomem_rng_probe(struct platform_device *pdev) 92 + { 93 + int ret; 94 + 95 + timeriomem_rng_data = pdev->dev.platform_data; 96 + 97 + if (timeriomem_rng_data->period != 0 98 + && usecs_to_jiffies(timeriomem_rng_data->period) > 0) { 99 + timeriomem_rng_timer.expires = jiffies; 100 + 101 + timeriomem_rng_ops.priv = usecs_to_jiffies( 102 + timeriomem_rng_data->period); 103 + } 104 + timeriomem_rng_data->present = 1; 105 + 106 + ret = hwrng_register(&timeriomem_rng_ops); 107 + if (ret) { 108 + dev_err(&pdev->dev, "problem registering\n"); 109 + return ret; 110 + } 111 + 112 + dev_info(&pdev->dev, "32bits from 0x%p @ %dus\n", 113 + timeriomem_rng_data->address, 114 + timeriomem_rng_data->period); 115 + 116 + return 0; 117 + } 118 + 119 + static int __devexit timeriomem_rng_remove(struct platform_device *pdev) 120 + { 121 + del_timer_sync(&timeriomem_rng_timer); 122 + hwrng_unregister(&timeriomem_rng_ops); 123 + 124 + return 0; 125 + } 126 + 127 + static struct platform_driver timeriomem_rng_driver = { 128 + .driver = { 129 + .name = "timeriomem_rng", 130 + .owner = THIS_MODULE, 131 + }, 132 + .probe = timeriomem_rng_probe, 133 + .remove = __devexit_p(timeriomem_rng_remove), 134 + }; 135 + 136 + static int __init timeriomem_rng_init(void) 137 + { 138 + return platform_driver_register(&timeriomem_rng_driver); 139 + } 140 + 141 + static void __exit timeriomem_rng_exit(void) 142 + { 143 + platform_driver_unregister(&timeriomem_rng_driver); 144 + } 145 + 146 + module_init(timeriomem_rng_init); 147 + module_exit(timeriomem_rng_exit); 148 + 149 + MODULE_LICENSE("GPL"); 150 + MODULE_AUTHOR("Alexander Clouter <alex@digriz.org.uk>"); 151 + MODULE_DESCRIPTION("Timer IOMEM H/W RNG driver");
+21
include/linux/timeriomem-rng.h
··· 1 + /* 2 + * linux/include/linux/timeriomem-rng.h 3 + * 4 + * Copyright (c) 2009 Alexander Clouter <alex@digriz.org.uk> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/completion.h> 12 + 13 + struct timeriomem_rng_data { 14 + struct completion completion; 15 + unsigned int present:1; 16 + 17 + u32 __iomem *address; 18 + 19 + /* measures in usecs */ 20 + unsigned int period; 21 + };