xref: /linux/drivers/char/hw_random/intel-rng.c (revision 5e8d780d745c1619aba81fe7166c5a4b5cad2b84)
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