1 /*
2 Hardware driver for the AMD 768/8111 Random Number Generator (RNG)
3 (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
4 (c) Copyright 2002,2003 Andi Kleen, SuSE Labs
5
6 derived from
7
8 Hardware driver for Intel i810 Random Number Generator (RNG)
9 Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
10 Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
11
12 Please read Documentation/i810_rng.txt for details on use.
13
14 ----------------------------------------------------------
15 This software may be used and distributed according to the terms
16 of the GNU General Public License, incorporated herein by reference.
17
18 */
19
20
21 #include <linux/module.h>
22 #include <linux/kernel.h>
23 #include <linux/fs.h>
24 #include <linux/init.h>
25 #include <linux/pci.h>
26 #include <linux/interrupt.h>
27 #include <linux/spinlock.h>
28 #include <linux/random.h>
29 #include <linux/miscdevice.h>
30 #include <linux/smp_lock.h>
31 #include <linux/mm.h>
32 #include <linux/delay.h>
33
34 #include <asm/io.h>
35 #include <asm/uaccess.h>
36
37
38 /*
39 * core module and version information
40 */
41 #define RNG_VERSION "0.1.0"
42 #define RNG_MODULE_NAME "amd768_rng"
43 #define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
44 #define PFX RNG_MODULE_NAME ": "
45
46
47 /*
48 * debugging macros
49 */
50 #undef RNG_DEBUG /* define to enable copious debugging info */
51
52 #ifdef RNG_DEBUG
53 /* note: prints function name for you */
54 #define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
55 #else
56 #define DPRINTK(fmt, args...)
57 #endif
58
59 #undef RNG_NDEBUG /* define to disable lightweight runtime checks */
60 #ifdef RNG_NDEBUG
61 #define assert(expr)
62 #else
63 #define assert(expr) \
64 if(!(expr)) { \
65 printk( "Assertion failed! %s,%s,%s,line=%d\n", \
66 #expr,__FILE__,__FUNCTION__,__LINE__); \
67 }
68 #endif
69
70 #define RNG_MISCDEV_MINOR 183 /* official */
71
72 /*
73 * various RNG status variables. they are globals
74 * as we only support a single RNG device
75 */
76
77 static u32 pmbase; /* PMxx I/O base */
78 static struct semaphore rng_open_sem; /* Semaphore for serializing rng_open/release */
79
80
81 /*
82 * inlined helper functions for accessing RNG registers
83 */
84
rng_data_present(void)85 static inline int rng_data_present (void)
86 {
87 return inl(pmbase+0xF4) & 1;
88 }
89
90
rng_data_read(void)91 static inline int rng_data_read (void)
92 {
93 return inl(pmbase+0xF0);
94 }
95
rng_dev_open(struct inode * inode,struct file * filp)96 static int rng_dev_open (struct inode *inode, struct file *filp)
97 {
98 if ((filp->f_mode & FMODE_READ) == 0)
99 return -EINVAL;
100 if (filp->f_mode & FMODE_WRITE)
101 return -EINVAL;
102
103 /* wait for device to become free */
104 if (filp->f_flags & O_NONBLOCK) {
105 if (down_trylock (&rng_open_sem))
106 return -EAGAIN;
107 } else {
108 if (down_interruptible (&rng_open_sem))
109 return -ERESTARTSYS;
110 }
111 return 0;
112 }
113
114
rng_dev_release(struct inode * inode,struct file * filp)115 static int rng_dev_release (struct inode *inode, struct file *filp)
116 {
117 up(&rng_open_sem);
118 return 0;
119 }
120
121
rng_dev_read(struct file * filp,char * buf,size_t size,loff_t * offp)122 static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
123 loff_t * offp)
124 {
125 static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
126 int have_data;
127 u32 data = 0;
128 ssize_t ret = 0;
129
130 while (size) {
131 spin_lock(&rng_lock);
132
133 have_data = 0;
134 if (rng_data_present()) {
135 data = rng_data_read();
136 have_data = 4;
137 }
138
139 spin_unlock (&rng_lock);
140
141 while (have_data > 0) {
142 if (put_user((u8)data, buf++)) {
143 ret = ret ? : -EFAULT;
144 break;
145 }
146 size--;
147 ret++;
148 have_data--;
149 data>>=8;
150 }
151
152 if (filp->f_flags & O_NONBLOCK)
153 return ret ? : -EAGAIN;
154
155 if(current->need_resched)
156 {
157 current->state = TASK_INTERRUPTIBLE;
158 schedule_timeout(1);
159 }
160 else
161 udelay(200); /* FIXME: We could poll for 250uS ?? */
162
163 if (signal_pending (current))
164 return ret ? : -ERESTARTSYS;
165 }
166 return ret;
167 }
168
169
170 static struct file_operations rng_chrdev_ops = {
171 owner: THIS_MODULE,
172 open: rng_dev_open,
173 release: rng_dev_release,
174 read: rng_dev_read,
175 };
176
177
178 static struct miscdevice rng_miscdev = {
179 RNG_MISCDEV_MINOR,
180 RNG_MODULE_NAME,
181 &rng_chrdev_ops,
182 };
183
184
185 /*
186 * rng_init_one - look for and attempt to init a single RNG
187 */
rng_init_one(struct pci_dev * dev)188 static int __init rng_init_one (struct pci_dev *dev)
189 {
190 int rc;
191 u8 rnen;
192
193 DPRINTK ("ENTER\n");
194
195 rc = misc_register (&rng_miscdev);
196 if (rc) {
197 printk (KERN_ERR PFX "cannot register misc device\n");
198 DPRINTK ("EXIT, returning %d\n", rc);
199 goto err_out;
200 }
201
202 pci_read_config_dword(dev, 0x58, &pmbase);
203
204 pmbase&=0x0000FF00;
205
206 if(pmbase == 0)
207 {
208 printk (KERN_ERR PFX "power management base not set\n");
209 DPRINTK ("EXIT, returning %d\n", rc);
210 goto err_out_free_miscdev;
211 }
212
213 pci_read_config_byte(dev, 0x40, &rnen);
214 rnen|=(1<<7); /* RNG on */
215 pci_write_config_byte(dev, 0x40, rnen);
216
217 pci_read_config_byte(dev, 0x41, &rnen);
218 rnen|=(1<<7); /* PMIO enable */
219 pci_write_config_byte(dev, 0x41, rnen);
220
221 printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase);
222 DPRINTK ("EXIT, returning 0\n");
223 return 0;
224
225 err_out_free_miscdev:
226 misc_deregister (&rng_miscdev);
227 err_out:
228 return rc;
229 }
230
231
232 /*
233 * Data for PCI driver interface
234 *
235 * This data only exists for exporting the supported
236 * PCI ids via MODULE_DEVICE_TABLE. We do not actually
237 * register a pci_driver, because someone else might one day
238 * want to register another driver on the same PCI id.
239 */
240 static struct pci_device_id rng_pci_tbl[] __initdata = {
241 { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, },
242 { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, },
243 { 0, },
244 };
245 MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
246
247
248 MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek, Andi Kleen");
249 MODULE_DESCRIPTION("AMD 768/8111 Random Number Generator (RNG) driver");
250 MODULE_LICENSE("GPL");
251
252
253 /*
254 * rng_init - initialize RNG module
255 */
rng_init(void)256 static int __init rng_init (void)
257 {
258 int rc;
259 struct pci_dev *pdev;
260
261 DPRINTK ("ENTER\n");
262
263 init_MUTEX (&rng_open_sem);
264
265 pci_for_each_dev(pdev) {
266 if (pci_match_device (rng_pci_tbl, pdev) != NULL)
267 goto match;
268 }
269
270 DPRINTK ("EXIT, returning -ENODEV\n");
271 return -ENODEV;
272
273 match:
274 rc = rng_init_one (pdev);
275 if (rc)
276 return rc;
277
278 printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
279
280 DPRINTK ("EXIT, returning 0\n");
281 return 0;
282 }
283
284
285 /*
286 * rng_init - shutdown RNG module
287 */
rng_cleanup(void)288 static void __exit rng_cleanup (void)
289 {
290 DPRINTK ("ENTER\n");
291 misc_deregister (&rng_miscdev);
292 DPRINTK ("EXIT\n");
293 }
294
295
296 module_init (rng_init);
297 module_exit (rng_cleanup);
298