1/*
2
3	Hardware driver for Intel i810 Random Number Generator (RNG)
4	Copyright 2000,2001 Jeff Garzik <jgarzik@mandrakesoft.com>
5	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
6
7	Driver Web site:  http://sourceforge.net/projects/gkernel/
8
9	Please read Documentation/i810_rng.txt for details on use.
10
11	----------------------------------------------------------
12
13	This software may be used and distributed according to the terms
14        of the GNU General Public License, incorporated herein by reference.
15
16 */
17
18
19#include <linux/module.h>
20#include <linux/kernel.h>
21#include <linux/fs.h>
22#include <linux/init.h>
23#include <linux/pci.h>
24#include <linux/interrupt.h>
25#include <linux/spinlock.h>
26#include <linux/random.h>
27#include <linux/miscdevice.h>
28#include <linux/smp_lock.h>
29#include <linux/mm.h>
30
31#include <asm/io.h>
32#include <asm/uaccess.h>
33
34
35/*
36 * core module and version information
37 */
38#define RNG_VERSION "0.9.8"
39#define RNG_MODULE_NAME "i810_rng"
40#define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
41#define PFX RNG_MODULE_NAME ": "
42
43
44/*
45 * debugging macros
46 */
47#undef RNG_DEBUG /* define to enable copious debugging info */
48
49#ifdef RNG_DEBUG
50/* note: prints function name for you */
51#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
52#else
53#define DPRINTK(fmt, args...)
54#endif
55
56#undef RNG_NDEBUG        /* define to disable lightweight runtime checks */
57#ifdef RNG_NDEBUG
58#define assert(expr)
59#else
60#define assert(expr) \
61        if(!(expr)) {                                   \
62        printk( "Assertion failed! %s,%s,%s,line=%d\n", \
63        #expr,__FILE__,__FUNCTION__,__LINE__);          \
64        }
65#endif
66
67
68/*
69 * RNG registers (offsets from rng_mem)
70 */
71#define RNG_HW_STATUS			0
72#define		RNG_PRESENT		0x40
73#define		RNG_ENABLED		0x01
74#define RNG_STATUS			1
75#define		RNG_DATA_PRESENT	0x01
76#define RNG_DATA			2
77
78/*
79 * Magic address at which Intel PCI bridges locate the RNG
80 */
81#define RNG_ADDR			0xFFBC015F
82#define RNG_ADDR_LEN			3
83
84#define RNG_MISCDEV_MINOR		183 /* official */
85
86/*
87 * various RNG status variables.  they are globals
88 * as we only support a single RNG device
89 */
90static void *rng_mem;			/* token to our ioremap'd RNG register area */
91static struct semaphore rng_open_sem;	/* Semaphore for serializing rng_open/release */
92
93
94/*
95 * inlined helper functions for accessing RNG registers
96 */
97static inline u8 rng_hwstatus (void)
98{
99	assert (rng_mem != NULL);
100	return readb (rng_mem + RNG_HW_STATUS);
101}
102
103static inline u8 rng_hwstatus_set (u8 hw_status)
104{
105	assert (rng_mem != NULL);
106	writeb (hw_status, rng_mem + RNG_HW_STATUS);
107	return rng_hwstatus ();
108}
109
110
111static inline int rng_data_present (void)
112{
113	assert (rng_mem != NULL);
114
115	return (readb (rng_mem + RNG_STATUS) & RNG_DATA_PRESENT) ? 1 : 0;
116}
117
118
119static inline int rng_data_read (void)
120{
121	assert (rng_mem != NULL);
122
123	return readb (rng_mem + RNG_DATA);
124}
125
126/*
127 * rng_enable - enable the RNG hardware
128 */
129
130static int rng_enable (void)
131{
132	int rc = 0;
133	u8 hw_status, new_status;
134
135	DPRINTK ("ENTER\n");
136
137	hw_status = rng_hwstatus ();
138
139	if ((hw_status & RNG_ENABLED) == 0) {
140		new_status = rng_hwstatus_set (hw_status | RNG_ENABLED);
141
142		if (new_status & RNG_ENABLED)
143			printk (KERN_INFO PFX "RNG h/w enabled\n");
144		else {
145			printk (KERN_ERR PFX "Unable to enable the RNG\n");
146			rc = -EIO;
147		}
148	}
149
150	DPRINTK ("EXIT, returning %d\n", rc);
151	return rc;
152}
153
154/*
155 * rng_disable - disable the RNG hardware
156 */
157
158static void rng_disable(void)
159{
160	u8 hw_status, new_status;
161
162	DPRINTK ("ENTER\n");
163
164	hw_status = rng_hwstatus ();
165
166	if (hw_status & RNG_ENABLED) {
167		new_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
168
169		if ((new_status & RNG_ENABLED) == 0)
170			printk (KERN_INFO PFX "RNG h/w disabled\n");
171		else {
172			printk (KERN_ERR PFX "Unable to disable the RNG\n");
173		}
174	}
175
176	DPRINTK ("EXIT\n");
177}
178
179static int rng_dev_open (struct inode *inode, struct file *filp)
180{
181	int rc;
182
183	if ((filp->f_mode & FMODE_READ) == 0)
184		return -EINVAL;
185	if (filp->f_mode & FMODE_WRITE)
186		return -EINVAL;
187
188	/* wait for device to become free */
189	if (filp->f_flags & O_NONBLOCK) {
190		if (down_trylock (&rng_open_sem))
191			return -EAGAIN;
192	} else {
193		if (down_interruptible (&rng_open_sem))
194			return -ERESTARTSYS;
195	}
196
197	rc = rng_enable ();
198	if (rc) {
199		up (&rng_open_sem);
200		return rc;
201	}
202
203	return 0;
204}
205
206
207static int rng_dev_release (struct inode *inode, struct file *filp)
208{
209	rng_disable ();
210	up (&rng_open_sem);
211	return 0;
212}
213
214
215static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
216			     loff_t * offp)
217{
218	static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
219	int have_data;
220	u8 data = 0;
221	ssize_t ret = 0;
222
223	while (size) {
224		spin_lock (&rng_lock);
225
226		have_data = 0;
227		if (rng_data_present ()) {
228			data = rng_data_read ();
229			have_data = 1;
230		}
231
232		spin_unlock (&rng_lock);
233
234		if (have_data) {
235			if (put_user (data, buf++)) {
236				ret = ret ? : -EFAULT;
237				break;
238			}
239			size--;
240			ret++;
241		}
242
243		if (filp->f_flags & O_NONBLOCK)
244			return ret ? : -EAGAIN;
245
246		current->state = TASK_INTERRUPTIBLE;
247		schedule_timeout(1);
248
249		if (signal_pending (current))
250			return ret ? : -ERESTARTSYS;
251	}
252
253	return ret;
254}
255
256
257static struct file_operations rng_chrdev_ops = {
258	owner:		THIS_MODULE,
259	open:		rng_dev_open,
260	release:	rng_dev_release,
261	read:		rng_dev_read,
262};
263
264
265static struct miscdevice rng_miscdev = {
266	RNG_MISCDEV_MINOR,
267	RNG_MODULE_NAME,
268	&rng_chrdev_ops,
269};
270
271
272/*
273 * rng_init_one - look for and attempt to init a single RNG
274 */
275static int __init rng_init_one (struct pci_dev *dev)
276{
277	int rc;
278	u8 hw_status;
279
280	DPRINTK ("ENTER\n");
281
282	rc = misc_register (&rng_miscdev);
283	if (rc) {
284		printk (KERN_ERR PFX "cannot register misc device\n");
285		DPRINTK ("EXIT, returning %d\n", rc);
286		goto err_out;
287	}
288
289	rng_mem = ioremap (RNG_ADDR, RNG_ADDR_LEN);
290	if (rng_mem == NULL) {
291		printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
292		DPRINTK ("EXIT, returning -EBUSY\n");
293		rc = -EBUSY;
294		goto err_out_free_miscdev;
295	}
296
297	/* Check for Intel 82802 */
298	hw_status = rng_hwstatus ();
299	if ((hw_status & RNG_PRESENT) == 0) {
300		printk (KERN_ERR PFX "RNG not detected\n");
301		DPRINTK ("EXIT, returning -ENODEV\n");
302		rc = -ENODEV;
303		goto err_out_free_map;
304	}
305
306	/* turn RNG h/w off, if it's on */
307	if (hw_status & RNG_ENABLED)
308		hw_status = rng_hwstatus_set (hw_status & ~RNG_ENABLED);
309	if (hw_status & RNG_ENABLED) {
310		printk (KERN_ERR PFX "cannot disable RNG, aborting\n");
311		goto err_out_free_map;
312	}
313
314	DPRINTK ("EXIT, returning 0\n");
315	return 0;
316
317err_out_free_map:
318	iounmap (rng_mem);
319err_out_free_miscdev:
320	misc_deregister (&rng_miscdev);
321err_out:
322	return rc;
323}
324
325
326/*
327 * Data for PCI driver interface
328 *
329 * This data only exists for exporting the supported
330 * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
331 * register a pci_driver, because someone else might one day
332 * want to register another driver on the same PCI id.
333 */
334static struct pci_device_id rng_pci_tbl[] __initdata = {
335	{ 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, },
336	{ 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, },
337	{ 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, },
338	{ 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, },
339	{ 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, },
340	{ 0, },
341};
342MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
343
344
345MODULE_AUTHOR("Jeff Garzik, Philipp Rumpf, Matt Sottek");
346MODULE_DESCRIPTION("Intel i8xx chipset Random Number Generator (RNG) driver");
347MODULE_LICENSE("GPL");
348
349
350/*
351 * rng_init - initialize RNG module
352 */
353static int __init rng_init (void)
354{
355	int rc;
356	struct pci_dev *pdev;
357
358	DPRINTK ("ENTER\n");
359
360	init_MUTEX (&rng_open_sem);
361
362	pci_for_each_dev(pdev) {
363		if (pci_match_device (rng_pci_tbl, pdev) != NULL)
364			goto match;
365	}
366
367	DPRINTK ("EXIT, returning -ENODEV\n");
368	return -ENODEV;
369
370match:
371	rc = rng_init_one (pdev);
372	if (rc)
373		return rc;
374
375	printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
376
377	DPRINTK ("EXIT, returning 0\n");
378	return 0;
379}
380
381
382/*
383 * rng_init - shutdown RNG module
384 */
385static void __exit rng_cleanup (void)
386{
387	DPRINTK ("ENTER\n");
388
389	misc_deregister (&rng_miscdev);
390
391	iounmap (rng_mem);
392
393	DPRINTK ("EXIT\n");
394}
395
396
397module_init (rng_init);
398module_exit (rng_cleanup);
399