/* * RNG driver for AMD Geode RNGs * * Copyright 2008 Willy Tarreau * * Inspired by drivers/char/hw_random/geode-rng.c from kernel 2.6.25. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ #include #include #include #include #include #include #include #include /* We read the random generator every 50 ms */ #define RNG_INTERVAL (HZ/20+1) #define GEODE_RNG_DATA_REG 0x50 #define GEODE_RNG_STATUS_REG 0x54 static struct timer_list timer; static void __iomem *rng_addr; static u32 rng_data[2]; static int nb_data; static u32 geode_rng_data_read(void) { return readl(rng_addr + GEODE_RNG_DATA_REG); } static int geode_rng_data_present(void) { return !!(readl(rng_addr + GEODE_RNG_STATUS_REG)); } static void geode_rng_timer(unsigned long data) { if (!geode_rng_data_present()) goto out; rng_data[nb_data] = geode_rng_data_read(); nb_data++; if (nb_data > 1) { nb_data = 0; /* We have collected 64 bits. Maybe we should reduce the * announced entropy ? At least, check for changing data * and refuse to feed consts. */ if (rng_data[0] != rng_data[1]) batch_entropy_store(rng_data[0], rng_data[1], 64); } out: timer.expires = jiffies + RNG_INTERVAL; add_timer(&timer); } static int __init geode_rng_init(void) { struct pci_dev *pdev = NULL; unsigned long rng_base; int err = -ENODEV; pdev = pci_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, NULL); if (pdev == NULL) { printk(KERN_ERR "geode-rng: AMD Geode RNG device not found\n"); goto out; } if ((err = pci_enable_device(pdev))) goto out; if ((err = pci_enable_device_bars(pdev, 1))) goto out; rng_base = pci_resource_start(pdev, 0); if (rng_base == 0) goto out; err = -ENOMEM; rng_addr = ioremap(rng_base, 0x58); if (!rng_addr) goto out; printk(KERN_INFO "AMD Geode RNG detected and enabled\n"); init_timer(&timer); timer.function = geode_rng_timer; timer.data = 0; timer.expires = jiffies + RNG_INTERVAL; add_timer(&timer); err = 0; out: return err; } static void __exit geode_rng_exit(void) { del_timer_sync(&timer); iounmap(rng_addr); } module_init(geode_rng_init); module_exit(geode_rng_exit); MODULE_AUTHOR("Willy Tarreau"); MODULE_LICENSE("GPL");