xref: /openbmc/linux/drivers/misc/phantom.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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