12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cef2cf07SJiri Slaby /*
3cef2cf07SJiri Slaby * Copyright (C) 2005-2007 Jiri Slaby <jirislaby@gmail.com>
4cef2cf07SJiri Slaby *
50211a9c8SFrederik Schwarzer * You need a userspace library to cooperate with this driver. It (and other
6cef2cf07SJiri Slaby * info) may be obtained here:
7cef2cf07SJiri Slaby * http://www.fi.muni.cz/~xslaby/phantom.html
8b2afe331SJiri Slaby * or alternatively, you might use OpenHaptics provided by Sensable.
9cef2cf07SJiri Slaby */
10cef2cf07SJiri Slaby
117e4e8e68SJiri Slaby #include <linux/compat.h>
12cef2cf07SJiri Slaby #include <linux/kernel.h>
13cef2cf07SJiri Slaby #include <linux/module.h>
14cef2cf07SJiri Slaby #include <linux/device.h>
15cef2cf07SJiri Slaby #include <linux/pci.h>
16cef2cf07SJiri Slaby #include <linux/fs.h>
17cef2cf07SJiri Slaby #include <linux/poll.h>
18cef2cf07SJiri Slaby #include <linux/interrupt.h>
19cef2cf07SJiri Slaby #include <linux/cdev.h>
205a0e3ad6STejun Heo #include <linux/slab.h>
21cef2cf07SJiri Slaby #include <linux/phantom.h>
22d43c36dcSAlexey Dobriyan #include <linux/sched.h>
23613655faSArnd Bergmann #include <linux/mutex.h>
24cef2cf07SJiri Slaby
2560063497SArun Sharma #include <linux/atomic.h>
26cef2cf07SJiri Slaby #include <asm/io.h>
27cef2cf07SJiri Slaby
2882f56087SJiri Slaby #define PHANTOM_VERSION "n0.9.8"
29cef2cf07SJiri Slaby
30cef2cf07SJiri Slaby #define PHANTOM_MAX_MINORS 8
31cef2cf07SJiri Slaby
32cef2cf07SJiri Slaby #define PHN_IRQCTL 0x4c /* irq control in caddr space */
33cef2cf07SJiri Slaby
34cef2cf07SJiri Slaby #define PHB_RUNNING 1
35bc552f77SJiri Slaby #define PHB_NOT_OH 2
36cef2cf07SJiri Slaby
37613655faSArnd Bergmann static DEFINE_MUTEX(phantom_mutex);
38cef2cf07SJiri Slaby static struct class *phantom_class;
39cef2cf07SJiri Slaby static int phantom_major;
40cef2cf07SJiri Slaby
41cef2cf07SJiri Slaby struct phantom_device {
42cef2cf07SJiri Slaby unsigned int opened;
43cef2cf07SJiri Slaby void __iomem *caddr;
44cef2cf07SJiri Slaby u32 __iomem *iaddr;
45cef2cf07SJiri Slaby u32 __iomem *oaddr;
46cef2cf07SJiri Slaby unsigned long status;
47cef2cf07SJiri Slaby atomic_t counter;
48cef2cf07SJiri Slaby
49cef2cf07SJiri Slaby wait_queue_head_t wait;
50cef2cf07SJiri Slaby struct cdev cdev;
51cef2cf07SJiri Slaby
52cef2cf07SJiri Slaby struct mutex open_lock;
53bc552f77SJiri Slaby spinlock_t regs_lock;
54bc552f77SJiri Slaby
55bc552f77SJiri Slaby /* used in NOT_OH mode */
56bc552f77SJiri Slaby struct phm_regs oregs;
57bc552f77SJiri Slaby u32 ctl_reg;
58cef2cf07SJiri Slaby };
59cef2cf07SJiri Slaby
60cef2cf07SJiri Slaby static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
61cef2cf07SJiri Slaby
phantom_status(struct phantom_device * dev,unsigned long newstat)62cef2cf07SJiri Slaby static int phantom_status(struct phantom_device *dev, unsigned long newstat)
63cef2cf07SJiri Slaby {
64cef2cf07SJiri Slaby pr_debug("phantom_status %lx %lx\n", dev->status, newstat);
65cef2cf07SJiri Slaby
66cef2cf07SJiri Slaby if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) {
67cef2cf07SJiri Slaby atomic_set(&dev->counter, 0);
68cef2cf07SJiri Slaby iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL);
69cef2cf07SJiri Slaby iowrite32(0x43, dev->caddr + PHN_IRQCTL);
70c8511f94SJiri Slaby ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
71c8511f94SJiri Slaby } else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) {
72cef2cf07SJiri Slaby iowrite32(0, dev->caddr + PHN_IRQCTL);
73c8511f94SJiri Slaby ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
74c8511f94SJiri Slaby }
75cef2cf07SJiri Slaby
76cef2cf07SJiri Slaby dev->status = newstat;
77cef2cf07SJiri Slaby
78cef2cf07SJiri Slaby return 0;
79cef2cf07SJiri Slaby }
80cef2cf07SJiri Slaby
81cef2cf07SJiri Slaby /*
82cef2cf07SJiri Slaby * File ops
83cef2cf07SJiri Slaby */
84cef2cf07SJiri Slaby
phantom_ioctl(struct file * file,unsigned int cmd,unsigned long arg)85c15395c0SJiri Slaby static long phantom_ioctl(struct file *file, unsigned int cmd,
86c15395c0SJiri Slaby unsigned long arg)
87cef2cf07SJiri Slaby {
88cef2cf07SJiri Slaby struct phantom_device *dev = file->private_data;
89cef2cf07SJiri Slaby struct phm_regs rs;
90cef2cf07SJiri Slaby struct phm_reg r;
91cef2cf07SJiri Slaby void __user *argp = (void __user *)arg;
92bc552f77SJiri Slaby unsigned long flags;
93cef2cf07SJiri Slaby unsigned int i;
94cef2cf07SJiri Slaby
95cef2cf07SJiri Slaby switch (cmd) {
967e4e8e68SJiri Slaby case PHN_SETREG:
97cef2cf07SJiri Slaby case PHN_SET_REG:
98cef2cf07SJiri Slaby if (copy_from_user(&r, argp, sizeof(r)))
99cef2cf07SJiri Slaby return -EFAULT;
100cef2cf07SJiri Slaby
101cef2cf07SJiri Slaby if (r.reg > 7)
102cef2cf07SJiri Slaby return -EINVAL;
103cef2cf07SJiri Slaby
104bc552f77SJiri Slaby spin_lock_irqsave(&dev->regs_lock, flags);
105cef2cf07SJiri Slaby if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
106c15395c0SJiri Slaby phantom_status(dev, dev->status | PHB_RUNNING)){
107bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
108cef2cf07SJiri Slaby return -ENODEV;
109c15395c0SJiri Slaby }
110cef2cf07SJiri Slaby
111cef2cf07SJiri Slaby pr_debug("phantom: writing %x to %u\n", r.value, r.reg);
112bc552f77SJiri Slaby
113bc552f77SJiri Slaby /* preserve amp bit (don't allow to change it when in NOT_OH) */
114bc552f77SJiri Slaby if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) {
115bc552f77SJiri Slaby r.value &= ~PHN_CTL_AMP;
116bc552f77SJiri Slaby r.value |= dev->ctl_reg & PHN_CTL_AMP;
117bc552f77SJiri Slaby dev->ctl_reg = r.value;
118bc552f77SJiri Slaby }
119bc552f77SJiri Slaby
120cef2cf07SJiri Slaby iowrite32(r.value, dev->iaddr + r.reg);
121c8511f94SJiri Slaby ioread32(dev->iaddr); /* PCI posting */
122cef2cf07SJiri Slaby
123cef2cf07SJiri Slaby if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
124cef2cf07SJiri Slaby phantom_status(dev, dev->status & ~PHB_RUNNING);
125bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
126cef2cf07SJiri Slaby break;
1277e4e8e68SJiri Slaby case PHN_SETREGS:
128cef2cf07SJiri Slaby case PHN_SET_REGS:
129cef2cf07SJiri Slaby if (copy_from_user(&rs, argp, sizeof(rs)))
130cef2cf07SJiri Slaby return -EFAULT;
131cef2cf07SJiri Slaby
132cef2cf07SJiri Slaby pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask);
133bc552f77SJiri Slaby spin_lock_irqsave(&dev->regs_lock, flags);
134bc552f77SJiri Slaby if (dev->status & PHB_NOT_OH)
135bc552f77SJiri Slaby memcpy(&dev->oregs, &rs, sizeof(rs));
136bc552f77SJiri Slaby else {
137bc552f77SJiri Slaby u32 m = min(rs.count, 8U);
138bc552f77SJiri Slaby for (i = 0; i < m; i++)
139bc552f77SJiri Slaby if (rs.mask & BIT(i))
140cef2cf07SJiri Slaby iowrite32(rs.values[i], dev->oaddr + i);
141c8511f94SJiri Slaby ioread32(dev->iaddr); /* PCI posting */
142bc552f77SJiri Slaby }
143bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
144cef2cf07SJiri Slaby break;
1457e4e8e68SJiri Slaby case PHN_GETREG:
146cef2cf07SJiri Slaby case PHN_GET_REG:
147cef2cf07SJiri Slaby if (copy_from_user(&r, argp, sizeof(r)))
148cef2cf07SJiri Slaby return -EFAULT;
149cef2cf07SJiri Slaby
150cef2cf07SJiri Slaby if (r.reg > 7)
151cef2cf07SJiri Slaby return -EINVAL;
152cef2cf07SJiri Slaby
153cef2cf07SJiri Slaby r.value = ioread32(dev->iaddr + r.reg);
154cef2cf07SJiri Slaby
155cef2cf07SJiri Slaby if (copy_to_user(argp, &r, sizeof(r)))
156cef2cf07SJiri Slaby return -EFAULT;
157cef2cf07SJiri Slaby break;
1587e4e8e68SJiri Slaby case PHN_GETREGS:
159bc552f77SJiri Slaby case PHN_GET_REGS: {
160bc552f77SJiri Slaby u32 m;
161bc552f77SJiri Slaby
162cef2cf07SJiri Slaby if (copy_from_user(&rs, argp, sizeof(rs)))
163cef2cf07SJiri Slaby return -EFAULT;
164cef2cf07SJiri Slaby
165bc552f77SJiri Slaby m = min(rs.count, 8U);
166bc552f77SJiri Slaby
167cef2cf07SJiri Slaby pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask);
168bc552f77SJiri Slaby spin_lock_irqsave(&dev->regs_lock, flags);
169bc552f77SJiri Slaby for (i = 0; i < m; i++)
170bc552f77SJiri Slaby if (rs.mask & BIT(i))
171cef2cf07SJiri Slaby rs.values[i] = ioread32(dev->iaddr + i);
1727d4f9f09SJiri Slaby atomic_set(&dev->counter, 0);
173bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
174cef2cf07SJiri Slaby
175cef2cf07SJiri Slaby if (copy_to_user(argp, &rs, sizeof(rs)))
176cef2cf07SJiri Slaby return -EFAULT;
177cef2cf07SJiri Slaby break;
178bc552f77SJiri Slaby } case PHN_NOT_OH:
179bc552f77SJiri Slaby spin_lock_irqsave(&dev->regs_lock, flags);
180bc552f77SJiri Slaby if (dev->status & PHB_RUNNING) {
181bc552f77SJiri Slaby printk(KERN_ERR "phantom: you need to set NOT_OH "
182bc552f77SJiri Slaby "before you start the device!\n");
183bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
184bc552f77SJiri Slaby return -EINVAL;
185bc552f77SJiri Slaby }
186bc552f77SJiri Slaby dev->status |= PHB_NOT_OH;
187bc552f77SJiri Slaby spin_unlock_irqrestore(&dev->regs_lock, flags);
188bc552f77SJiri Slaby break;
189cef2cf07SJiri Slaby default:
190cef2cf07SJiri Slaby return -ENOTTY;
191cef2cf07SJiri Slaby }
192cef2cf07SJiri Slaby
193cef2cf07SJiri Slaby return 0;
194cef2cf07SJiri Slaby }
195cef2cf07SJiri Slaby
1967e4e8e68SJiri Slaby #ifdef CONFIG_COMPAT
phantom_compat_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)1977e4e8e68SJiri Slaby static long phantom_compat_ioctl(struct file *filp, unsigned int cmd,
1987e4e8e68SJiri Slaby unsigned long arg)
1997e4e8e68SJiri Slaby {
2007e4e8e68SJiri Slaby if (_IOC_NR(cmd) <= 3 && _IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
2017e4e8e68SJiri Slaby cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
2027e4e8e68SJiri Slaby cmd |= sizeof(void *) << _IOC_SIZESHIFT;
2037e4e8e68SJiri Slaby }
2047e4e8e68SJiri Slaby return phantom_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
2057e4e8e68SJiri Slaby }
2067e4e8e68SJiri Slaby #else
2077e4e8e68SJiri Slaby #define phantom_compat_ioctl NULL
2087e4e8e68SJiri Slaby #endif
2097e4e8e68SJiri Slaby
phantom_open(struct inode * inode,struct file * file)210cef2cf07SJiri Slaby static int phantom_open(struct inode *inode, struct file *file)
211cef2cf07SJiri Slaby {
212cef2cf07SJiri Slaby struct phantom_device *dev = container_of(inode->i_cdev,
213cef2cf07SJiri Slaby struct phantom_device, cdev);
214cef2cf07SJiri Slaby
215613655faSArnd Bergmann mutex_lock(&phantom_mutex);
216cef2cf07SJiri Slaby nonseekable_open(inode, file);
217cef2cf07SJiri Slaby
2184541b5ecSJonathan Corbet if (mutex_lock_interruptible(&dev->open_lock)) {
219613655faSArnd Bergmann mutex_unlock(&phantom_mutex);
220cef2cf07SJiri Slaby return -ERESTARTSYS;
2214541b5ecSJonathan Corbet }
222cef2cf07SJiri Slaby
223cef2cf07SJiri Slaby if (dev->opened) {
224cef2cf07SJiri Slaby mutex_unlock(&dev->open_lock);
225613655faSArnd Bergmann mutex_unlock(&phantom_mutex);
226cef2cf07SJiri Slaby return -EINVAL;
227cef2cf07SJiri Slaby }
228cef2cf07SJiri Slaby
229bc552f77SJiri Slaby WARN_ON(dev->status & PHB_NOT_OH);
230bc552f77SJiri Slaby
231cef2cf07SJiri Slaby file->private_data = dev;
232cef2cf07SJiri Slaby
233bc552f77SJiri Slaby atomic_set(&dev->counter, 0);
234cef2cf07SJiri Slaby dev->opened++;
235cef2cf07SJiri Slaby mutex_unlock(&dev->open_lock);
236613655faSArnd Bergmann mutex_unlock(&phantom_mutex);
237cef2cf07SJiri Slaby return 0;
238cef2cf07SJiri Slaby }
239cef2cf07SJiri Slaby
phantom_release(struct inode * inode,struct file * file)240cef2cf07SJiri Slaby static int phantom_release(struct inode *inode, struct file *file)
241cef2cf07SJiri Slaby {
242cef2cf07SJiri Slaby struct phantom_device *dev = file->private_data;
243cef2cf07SJiri Slaby
244cef2cf07SJiri Slaby mutex_lock(&dev->open_lock);
245cef2cf07SJiri Slaby
246cef2cf07SJiri Slaby dev->opened = 0;
247cef2cf07SJiri Slaby phantom_status(dev, dev->status & ~PHB_RUNNING);
248bc552f77SJiri Slaby dev->status &= ~PHB_NOT_OH;
249cef2cf07SJiri Slaby
250cef2cf07SJiri Slaby mutex_unlock(&dev->open_lock);
251cef2cf07SJiri Slaby
252cef2cf07SJiri Slaby return 0;
253cef2cf07SJiri Slaby }
254cef2cf07SJiri Slaby
phantom_poll(struct file * file,poll_table * wait)255afc9a42bSAl Viro static __poll_t phantom_poll(struct file *file, poll_table *wait)
256cef2cf07SJiri Slaby {
257cef2cf07SJiri Slaby struct phantom_device *dev = file->private_data;
258afc9a42bSAl Viro __poll_t mask = 0;
259cef2cf07SJiri Slaby
260cef2cf07SJiri Slaby pr_debug("phantom_poll: %d\n", atomic_read(&dev->counter));
261cef2cf07SJiri Slaby poll_wait(file, &dev->wait, wait);
2627d4f9f09SJiri Slaby
2637d4f9f09SJiri Slaby if (!(dev->status & PHB_RUNNING))
264a9a08845SLinus Torvalds mask = EPOLLERR;
2657d4f9f09SJiri Slaby else if (atomic_read(&dev->counter))
266a9a08845SLinus Torvalds mask = EPOLLIN | EPOLLRDNORM;
2677d4f9f09SJiri Slaby
268cef2cf07SJiri Slaby pr_debug("phantom_poll end: %x/%d\n", mask, atomic_read(&dev->counter));
269cef2cf07SJiri Slaby
270cef2cf07SJiri Slaby return mask;
271cef2cf07SJiri Slaby }
272cef2cf07SJiri Slaby
273828c0950SAlexey Dobriyan static const struct file_operations phantom_file_ops = {
274cef2cf07SJiri Slaby .open = phantom_open,
275cef2cf07SJiri Slaby .release = phantom_release,
276c15395c0SJiri Slaby .unlocked_ioctl = phantom_ioctl,
2777e4e8e68SJiri Slaby .compat_ioctl = phantom_compat_ioctl,
278cef2cf07SJiri Slaby .poll = phantom_poll,
2796038f373SArnd Bergmann .llseek = no_llseek,
280cef2cf07SJiri Slaby };
281cef2cf07SJiri Slaby
phantom_isr(int irq,void * data)282cef2cf07SJiri Slaby static irqreturn_t phantom_isr(int irq, void *data)
283cef2cf07SJiri Slaby {
284cef2cf07SJiri Slaby struct phantom_device *dev = data;
285bc552f77SJiri Slaby unsigned int i;
286bc552f77SJiri Slaby u32 ctl;
287cef2cf07SJiri Slaby
288bc552f77SJiri Slaby spin_lock(&dev->regs_lock);
289bc552f77SJiri Slaby ctl = ioread32(dev->iaddr + PHN_CONTROL);
290bc552f77SJiri Slaby if (!(ctl & PHN_CTL_IRQ)) {
291bc552f77SJiri Slaby spin_unlock(&dev->regs_lock);
292cef2cf07SJiri Slaby return IRQ_NONE;
293bc552f77SJiri Slaby }
294cef2cf07SJiri Slaby
295cef2cf07SJiri Slaby iowrite32(0, dev->iaddr);
296cef2cf07SJiri Slaby iowrite32(0xc0, dev->iaddr);
297bc552f77SJiri Slaby
298bc552f77SJiri Slaby if (dev->status & PHB_NOT_OH) {
299bc552f77SJiri Slaby struct phm_regs *r = &dev->oregs;
300bc552f77SJiri Slaby u32 m = min(r->count, 8U);
301bc552f77SJiri Slaby
302bc552f77SJiri Slaby for (i = 0; i < m; i++)
303bc552f77SJiri Slaby if (r->mask & BIT(i))
304bc552f77SJiri Slaby iowrite32(r->values[i], dev->oaddr + i);
305bc552f77SJiri Slaby
306bc552f77SJiri Slaby dev->ctl_reg ^= PHN_CTL_AMP;
307bc552f77SJiri Slaby iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL);
308bc552f77SJiri Slaby }
309bc552f77SJiri Slaby spin_unlock(&dev->regs_lock);
310bc552f77SJiri Slaby
311c8511f94SJiri Slaby ioread32(dev->iaddr); /* PCI posting */
312cef2cf07SJiri Slaby
313cef2cf07SJiri Slaby atomic_inc(&dev->counter);
314cef2cf07SJiri Slaby wake_up_interruptible(&dev->wait);
315cef2cf07SJiri Slaby
316cef2cf07SJiri Slaby return IRQ_HANDLED;
317cef2cf07SJiri Slaby }
318cef2cf07SJiri Slaby
319cef2cf07SJiri Slaby /*
320cef2cf07SJiri Slaby * Init and deinit driver
321cef2cf07SJiri Slaby */
322cef2cf07SJiri Slaby
phantom_get_free(void)32380c8ae28SBill Pemberton static unsigned int phantom_get_free(void)
324cef2cf07SJiri Slaby {
325cef2cf07SJiri Slaby unsigned int i;
326cef2cf07SJiri Slaby
327cef2cf07SJiri Slaby for (i = 0; i < PHANTOM_MAX_MINORS; i++)
328cef2cf07SJiri Slaby if (phantom_devices[i] == 0)
329cef2cf07SJiri Slaby break;
330cef2cf07SJiri Slaby
331cef2cf07SJiri Slaby return i;
332cef2cf07SJiri Slaby }
333cef2cf07SJiri Slaby
phantom_probe(struct pci_dev * pdev,const struct pci_device_id * pci_id)33480c8ae28SBill Pemberton static int phantom_probe(struct pci_dev *pdev,
335cef2cf07SJiri Slaby const struct pci_device_id *pci_id)
336cef2cf07SJiri Slaby {
337cef2cf07SJiri Slaby struct phantom_device *pht;
338cef2cf07SJiri Slaby unsigned int minor;
339cef2cf07SJiri Slaby int retval;
340cef2cf07SJiri Slaby
341cef2cf07SJiri Slaby retval = pci_enable_device(pdev);
34210ad5278SRahul Ruikar if (retval) {
34310ad5278SRahul Ruikar dev_err(&pdev->dev, "pci_enable_device failed!\n");
344cef2cf07SJiri Slaby goto err;
34510ad5278SRahul Ruikar }
346cef2cf07SJiri Slaby
347cef2cf07SJiri Slaby minor = phantom_get_free();
348cef2cf07SJiri Slaby if (minor == PHANTOM_MAX_MINORS) {
349cef2cf07SJiri Slaby dev_err(&pdev->dev, "too many devices found!\n");
350cef2cf07SJiri Slaby retval = -EIO;
351cef2cf07SJiri Slaby goto err_dis;
352cef2cf07SJiri Slaby }
353cef2cf07SJiri Slaby
354cef2cf07SJiri Slaby phantom_devices[minor] = 1;
355cef2cf07SJiri Slaby
356cef2cf07SJiri Slaby retval = pci_request_regions(pdev, "phantom");
35710ad5278SRahul Ruikar if (retval) {
35810ad5278SRahul Ruikar dev_err(&pdev->dev, "pci_request_regions failed!\n");
359cef2cf07SJiri Slaby goto err_null;
36010ad5278SRahul Ruikar }
361cef2cf07SJiri Slaby
362cef2cf07SJiri Slaby retval = -ENOMEM;
363cef2cf07SJiri Slaby pht = kzalloc(sizeof(*pht), GFP_KERNEL);
364cef2cf07SJiri Slaby if (pht == NULL) {
365cef2cf07SJiri Slaby dev_err(&pdev->dev, "unable to allocate device\n");
366cef2cf07SJiri Slaby goto err_reg;
367cef2cf07SJiri Slaby }
368cef2cf07SJiri Slaby
369cef2cf07SJiri Slaby pht->caddr = pci_iomap(pdev, 0, 0);
370cef2cf07SJiri Slaby if (pht->caddr == NULL) {
371cef2cf07SJiri Slaby dev_err(&pdev->dev, "can't remap conf space\n");
372cef2cf07SJiri Slaby goto err_fr;
373cef2cf07SJiri Slaby }
374cef2cf07SJiri Slaby pht->iaddr = pci_iomap(pdev, 2, 0);
375cef2cf07SJiri Slaby if (pht->iaddr == NULL) {
376cef2cf07SJiri Slaby dev_err(&pdev->dev, "can't remap input space\n");
377cef2cf07SJiri Slaby goto err_unmc;
378cef2cf07SJiri Slaby }
379cef2cf07SJiri Slaby pht->oaddr = pci_iomap(pdev, 3, 0);
380cef2cf07SJiri Slaby if (pht->oaddr == NULL) {
381cef2cf07SJiri Slaby dev_err(&pdev->dev, "can't remap output space\n");
382cef2cf07SJiri Slaby goto err_unmi;
383cef2cf07SJiri Slaby }
384cef2cf07SJiri Slaby
385cef2cf07SJiri Slaby mutex_init(&pht->open_lock);
386bc552f77SJiri Slaby spin_lock_init(&pht->regs_lock);
387cef2cf07SJiri Slaby init_waitqueue_head(&pht->wait);
388cef2cf07SJiri Slaby cdev_init(&pht->cdev, &phantom_file_ops);
389cef2cf07SJiri Slaby pht->cdev.owner = THIS_MODULE;
390cef2cf07SJiri Slaby
391cef2cf07SJiri Slaby iowrite32(0, pht->caddr + PHN_IRQCTL);
392c8511f94SJiri Slaby ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
393cef2cf07SJiri Slaby retval = request_irq(pdev->irq, phantom_isr,
394bb9da88dSMichael Opdenacker IRQF_SHARED, "phantom", pht);
395cef2cf07SJiri Slaby if (retval) {
396cef2cf07SJiri Slaby dev_err(&pdev->dev, "can't establish ISR\n");
397cef2cf07SJiri Slaby goto err_unmo;
398cef2cf07SJiri Slaby }
399cef2cf07SJiri Slaby
400cef2cf07SJiri Slaby retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
401cef2cf07SJiri Slaby if (retval) {
402cef2cf07SJiri Slaby dev_err(&pdev->dev, "chardev registration failed\n");
403cef2cf07SJiri Slaby goto err_irq;
404cef2cf07SJiri Slaby }
405cef2cf07SJiri Slaby
406a9b12619SGreg Kroah-Hartman if (IS_ERR(device_create(phantom_class, &pdev->dev,
407a9b12619SGreg Kroah-Hartman MKDEV(phantom_major, minor), NULL,
408a9b12619SGreg Kroah-Hartman "phantom%u", minor)))
409cef2cf07SJiri Slaby dev_err(&pdev->dev, "can't create device\n");
410cef2cf07SJiri Slaby
411cef2cf07SJiri Slaby pci_set_drvdata(pdev, pht);
412cef2cf07SJiri Slaby
413cef2cf07SJiri Slaby return 0;
414cef2cf07SJiri Slaby err_irq:
415cef2cf07SJiri Slaby free_irq(pdev->irq, pht);
416cef2cf07SJiri Slaby err_unmo:
417cef2cf07SJiri Slaby pci_iounmap(pdev, pht->oaddr);
418cef2cf07SJiri Slaby err_unmi:
419cef2cf07SJiri Slaby pci_iounmap(pdev, pht->iaddr);
420cef2cf07SJiri Slaby err_unmc:
421cef2cf07SJiri Slaby pci_iounmap(pdev, pht->caddr);
422cef2cf07SJiri Slaby err_fr:
423cef2cf07SJiri Slaby kfree(pht);
424cef2cf07SJiri Slaby err_reg:
425cef2cf07SJiri Slaby pci_release_regions(pdev);
426cef2cf07SJiri Slaby err_null:
427cef2cf07SJiri Slaby phantom_devices[minor] = 0;
428cef2cf07SJiri Slaby err_dis:
429cef2cf07SJiri Slaby pci_disable_device(pdev);
430cef2cf07SJiri Slaby err:
431cef2cf07SJiri Slaby return retval;
432cef2cf07SJiri Slaby }
433cef2cf07SJiri Slaby
phantom_remove(struct pci_dev * pdev)434486a5c28SBill Pemberton static void phantom_remove(struct pci_dev *pdev)
435cef2cf07SJiri Slaby {
436cef2cf07SJiri Slaby struct phantom_device *pht = pci_get_drvdata(pdev);
437cef2cf07SJiri Slaby unsigned int minor = MINOR(pht->cdev.dev);
438cef2cf07SJiri Slaby
439cef2cf07SJiri Slaby device_destroy(phantom_class, MKDEV(phantom_major, minor));
440cef2cf07SJiri Slaby
441cef2cf07SJiri Slaby cdev_del(&pht->cdev);
442cef2cf07SJiri Slaby
443cef2cf07SJiri Slaby iowrite32(0, pht->caddr + PHN_IRQCTL);
444c8511f94SJiri Slaby ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
445cef2cf07SJiri Slaby free_irq(pdev->irq, pht);
446cef2cf07SJiri Slaby
447cef2cf07SJiri Slaby pci_iounmap(pdev, pht->oaddr);
448cef2cf07SJiri Slaby pci_iounmap(pdev, pht->iaddr);
449cef2cf07SJiri Slaby pci_iounmap(pdev, pht->caddr);
450cef2cf07SJiri Slaby
451cef2cf07SJiri Slaby kfree(pht);
452cef2cf07SJiri Slaby
453cef2cf07SJiri Slaby pci_release_regions(pdev);
454cef2cf07SJiri Slaby
455cef2cf07SJiri Slaby phantom_devices[minor] = 0;
456cef2cf07SJiri Slaby
457cef2cf07SJiri Slaby pci_disable_device(pdev);
458cef2cf07SJiri Slaby }
459cef2cf07SJiri Slaby
phantom_suspend(struct device * dev_d)4606bbf5256SVaibhav Gupta static int __maybe_unused phantom_suspend(struct device *dev_d)
461cef2cf07SJiri Slaby {
4626bbf5256SVaibhav Gupta struct phantom_device *dev = dev_get_drvdata(dev_d);
463cef2cf07SJiri Slaby
464cef2cf07SJiri Slaby iowrite32(0, dev->caddr + PHN_IRQCTL);
465c8511f94SJiri Slaby ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
466cef2cf07SJiri Slaby
4676bbf5256SVaibhav Gupta synchronize_irq(to_pci_dev(dev_d)->irq);
468aee8447cSJiri Slaby
469cef2cf07SJiri Slaby return 0;
470cef2cf07SJiri Slaby }
471cef2cf07SJiri Slaby
phantom_resume(struct device * dev_d)4726bbf5256SVaibhav Gupta static int __maybe_unused phantom_resume(struct device *dev_d)
473cef2cf07SJiri Slaby {
4746bbf5256SVaibhav Gupta struct phantom_device *dev = dev_get_drvdata(dev_d);
475cef2cf07SJiri Slaby
476cef2cf07SJiri Slaby iowrite32(0, dev->caddr + PHN_IRQCTL);
477cef2cf07SJiri Slaby
478cef2cf07SJiri Slaby return 0;
479cef2cf07SJiri Slaby }
480cef2cf07SJiri Slaby
4812c685064SBill Pemberton static struct pci_device_id phantom_pci_tbl[] = {
48282f56087SJiri Slaby { .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
48382f56087SJiri Slaby .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
484cef2cf07SJiri Slaby .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
485cef2cf07SJiri Slaby { 0, }
486cef2cf07SJiri Slaby };
487cef2cf07SJiri Slaby MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
488cef2cf07SJiri Slaby
4896bbf5256SVaibhav Gupta static SIMPLE_DEV_PM_OPS(phantom_pm_ops, phantom_suspend, phantom_resume);
4906bbf5256SVaibhav Gupta
491cef2cf07SJiri Slaby static struct pci_driver phantom_pci_driver = {
492cef2cf07SJiri Slaby .name = "phantom",
493cef2cf07SJiri Slaby .id_table = phantom_pci_tbl,
494cef2cf07SJiri Slaby .probe = phantom_probe,
4952d6bed9cSBill Pemberton .remove = phantom_remove,
4966bbf5256SVaibhav Gupta .driver.pm = &phantom_pm_ops,
497cef2cf07SJiri Slaby };
498cef2cf07SJiri Slaby
4990933e2d9SAndi Kleen static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION);
500cef2cf07SJiri Slaby
phantom_init(void)501cef2cf07SJiri Slaby static int __init phantom_init(void)
502cef2cf07SJiri Slaby {
503cef2cf07SJiri Slaby int retval;
504cef2cf07SJiri Slaby dev_t dev;
505cef2cf07SJiri Slaby
506*1aaba11dSGreg Kroah-Hartman phantom_class = class_create("phantom");
507cef2cf07SJiri Slaby if (IS_ERR(phantom_class)) {
508cef2cf07SJiri Slaby retval = PTR_ERR(phantom_class);
509cef2cf07SJiri Slaby printk(KERN_ERR "phantom: can't register phantom class\n");
510cef2cf07SJiri Slaby goto err;
511cef2cf07SJiri Slaby }
5120933e2d9SAndi Kleen retval = class_create_file(phantom_class, &class_attr_version.attr);
513cef2cf07SJiri Slaby if (retval) {
514cef2cf07SJiri Slaby printk(KERN_ERR "phantom: can't create sysfs version file\n");
515cef2cf07SJiri Slaby goto err_class;
516cef2cf07SJiri Slaby }
517cef2cf07SJiri Slaby
518cef2cf07SJiri Slaby retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom");
519cef2cf07SJiri Slaby if (retval) {
520cef2cf07SJiri Slaby printk(KERN_ERR "phantom: can't register character device\n");
521cef2cf07SJiri Slaby goto err_attr;
522cef2cf07SJiri Slaby }
523cef2cf07SJiri Slaby phantom_major = MAJOR(dev);
524cef2cf07SJiri Slaby
525cef2cf07SJiri Slaby retval = pci_register_driver(&phantom_pci_driver);
526cef2cf07SJiri Slaby if (retval) {
527cef2cf07SJiri Slaby printk(KERN_ERR "phantom: can't register pci driver\n");
528cef2cf07SJiri Slaby goto err_unchr;
529cef2cf07SJiri Slaby }
530cef2cf07SJiri Slaby
531cef2cf07SJiri Slaby printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", "
532cef2cf07SJiri Slaby "init OK\n");
533cef2cf07SJiri Slaby
534cef2cf07SJiri Slaby return 0;
535cef2cf07SJiri Slaby err_unchr:
536cef2cf07SJiri Slaby unregister_chrdev_region(dev, PHANTOM_MAX_MINORS);
537cef2cf07SJiri Slaby err_attr:
5380933e2d9SAndi Kleen class_remove_file(phantom_class, &class_attr_version.attr);
539cef2cf07SJiri Slaby err_class:
540cef2cf07SJiri Slaby class_destroy(phantom_class);
541cef2cf07SJiri Slaby err:
542cef2cf07SJiri Slaby return retval;
543cef2cf07SJiri Slaby }
544cef2cf07SJiri Slaby
phantom_exit(void)545cef2cf07SJiri Slaby static void __exit phantom_exit(void)
546cef2cf07SJiri Slaby {
547cef2cf07SJiri Slaby pci_unregister_driver(&phantom_pci_driver);
548cef2cf07SJiri Slaby
549cef2cf07SJiri Slaby unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS);
550cef2cf07SJiri Slaby
5510933e2d9SAndi Kleen class_remove_file(phantom_class, &class_attr_version.attr);
552cef2cf07SJiri Slaby class_destroy(phantom_class);
553cef2cf07SJiri Slaby
554cef2cf07SJiri Slaby pr_debug("phantom: module successfully removed\n");
555cef2cf07SJiri Slaby }
556cef2cf07SJiri Slaby
557cef2cf07SJiri Slaby module_init(phantom_init);
558cef2cf07SJiri Slaby module_exit(phantom_exit);
559cef2cf07SJiri Slaby
560cef2cf07SJiri Slaby MODULE_AUTHOR("Jiri Slaby <jirislaby@gmail.com>");
561ec905a18SJiri Slaby MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
562cef2cf07SJiri Slaby MODULE_LICENSE("GPL");
563cef2cf07SJiri Slaby MODULE_VERSION(PHANTOM_VERSION);
564