1 /* 2 * RNG driver for Intel RNGs 3 * 4 * Copyright 2005 (c) MontaVista Software, Inc. 5 * 6 * with the majority of the code coming from: 7 * 8 * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) 9 * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com> 10 * 11 * derived from 12 * 13 * Hardware driver for the AMD 768 Random Number Generator (RNG) 14 * (c) Copyright 2001 Red Hat Inc <alan@redhat.com> 15 * 16 * derived from 17 * 18 * Hardware driver for Intel i810 Random Number Generator (RNG) 19 * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com> 20 * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com> 21 * 22 * This file is licensed under the terms of the GNU General Public 23 * License version 2. This program is licensed "as is" without any 24 * warranty of any kind, whether express or implied. 25 */ 26 27 #include <linux/module.h> 28 #include <linux/kernel.h> 29 #include <linux/pci.h> 30 #include <linux/hw_random.h> 31 #include <asm/io.h> 32 33 34 #define PFX KBUILD_MODNAME ": " 35 36 /* 37 * RNG registers 38 */ 39 #define INTEL_RNG_HW_STATUS 0 40 #define INTEL_RNG_PRESENT 0x40 41 #define INTEL_RNG_ENABLED 0x01 42 #define INTEL_RNG_STATUS 1 43 #define INTEL_RNG_DATA_PRESENT 0x01 44 #define INTEL_RNG_DATA 2 45 46 /* 47 * Magic address at which Intel PCI bridges locate the RNG 48 */ 49 #define INTEL_RNG_ADDR 0xFFBC015F 50 #define INTEL_RNG_ADDR_LEN 3 51 52 /* 53 * Data for PCI driver interface 54 * 55 * This data only exists for exporting the supported 56 * PCI ids via MODULE_DEVICE_TABLE. We do not actually 57 * register a pci_driver, because someone else might one day 58 * want to register another driver on the same PCI id. 59 */ 60 static const struct pci_device_id pci_tbl[] = { 61 { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 62 { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 63 { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 64 { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 65 { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 66 { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, 67 { 0, }, /* terminate list */ 68 }; 69 MODULE_DEVICE_TABLE(pci, pci_tbl); 70 71 72 static inline u8 hwstatus_get(void __iomem *mem) 73 { 74 return readb(mem + INTEL_RNG_HW_STATUS); 75 } 76 77 static inline u8 hwstatus_set(void __iomem *mem, 78 u8 hw_status) 79 { 80 writeb(hw_status, mem + INTEL_RNG_HW_STATUS); 81 return hwstatus_get(mem); 82 } 83 84 static int intel_rng_data_present(struct hwrng *rng) 85 { 86 void __iomem *mem = (void __iomem *)rng->priv; 87 88 return !!(readb(mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT); 89 } 90 91 static int intel_rng_data_read(struct hwrng *rng, u32 *data) 92 { 93 void __iomem *mem = (void __iomem *)rng->priv; 94 95 *data = readb(mem + INTEL_RNG_DATA); 96 97 return 1; 98 } 99 100 static int intel_rng_init(struct hwrng *rng) 101 { 102 void __iomem *mem = (void __iomem *)rng->priv; 103 u8 hw_status; 104 int err = -EIO; 105 106 hw_status = hwstatus_get(mem); 107 /* turn RNG h/w on, if it's off */ 108 if ((hw_status & INTEL_RNG_ENABLED) == 0) 109 hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED); 110 if ((hw_status & INTEL_RNG_ENABLED) == 0) { 111 printk(KERN_ERR PFX "cannot enable RNG, aborting\n"); 112 goto out; 113 } 114 err = 0; 115 out: 116 return err; 117 } 118 119 static void intel_rng_cleanup(struct hwrng *rng) 120 { 121 void __iomem *mem = (void __iomem *)rng->priv; 122 u8 hw_status; 123 124 hw_status = hwstatus_get(mem); 125 if (hw_status & INTEL_RNG_ENABLED) 126 hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED); 127 else 128 printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); 129 } 130 131 132 static struct hwrng intel_rng = { 133 .name = "intel", 134 .init = intel_rng_init, 135 .cleanup = intel_rng_cleanup, 136 .data_present = intel_rng_data_present, 137 .data_read = intel_rng_data_read, 138 }; 139 140 141 static int __init mod_init(void) 142 { 143 int err = -ENODEV; 144 void __iomem *mem; 145 u8 hw_status; 146 147 if (!pci_dev_present(pci_tbl)) 148 goto out; /* Device not found. */ 149 150 err = -ENOMEM; 151 mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); 152 if (!mem) 153 goto out; 154 intel_rng.priv = (unsigned long)mem; 155 156 /* Check for Intel 82802 */ 157 err = -ENODEV; 158 hw_status = hwstatus_get(mem); 159 if ((hw_status & INTEL_RNG_PRESENT) == 0) 160 goto err_unmap; 161 162 printk(KERN_INFO "Intel 82802 RNG detected\n"); 163 err = hwrng_register(&intel_rng); 164 if (err) { 165 printk(KERN_ERR PFX "RNG registering failed (%d)\n", 166 err); 167 goto out; 168 } 169 out: 170 return err; 171 172 err_unmap: 173 iounmap(mem); 174 goto out; 175 } 176 177 static void __exit mod_exit(void) 178 { 179 void __iomem *mem = (void __iomem *)intel_rng.priv; 180 181 hwrng_unregister(&intel_rng); 182 iounmap(mem); 183 } 184 185 subsys_initcall(mod_init); 186 module_exit(mod_exit); 187 188 MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets"); 189 MODULE_LICENSE("GPL"); 190