1 /* 2 * RNG driver for AMD Geode RNGs 3 * 4 * Copyright 2008 Willy Tarreau <w@1wt.eu> 5 * 6 * Inspired by drivers/char/hw_random/geode-rng.c from kernel 2.6.25. 7 * 8 * This file is licensed under the terms of the GNU General Public 9 * License version 2. This program is licensed "as is" without any 10 * warranty of any kind, whether express or implied. 11 */ 12 13 #include <linux/errno.h> 14 #include <linux/kernel.h> 15 #include <linux/module.h> 16 #include <linux/pci.h> 17 #include <linux/random.h> 18 #include <linux/sched.h> 19 #include <linux/timer.h> 20 #include <asm/io.h> 21 22 /* We read the random generator every 50 ms */ 23 #define RNG_INTERVAL (HZ/20+1) 24 25 #define GEODE_RNG_DATA_REG 0x50 26 #define GEODE_RNG_STATUS_REG 0x54 27 28 static struct timer_list timer; 29 static void __iomem *rng_addr; 30 static u32 rng_data[2]; 31 static int nb_data; 32 geode_rng_data_read(void)33static u32 geode_rng_data_read(void) 34 { 35 return readl(rng_addr + GEODE_RNG_DATA_REG); 36 } 37 geode_rng_data_present(void)38static int geode_rng_data_present(void) 39 { 40 return !!(readl(rng_addr + GEODE_RNG_STATUS_REG)); 41 } 42 geode_rng_timer(unsigned long data)43static void geode_rng_timer(unsigned long data) 44 { 45 if (!geode_rng_data_present()) 46 goto out; 47 48 rng_data[nb_data] = geode_rng_data_read(); 49 nb_data++; 50 if (nb_data > 1) { 51 nb_data = 0; 52 /* We have collected 64 bits. Maybe we should reduce the 53 * announced entropy ? At least, check for changing data 54 * and refuse to feed consts. 55 */ 56 if (rng_data[0] != rng_data[1]) 57 batch_entropy_store(rng_data[0], rng_data[1], 64); 58 } 59 out: 60 timer.expires = jiffies + RNG_INTERVAL; 61 add_timer(&timer); 62 } 63 geode_rng_init(void)64static int __init geode_rng_init(void) 65 { 66 struct pci_dev *pdev = NULL; 67 unsigned long rng_base; 68 int err = -ENODEV; 69 70 pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, NULL); 71 72 if (pdev == NULL) { 73 printk(KERN_ERR "geode-rng: AMD Geode RNG device not found\n"); 74 goto out; 75 } 76 77 if ((err = pci_enable_device(pdev))) 78 goto out; 79 80 if ((err = pci_enable_device_bars(pdev, 1))) 81 goto out; 82 83 rng_base = pci_resource_start(pdev, 0); 84 if (rng_base == 0) 85 goto out; 86 87 err = -ENOMEM; 88 rng_addr = ioremap(rng_base, 0x58); 89 if (!rng_addr) 90 goto out; 91 92 printk(KERN_INFO "AMD Geode RNG detected and enabled\n"); 93 94 init_timer(&timer); 95 timer.function = geode_rng_timer; 96 timer.data = 0; 97 timer.expires = jiffies + RNG_INTERVAL; 98 add_timer(&timer); 99 err = 0; 100 out: 101 return err; 102 } 103 geode_rng_exit(void)104static void __exit geode_rng_exit(void) 105 { 106 del_timer_sync(&timer); 107 iounmap(rng_addr); 108 } 109 110 module_init(geode_rng_init); 111 module_exit(geode_rng_exit); 112 113 MODULE_AUTHOR("Willy Tarreau"); 114 MODULE_LICENSE("GPL"); 115