aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJiri Slaby <jirislaby@gmail.com>2007-05-08 03:31:45 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:14 -0400
commitcef2cf07273d12ac3453d2baff096423f17b7403 (patch)
tree807ce95b91d1a09f449d61301f1148c85d7818db /drivers
parent6f7f02e78a75a09195d963e0392b195bc2d55c5c (diff)
Misc: add sensable phantom driver
Add sensable phantom driver Signed-off-by: Jiri Slaby <jirislaby@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/phantom.c463
3 files changed, 473 insertions, 0 deletions
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index bfb02c1a45ae..877e7909a0e5 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -25,6 +25,15 @@ config IBM_ASM
25 information on the specific driver level and support statement 25 information on the specific driver level and support statement
26 for your IBM server. 26 for your IBM server.
27 27
28config PHANTOM
29 tristate "Sensable PHANToM"
30 depends on PCI
31 help
32 Say Y here if you want to build a driver for Sensable PHANToM device.
33
34 If you choose to build module, its name will be phantom. If unsure,
35 say N here.
36
28 37
29 If unsure, say N. 38 If unsure, say N.
30 39
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ece6baf76bc7..5b6d46de005c 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_BLINK) += blink.o
11obj-$(CONFIG_LKDTM) += lkdtm.o 11obj-$(CONFIG_LKDTM) += lkdtm.o
12obj-$(CONFIG_TIFM_CORE) += tifm_core.o 12obj-$(CONFIG_TIFM_CORE) += tifm_core.o
13obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o 13obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
14obj-$(CONFIG_PHANTOM) += phantom.o
14obj-$(CONFIG_SGI_IOC4) += ioc4.o 15obj-$(CONFIG_SGI_IOC4) += ioc4.o
15obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o 16obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o
16obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o 17obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o
diff --git a/drivers/misc/phantom.c b/drivers/misc/phantom.c
new file mode 100644
index 000000000000..35b139b0e5f2
--- /dev/null
+++ b/drivers/misc/phantom.c
@@ -0,0 +1,463 @@
1/*
2 * Copyright (C) 2005-2007 Jiri Slaby <jirislaby@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * You need an userspace library to cooperate with this driver. It (and other
10 * info) may be obtained here:
11 * http://www.fi.muni.cz/~xslaby/phantom.html
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/device.h>
17#include <linux/pci.h>
18#include <linux/fs.h>
19#include <linux/poll.h>
20#include <linux/interrupt.h>
21#include <linux/cdev.h>
22#include <linux/phantom.h>
23
24#include <asm/atomic.h>
25#include <asm/io.h>
26
27#define PHANTOM_VERSION "n0.9.5"
28
29#define PHANTOM_MAX_MINORS 8
30
31#define PHN_IRQCTL 0x4c /* irq control in caddr space */
32
33#define PHB_RUNNING 1
34
35static struct class *phantom_class;
36static int phantom_major;
37
38struct phantom_device {
39 unsigned int opened;
40 void __iomem *caddr;
41 u32 __iomem *iaddr;
42 u32 __iomem *oaddr;
43 unsigned long status;
44 atomic_t counter;
45
46 wait_queue_head_t wait;
47 struct cdev cdev;
48
49 struct mutex open_lock;
50};
51
52static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
53
54static int phantom_status(struct phantom_device *dev, unsigned long newstat)
55{
56 pr_debug("phantom_status %lx %lx\n", dev->status, newstat);
57
58 if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) {
59 atomic_set(&dev->counter, 0);
60 iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL);
61 iowrite32(0x43, dev->caddr + PHN_IRQCTL);
62 } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING))
63 iowrite32(0, dev->caddr + PHN_IRQCTL);
64
65 dev->status = newstat;
66
67 return 0;
68}
69
70/*
71 * File ops
72 */
73
74static int phantom_ioctl(struct inode *inode, struct file *file, u_int cmd,
75 u_long arg)
76{
77 struct phantom_device *dev = file->private_data;
78 struct phm_regs rs;
79 struct phm_reg r;
80 void __user *argp = (void __user *)arg;
81 unsigned int i;
82
83 if (_IOC_TYPE(cmd) != PH_IOC_MAGIC ||
84 _IOC_NR(cmd) > PH_IOC_MAXNR)
85 return -ENOTTY;
86
87 switch (cmd) {
88 case PHN_SET_REG:
89 if (copy_from_user(&r, argp, sizeof(r)))
90 return -EFAULT;
91
92 if (r.reg > 7)
93 return -EINVAL;
94
95 if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
96 phantom_status(dev, dev->status | PHB_RUNNING))
97 return -ENODEV;
98
99 pr_debug("phantom: writing %x to %u\n", r.value, r.reg);
100 iowrite32(r.value, dev->iaddr + r.reg);
101
102 if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
103 phantom_status(dev, dev->status & ~PHB_RUNNING);
104 break;
105 case PHN_SET_REGS:
106 if (copy_from_user(&rs, argp, sizeof(rs)))
107 return -EFAULT;
108
109 pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask);
110 for (i = 0; i < min(rs.count, 8U); i++)
111 if ((1 << i) & rs.mask)
112 iowrite32(rs.values[i], dev->oaddr + i);
113 break;
114 case PHN_GET_REG:
115 if (copy_from_user(&r, argp, sizeof(r)))
116 return -EFAULT;
117
118 if (r.reg > 7)
119 return -EINVAL;
120
121 r.value = ioread32(dev->iaddr + r.reg);
122
123 if (copy_to_user(argp, &r, sizeof(r)))
124 return -EFAULT;
125 break;
126 case PHN_GET_REGS:
127 if (copy_from_user(&rs, argp, sizeof(rs)))
128 return -EFAULT;
129
130 pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask);
131 for (i = 0; i < min(rs.count, 8U); i++)
132 if ((1 << i) & rs.mask)
133 rs.values[i] = ioread32(dev->iaddr + i);
134
135 if (copy_to_user(argp, &rs, sizeof(rs)))
136 return -EFAULT;
137 break;
138 default:
139 return -ENOTTY;
140 }
141
142 return 0;
143}
144
145static int phantom_open(struct inode *inode, struct file *file)
146{
147 struct phantom_device *dev = container_of(inode->i_cdev,
148 struct phantom_device, cdev);
149
150 nonseekable_open(inode, file);
151
152 if (mutex_lock_interruptible(&dev->open_lock))
153 return -ERESTARTSYS;
154
155 if (dev->opened) {
156 mutex_unlock(&dev->open_lock);
157 return -EINVAL;
158 }
159
160 file->private_data = dev;
161
162 dev->opened++;
163 mutex_unlock(&dev->open_lock);
164
165 return 0;
166}
167
168static int phantom_release(struct inode *inode, struct file *file)
169{
170 struct phantom_device *dev = file->private_data;
171
172 mutex_lock(&dev->open_lock);
173
174 dev->opened = 0;
175 phantom_status(dev, dev->status & ~PHB_RUNNING);
176
177 mutex_unlock(&dev->open_lock);
178
179 return 0;
180}
181
182static unsigned int phantom_poll(struct file *file, poll_table *wait)
183{
184 struct phantom_device *dev = file->private_data;
185 unsigned int mask = 0;
186
187 pr_debug("phantom_poll: %d\n", atomic_read(&dev->counter));
188 poll_wait(file, &dev->wait, wait);
189 if (atomic_read(&dev->counter)) {
190 mask = POLLIN | POLLRDNORM;
191 atomic_dec(&dev->counter);
192 } else if ((dev->status & PHB_RUNNING) == 0)
193 mask = POLLIN | POLLRDNORM | POLLERR;
194 pr_debug("phantom_poll end: %x/%d\n", mask, atomic_read(&dev->counter));
195
196 return mask;
197}
198
199static struct file_operations phantom_file_ops = {
200 .open = phantom_open,
201 .release = phantom_release,
202 .ioctl = phantom_ioctl,
203 .poll = phantom_poll,
204};
205
206static irqreturn_t phantom_isr(int irq, void *data)
207{
208 struct phantom_device *dev = data;
209
210 if (!(ioread32(dev->iaddr + PHN_CONTROL) & PHN_CTL_IRQ))
211 return IRQ_NONE;
212
213 iowrite32(0, dev->iaddr);
214 iowrite32(0xc0, dev->iaddr);
215
216 atomic_inc(&dev->counter);
217 wake_up_interruptible(&dev->wait);
218
219 return IRQ_HANDLED;
220}
221
222/*
223 * Init and deinit driver
224 */
225
226static unsigned int __devinit phantom_get_free(void)
227{
228 unsigned int i;
229
230 for (i = 0; i < PHANTOM_MAX_MINORS; i++)
231 if (phantom_devices[i] == 0)
232 break;
233
234 return i;
235}
236
237static int __devinit phantom_probe(struct pci_dev *pdev,
238 const struct pci_device_id *pci_id)
239{
240 struct phantom_device *pht;
241 unsigned int minor;
242 int retval;
243
244 retval = pci_enable_device(pdev);
245 if (retval)
246 goto err;
247
248 minor = phantom_get_free();
249 if (minor == PHANTOM_MAX_MINORS) {
250 dev_err(&pdev->dev, "too many devices found!\n");
251 retval = -EIO;
252 goto err_dis;
253 }
254
255 phantom_devices[minor] = 1;
256
257 retval = pci_request_regions(pdev, "phantom");
258 if (retval)
259 goto err_null;
260
261 retval = -ENOMEM;
262 pht = kzalloc(sizeof(*pht), GFP_KERNEL);
263 if (pht == NULL) {
264 dev_err(&pdev->dev, "unable to allocate device\n");
265 goto err_reg;
266 }
267
268 pht->caddr = pci_iomap(pdev, 0, 0);
269 if (pht->caddr == NULL) {
270 dev_err(&pdev->dev, "can't remap conf space\n");
271 goto err_fr;
272 }
273 pht->iaddr = pci_iomap(pdev, 2, 0);
274 if (pht->iaddr == NULL) {
275 dev_err(&pdev->dev, "can't remap input space\n");
276 goto err_unmc;
277 }
278 pht->oaddr = pci_iomap(pdev, 3, 0);
279 if (pht->oaddr == NULL) {
280 dev_err(&pdev->dev, "can't remap output space\n");
281 goto err_unmi;
282 }
283
284 mutex_init(&pht->open_lock);
285 init_waitqueue_head(&pht->wait);
286 cdev_init(&pht->cdev, &phantom_file_ops);
287 pht->cdev.owner = THIS_MODULE;
288
289 iowrite32(0, pht->caddr + PHN_IRQCTL);
290 retval = request_irq(pdev->irq, phantom_isr,
291 IRQF_SHARED | IRQF_DISABLED, "phantom", pht);
292 if (retval) {
293 dev_err(&pdev->dev, "can't establish ISR\n");
294 goto err_unmo;
295 }
296
297 retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
298 if (retval) {
299 dev_err(&pdev->dev, "chardev registration failed\n");
300 goto err_irq;
301 }
302
303 if (IS_ERR(device_create(phantom_class, &pdev->dev, MKDEV(phantom_major,
304 minor), "phantom%u", minor)))
305 dev_err(&pdev->dev, "can't create device\n");
306
307 pci_set_drvdata(pdev, pht);
308
309 return 0;
310err_irq:
311 free_irq(pdev->irq, pht);
312err_unmo:
313 pci_iounmap(pdev, pht->oaddr);
314err_unmi:
315 pci_iounmap(pdev, pht->iaddr);
316err_unmc:
317 pci_iounmap(pdev, pht->caddr);
318err_fr:
319 kfree(pht);
320err_reg:
321 pci_release_regions(pdev);
322err_null:
323 phantom_devices[minor] = 0;
324err_dis:
325 pci_disable_device(pdev);
326err:
327 return retval;
328}
329
330static void __devexit phantom_remove(struct pci_dev *pdev)
331{
332 struct phantom_device *pht = pci_get_drvdata(pdev);
333 unsigned int minor = MINOR(pht->cdev.dev);
334
335 device_destroy(phantom_class, MKDEV(phantom_major, minor));
336
337 cdev_del(&pht->cdev);
338
339 iowrite32(0, pht->caddr + PHN_IRQCTL);
340 free_irq(pdev->irq, pht);
341
342 pci_iounmap(pdev, pht->oaddr);
343 pci_iounmap(pdev, pht->iaddr);
344 pci_iounmap(pdev, pht->caddr);
345
346 kfree(pht);
347
348 pci_release_regions(pdev);
349
350 phantom_devices[minor] = 0;
351
352 pci_disable_device(pdev);
353}
354
355#ifdef CONFIG_PM
356static int phantom_suspend(struct pci_dev *pdev, pm_message_t state)
357{
358 struct phantom_device *dev = pci_get_drvdata(pdev);
359
360 iowrite32(0, dev->caddr + PHN_IRQCTL);
361
362 return 0;
363}
364
365static int phantom_resume(struct pci_dev *pdev)
366{
367 struct phantom_device *dev = pci_get_drvdata(pdev);
368
369 iowrite32(0, dev->caddr + PHN_IRQCTL);
370
371 return 0;
372}
373#else
374#define phantom_suspend NULL
375#define phantom_resume NULL
376#endif
377
378static struct pci_device_id phantom_pci_tbl[] __devinitdata = {
379 { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050),
380 .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
381 { 0, }
382};
383MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
384
385static struct pci_driver phantom_pci_driver = {
386 .name = "phantom",
387 .id_table = phantom_pci_tbl,
388 .probe = phantom_probe,
389 .remove = __devexit_p(phantom_remove),
390 .suspend = phantom_suspend,
391 .resume = phantom_resume
392};
393
394static ssize_t phantom_show_version(struct class *cls, char *buf)
395{
396 return sprintf(buf, PHANTOM_VERSION "\n");
397}
398
399static CLASS_ATTR(version, 0444, phantom_show_version, NULL);
400
401static int __init phantom_init(void)
402{
403 int retval;
404 dev_t dev;
405
406 phantom_class = class_create(THIS_MODULE, "phantom");
407 if (IS_ERR(phantom_class)) {
408 retval = PTR_ERR(phantom_class);
409 printk(KERN_ERR "phantom: can't register phantom class\n");
410 goto err;
411 }
412 retval = class_create_file(phantom_class, &class_attr_version);
413 if (retval) {
414 printk(KERN_ERR "phantom: can't create sysfs version file\n");
415 goto err_class;
416 }
417
418 retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom");
419 if (retval) {
420 printk(KERN_ERR "phantom: can't register character device\n");
421 goto err_attr;
422 }
423 phantom_major = MAJOR(dev);
424
425 retval = pci_register_driver(&phantom_pci_driver);
426 if (retval) {
427 printk(KERN_ERR "phantom: can't register pci driver\n");
428 goto err_unchr;
429 }
430
431 printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", "
432 "init OK\n");
433
434 return 0;
435err_unchr:
436 unregister_chrdev_region(dev, PHANTOM_MAX_MINORS);
437err_attr:
438 class_remove_file(phantom_class, &class_attr_version);
439err_class:
440 class_destroy(phantom_class);
441err:
442 return retval;
443}
444
445static void __exit phantom_exit(void)
446{
447 pci_unregister_driver(&phantom_pci_driver);
448
449 unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS);
450
451 class_remove_file(phantom_class, &class_attr_version);
452 class_destroy(phantom_class);
453
454 pr_debug("phantom: module successfully removed\n");
455}
456
457module_init(phantom_init);
458module_exit(phantom_exit);
459
460MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
461MODULE_DESCRIPTION("Sensable Phantom driver");
462MODULE_LICENSE("GPL");
463MODULE_VERSION(PHANTOM_VERSION);