xref: /linux-6.15/drivers/misc/phantom.c (revision cb787f4a)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2cef2cf07SJiri Slaby /*
3cef2cf07SJiri Slaby  *  Copyright (C) 2005-2007 Jiri Slaby <[email protected]>
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 int phantom_major;
39cef2cf07SJiri Slaby 
40*f6c086efSGreg Kroah-Hartman static const struct class phantom_class = {
41*f6c086efSGreg Kroah-Hartman 	.name = "phantom",
42*f6c086efSGreg Kroah-Hartman };
43*f6c086efSGreg Kroah-Hartman 
44cef2cf07SJiri Slaby struct phantom_device {
45cef2cf07SJiri Slaby 	unsigned int opened;
46cef2cf07SJiri Slaby 	void __iomem *caddr;
47cef2cf07SJiri Slaby 	u32 __iomem *iaddr;
48cef2cf07SJiri Slaby 	u32 __iomem *oaddr;
49cef2cf07SJiri Slaby 	unsigned long status;
50cef2cf07SJiri Slaby 	atomic_t counter;
51cef2cf07SJiri Slaby 
52cef2cf07SJiri Slaby 	wait_queue_head_t wait;
53cef2cf07SJiri Slaby 	struct cdev cdev;
54cef2cf07SJiri Slaby 
55cef2cf07SJiri Slaby 	struct mutex open_lock;
56bc552f77SJiri Slaby 	spinlock_t regs_lock;
57bc552f77SJiri Slaby 
58bc552f77SJiri Slaby 	/* used in NOT_OH mode */
59bc552f77SJiri Slaby 	struct phm_regs oregs;
60bc552f77SJiri Slaby 	u32 ctl_reg;
61cef2cf07SJiri Slaby };
62cef2cf07SJiri Slaby 
63cef2cf07SJiri Slaby static unsigned char phantom_devices[PHANTOM_MAX_MINORS];
64cef2cf07SJiri Slaby 
phantom_status(struct phantom_device * dev,unsigned long newstat)65cef2cf07SJiri Slaby static int phantom_status(struct phantom_device *dev, unsigned long newstat)
66cef2cf07SJiri Slaby {
67cef2cf07SJiri Slaby 	pr_debug("phantom_status %lx %lx\n", dev->status, newstat);
68cef2cf07SJiri Slaby 
69cef2cf07SJiri Slaby 	if (!(dev->status & PHB_RUNNING) && (newstat & PHB_RUNNING)) {
70cef2cf07SJiri Slaby 		atomic_set(&dev->counter, 0);
71cef2cf07SJiri Slaby 		iowrite32(PHN_CTL_IRQ, dev->iaddr + PHN_CONTROL);
72cef2cf07SJiri Slaby 		iowrite32(0x43, dev->caddr + PHN_IRQCTL);
73c8511f94SJiri Slaby 		ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
74c8511f94SJiri Slaby 	} else if ((dev->status & PHB_RUNNING) && !(newstat & PHB_RUNNING)) {
75cef2cf07SJiri Slaby 		iowrite32(0, dev->caddr + PHN_IRQCTL);
76c8511f94SJiri Slaby 		ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
77c8511f94SJiri Slaby 	}
78cef2cf07SJiri Slaby 
79cef2cf07SJiri Slaby 	dev->status = newstat;
80cef2cf07SJiri Slaby 
81cef2cf07SJiri Slaby 	return 0;
82cef2cf07SJiri Slaby }
83cef2cf07SJiri Slaby 
84cef2cf07SJiri Slaby /*
85cef2cf07SJiri Slaby  * File ops
86cef2cf07SJiri Slaby  */
87cef2cf07SJiri Slaby 
phantom_ioctl(struct file * file,unsigned int cmd,unsigned long arg)88c15395c0SJiri Slaby static long phantom_ioctl(struct file *file, unsigned int cmd,
89c15395c0SJiri Slaby 		unsigned long arg)
90cef2cf07SJiri Slaby {
91cef2cf07SJiri Slaby 	struct phantom_device *dev = file->private_data;
92cef2cf07SJiri Slaby 	struct phm_regs rs;
93cef2cf07SJiri Slaby 	struct phm_reg r;
94cef2cf07SJiri Slaby 	void __user *argp = (void __user *)arg;
95bc552f77SJiri Slaby 	unsigned long flags;
96cef2cf07SJiri Slaby 	unsigned int i;
97cef2cf07SJiri Slaby 
98cef2cf07SJiri Slaby 	switch (cmd) {
997e4e8e68SJiri Slaby 	case PHN_SETREG:
100cef2cf07SJiri Slaby 	case PHN_SET_REG:
101cef2cf07SJiri Slaby 		if (copy_from_user(&r, argp, sizeof(r)))
102cef2cf07SJiri Slaby 			return -EFAULT;
103cef2cf07SJiri Slaby 
104cef2cf07SJiri Slaby 		if (r.reg > 7)
105cef2cf07SJiri Slaby 			return -EINVAL;
106cef2cf07SJiri Slaby 
107bc552f77SJiri Slaby 		spin_lock_irqsave(&dev->regs_lock, flags);
108cef2cf07SJiri Slaby 		if (r.reg == PHN_CONTROL && (r.value & PHN_CTL_IRQ) &&
109c15395c0SJiri Slaby 				phantom_status(dev, dev->status | PHB_RUNNING)){
110bc552f77SJiri Slaby 			spin_unlock_irqrestore(&dev->regs_lock, flags);
111cef2cf07SJiri Slaby 			return -ENODEV;
112c15395c0SJiri Slaby 		}
113cef2cf07SJiri Slaby 
114cef2cf07SJiri Slaby 		pr_debug("phantom: writing %x to %u\n", r.value, r.reg);
115bc552f77SJiri Slaby 
116bc552f77SJiri Slaby 		/* preserve amp bit (don't allow to change it when in NOT_OH) */
117bc552f77SJiri Slaby 		if (r.reg == PHN_CONTROL && (dev->status & PHB_NOT_OH)) {
118bc552f77SJiri Slaby 			r.value &= ~PHN_CTL_AMP;
119bc552f77SJiri Slaby 			r.value |= dev->ctl_reg & PHN_CTL_AMP;
120bc552f77SJiri Slaby 			dev->ctl_reg = r.value;
121bc552f77SJiri Slaby 		}
122bc552f77SJiri Slaby 
123cef2cf07SJiri Slaby 		iowrite32(r.value, dev->iaddr + r.reg);
124c8511f94SJiri Slaby 		ioread32(dev->iaddr); /* PCI posting */
125cef2cf07SJiri Slaby 
126cef2cf07SJiri Slaby 		if (r.reg == PHN_CONTROL && !(r.value & PHN_CTL_IRQ))
127cef2cf07SJiri Slaby 			phantom_status(dev, dev->status & ~PHB_RUNNING);
128bc552f77SJiri Slaby 		spin_unlock_irqrestore(&dev->regs_lock, flags);
129cef2cf07SJiri Slaby 		break;
1307e4e8e68SJiri Slaby 	case PHN_SETREGS:
131cef2cf07SJiri Slaby 	case PHN_SET_REGS:
132cef2cf07SJiri Slaby 		if (copy_from_user(&rs, argp, sizeof(rs)))
133cef2cf07SJiri Slaby 			return -EFAULT;
134cef2cf07SJiri Slaby 
135cef2cf07SJiri Slaby 		pr_debug("phantom: SRS %u regs %x\n", rs.count, rs.mask);
136bc552f77SJiri Slaby 		spin_lock_irqsave(&dev->regs_lock, flags);
137bc552f77SJiri Slaby 		if (dev->status & PHB_NOT_OH)
138bc552f77SJiri Slaby 			memcpy(&dev->oregs, &rs, sizeof(rs));
139bc552f77SJiri Slaby 		else {
140bc552f77SJiri Slaby 			u32 m = min(rs.count, 8U);
141bc552f77SJiri Slaby 			for (i = 0; i < m; i++)
142bc552f77SJiri Slaby 				if (rs.mask & BIT(i))
143cef2cf07SJiri Slaby 					iowrite32(rs.values[i], dev->oaddr + i);
144c8511f94SJiri Slaby 			ioread32(dev->iaddr); /* PCI posting */
145bc552f77SJiri Slaby 		}
146bc552f77SJiri Slaby 		spin_unlock_irqrestore(&dev->regs_lock, flags);
147cef2cf07SJiri Slaby 		break;
1487e4e8e68SJiri Slaby 	case PHN_GETREG:
149cef2cf07SJiri Slaby 	case PHN_GET_REG:
150cef2cf07SJiri Slaby 		if (copy_from_user(&r, argp, sizeof(r)))
151cef2cf07SJiri Slaby 			return -EFAULT;
152cef2cf07SJiri Slaby 
153cef2cf07SJiri Slaby 		if (r.reg > 7)
154cef2cf07SJiri Slaby 			return -EINVAL;
155cef2cf07SJiri Slaby 
156cef2cf07SJiri Slaby 		r.value = ioread32(dev->iaddr + r.reg);
157cef2cf07SJiri Slaby 
158cef2cf07SJiri Slaby 		if (copy_to_user(argp, &r, sizeof(r)))
159cef2cf07SJiri Slaby 			return -EFAULT;
160cef2cf07SJiri Slaby 		break;
1617e4e8e68SJiri Slaby 	case PHN_GETREGS:
162bc552f77SJiri Slaby 	case PHN_GET_REGS: {
163bc552f77SJiri Slaby 		u32 m;
164bc552f77SJiri Slaby 
165cef2cf07SJiri Slaby 		if (copy_from_user(&rs, argp, sizeof(rs)))
166cef2cf07SJiri Slaby 			return -EFAULT;
167cef2cf07SJiri Slaby 
168bc552f77SJiri Slaby 		m = min(rs.count, 8U);
169bc552f77SJiri Slaby 
170cef2cf07SJiri Slaby 		pr_debug("phantom: GRS %u regs %x\n", rs.count, rs.mask);
171bc552f77SJiri Slaby 		spin_lock_irqsave(&dev->regs_lock, flags);
172bc552f77SJiri Slaby 		for (i = 0; i < m; i++)
173bc552f77SJiri Slaby 			if (rs.mask & BIT(i))
174cef2cf07SJiri Slaby 				rs.values[i] = ioread32(dev->iaddr + i);
1757d4f9f09SJiri Slaby 		atomic_set(&dev->counter, 0);
176bc552f77SJiri Slaby 		spin_unlock_irqrestore(&dev->regs_lock, flags);
177cef2cf07SJiri Slaby 
178cef2cf07SJiri Slaby 		if (copy_to_user(argp, &rs, sizeof(rs)))
179cef2cf07SJiri Slaby 			return -EFAULT;
180cef2cf07SJiri Slaby 		break;
181bc552f77SJiri Slaby 	} case PHN_NOT_OH:
182bc552f77SJiri Slaby 		spin_lock_irqsave(&dev->regs_lock, flags);
183bc552f77SJiri Slaby 		if (dev->status & PHB_RUNNING) {
184bc552f77SJiri Slaby 			printk(KERN_ERR "phantom: you need to set NOT_OH "
185bc552f77SJiri Slaby 					"before you start the device!\n");
186bc552f77SJiri Slaby 			spin_unlock_irqrestore(&dev->regs_lock, flags);
187bc552f77SJiri Slaby 			return -EINVAL;
188bc552f77SJiri Slaby 		}
189bc552f77SJiri Slaby 		dev->status |= PHB_NOT_OH;
190bc552f77SJiri Slaby 		spin_unlock_irqrestore(&dev->regs_lock, flags);
191bc552f77SJiri Slaby 		break;
192cef2cf07SJiri Slaby 	default:
193cef2cf07SJiri Slaby 		return -ENOTTY;
194cef2cf07SJiri Slaby 	}
195cef2cf07SJiri Slaby 
196cef2cf07SJiri Slaby 	return 0;
197cef2cf07SJiri Slaby }
198cef2cf07SJiri Slaby 
1997e4e8e68SJiri Slaby #ifdef CONFIG_COMPAT
phantom_compat_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)2007e4e8e68SJiri Slaby static long phantom_compat_ioctl(struct file *filp, unsigned int cmd,
2017e4e8e68SJiri Slaby 		unsigned long arg)
2027e4e8e68SJiri Slaby {
2037e4e8e68SJiri Slaby 	if (_IOC_NR(cmd) <= 3 && _IOC_SIZE(cmd) == sizeof(compat_uptr_t)) {
2047e4e8e68SJiri Slaby 		cmd &= ~(_IOC_SIZEMASK << _IOC_SIZESHIFT);
2057e4e8e68SJiri Slaby 		cmd |= sizeof(void *) << _IOC_SIZESHIFT;
2067e4e8e68SJiri Slaby 	}
2077e4e8e68SJiri Slaby 	return phantom_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
2087e4e8e68SJiri Slaby }
2097e4e8e68SJiri Slaby #else
2107e4e8e68SJiri Slaby #define phantom_compat_ioctl NULL
2117e4e8e68SJiri Slaby #endif
2127e4e8e68SJiri Slaby 
phantom_open(struct inode * inode,struct file * file)213cef2cf07SJiri Slaby static int phantom_open(struct inode *inode, struct file *file)
214cef2cf07SJiri Slaby {
215cef2cf07SJiri Slaby 	struct phantom_device *dev = container_of(inode->i_cdev,
216cef2cf07SJiri Slaby 			struct phantom_device, cdev);
217cef2cf07SJiri Slaby 
218613655faSArnd Bergmann 	mutex_lock(&phantom_mutex);
219cef2cf07SJiri Slaby 	nonseekable_open(inode, file);
220cef2cf07SJiri Slaby 
2214541b5ecSJonathan Corbet 	if (mutex_lock_interruptible(&dev->open_lock)) {
222613655faSArnd Bergmann 		mutex_unlock(&phantom_mutex);
223cef2cf07SJiri Slaby 		return -ERESTARTSYS;
2244541b5ecSJonathan Corbet 	}
225cef2cf07SJiri Slaby 
226cef2cf07SJiri Slaby 	if (dev->opened) {
227cef2cf07SJiri Slaby 		mutex_unlock(&dev->open_lock);
228613655faSArnd Bergmann 		mutex_unlock(&phantom_mutex);
229cef2cf07SJiri Slaby 		return -EINVAL;
230cef2cf07SJiri Slaby 	}
231cef2cf07SJiri Slaby 
232bc552f77SJiri Slaby 	WARN_ON(dev->status & PHB_NOT_OH);
233bc552f77SJiri Slaby 
234cef2cf07SJiri Slaby 	file->private_data = dev;
235cef2cf07SJiri Slaby 
236bc552f77SJiri Slaby 	atomic_set(&dev->counter, 0);
237cef2cf07SJiri Slaby 	dev->opened++;
238cef2cf07SJiri Slaby 	mutex_unlock(&dev->open_lock);
239613655faSArnd Bergmann 	mutex_unlock(&phantom_mutex);
240cef2cf07SJiri Slaby 	return 0;
241cef2cf07SJiri Slaby }
242cef2cf07SJiri Slaby 
phantom_release(struct inode * inode,struct file * file)243cef2cf07SJiri Slaby static int phantom_release(struct inode *inode, struct file *file)
244cef2cf07SJiri Slaby {
245cef2cf07SJiri Slaby 	struct phantom_device *dev = file->private_data;
246cef2cf07SJiri Slaby 
247cef2cf07SJiri Slaby 	mutex_lock(&dev->open_lock);
248cef2cf07SJiri Slaby 
249cef2cf07SJiri Slaby 	dev->opened = 0;
250cef2cf07SJiri Slaby 	phantom_status(dev, dev->status & ~PHB_RUNNING);
251bc552f77SJiri Slaby 	dev->status &= ~PHB_NOT_OH;
252cef2cf07SJiri Slaby 
253cef2cf07SJiri Slaby 	mutex_unlock(&dev->open_lock);
254cef2cf07SJiri Slaby 
255cef2cf07SJiri Slaby 	return 0;
256cef2cf07SJiri Slaby }
257cef2cf07SJiri Slaby 
phantom_poll(struct file * file,poll_table * wait)258afc9a42bSAl Viro static __poll_t phantom_poll(struct file *file, poll_table *wait)
259cef2cf07SJiri Slaby {
260cef2cf07SJiri Slaby 	struct phantom_device *dev = file->private_data;
261afc9a42bSAl Viro 	__poll_t mask = 0;
262cef2cf07SJiri Slaby 
263cef2cf07SJiri Slaby 	pr_debug("phantom_poll: %d\n", atomic_read(&dev->counter));
264cef2cf07SJiri Slaby 	poll_wait(file, &dev->wait, wait);
2657d4f9f09SJiri Slaby 
2667d4f9f09SJiri Slaby 	if (!(dev->status & PHB_RUNNING))
267a9a08845SLinus Torvalds 		mask = EPOLLERR;
2687d4f9f09SJiri Slaby 	else if (atomic_read(&dev->counter))
269a9a08845SLinus Torvalds 		mask = EPOLLIN | EPOLLRDNORM;
2707d4f9f09SJiri Slaby 
271cef2cf07SJiri Slaby 	pr_debug("phantom_poll end: %x/%d\n", mask, atomic_read(&dev->counter));
272cef2cf07SJiri Slaby 
273cef2cf07SJiri Slaby 	return mask;
274cef2cf07SJiri Slaby }
275cef2cf07SJiri Slaby 
276828c0950SAlexey Dobriyan static const struct file_operations phantom_file_ops = {
277cef2cf07SJiri Slaby 	.open = phantom_open,
278cef2cf07SJiri Slaby 	.release = phantom_release,
279c15395c0SJiri Slaby 	.unlocked_ioctl = phantom_ioctl,
2807e4e8e68SJiri Slaby 	.compat_ioctl = phantom_compat_ioctl,
281cef2cf07SJiri Slaby 	.poll = phantom_poll,
282cef2cf07SJiri Slaby };
283cef2cf07SJiri Slaby 
phantom_isr(int irq,void * data)284cef2cf07SJiri Slaby static irqreturn_t phantom_isr(int irq, void *data)
285cef2cf07SJiri Slaby {
286cef2cf07SJiri Slaby 	struct phantom_device *dev = data;
287bc552f77SJiri Slaby 	unsigned int i;
288bc552f77SJiri Slaby 	u32 ctl;
289cef2cf07SJiri Slaby 
290bc552f77SJiri Slaby 	spin_lock(&dev->regs_lock);
291bc552f77SJiri Slaby 	ctl = ioread32(dev->iaddr + PHN_CONTROL);
292bc552f77SJiri Slaby 	if (!(ctl & PHN_CTL_IRQ)) {
293bc552f77SJiri Slaby 		spin_unlock(&dev->regs_lock);
294cef2cf07SJiri Slaby 		return IRQ_NONE;
295bc552f77SJiri Slaby 	}
296cef2cf07SJiri Slaby 
297cef2cf07SJiri Slaby 	iowrite32(0, dev->iaddr);
298cef2cf07SJiri Slaby 	iowrite32(0xc0, dev->iaddr);
299bc552f77SJiri Slaby 
300bc552f77SJiri Slaby 	if (dev->status & PHB_NOT_OH) {
301bc552f77SJiri Slaby 		struct phm_regs *r = &dev->oregs;
302bc552f77SJiri Slaby 		u32 m = min(r->count, 8U);
303bc552f77SJiri Slaby 
304bc552f77SJiri Slaby 		for (i = 0; i < m; i++)
305bc552f77SJiri Slaby 			if (r->mask & BIT(i))
306bc552f77SJiri Slaby 				iowrite32(r->values[i], dev->oaddr + i);
307bc552f77SJiri Slaby 
308bc552f77SJiri Slaby 		dev->ctl_reg ^= PHN_CTL_AMP;
309bc552f77SJiri Slaby 		iowrite32(dev->ctl_reg, dev->iaddr + PHN_CONTROL);
310bc552f77SJiri Slaby 	}
311bc552f77SJiri Slaby 	spin_unlock(&dev->regs_lock);
312bc552f77SJiri Slaby 
313c8511f94SJiri Slaby 	ioread32(dev->iaddr); /* PCI posting */
314cef2cf07SJiri Slaby 
315cef2cf07SJiri Slaby 	atomic_inc(&dev->counter);
316cef2cf07SJiri Slaby 	wake_up_interruptible(&dev->wait);
317cef2cf07SJiri Slaby 
318cef2cf07SJiri Slaby 	return IRQ_HANDLED;
319cef2cf07SJiri Slaby }
320cef2cf07SJiri Slaby 
321cef2cf07SJiri Slaby /*
322cef2cf07SJiri Slaby  * Init and deinit driver
323cef2cf07SJiri Slaby  */
324cef2cf07SJiri Slaby 
phantom_get_free(void)32580c8ae28SBill Pemberton static unsigned int phantom_get_free(void)
326cef2cf07SJiri Slaby {
327cef2cf07SJiri Slaby 	unsigned int i;
328cef2cf07SJiri Slaby 
329cef2cf07SJiri Slaby 	for (i = 0; i < PHANTOM_MAX_MINORS; i++)
330cef2cf07SJiri Slaby 		if (phantom_devices[i] == 0)
331cef2cf07SJiri Slaby 			break;
332cef2cf07SJiri Slaby 
333cef2cf07SJiri Slaby 	return i;
334cef2cf07SJiri Slaby }
335cef2cf07SJiri Slaby 
phantom_probe(struct pci_dev * pdev,const struct pci_device_id * pci_id)33680c8ae28SBill Pemberton static int phantom_probe(struct pci_dev *pdev,
337cef2cf07SJiri Slaby 	const struct pci_device_id *pci_id)
338cef2cf07SJiri Slaby {
339cef2cf07SJiri Slaby 	struct phantom_device *pht;
340cef2cf07SJiri Slaby 	unsigned int minor;
341cef2cf07SJiri Slaby 	int retval;
342cef2cf07SJiri Slaby 
343cef2cf07SJiri Slaby 	retval = pci_enable_device(pdev);
34410ad5278SRahul Ruikar 	if (retval) {
34510ad5278SRahul Ruikar 		dev_err(&pdev->dev, "pci_enable_device failed!\n");
346cef2cf07SJiri Slaby 		goto err;
34710ad5278SRahul Ruikar 	}
348cef2cf07SJiri Slaby 
349cef2cf07SJiri Slaby 	minor = phantom_get_free();
350cef2cf07SJiri Slaby 	if (minor == PHANTOM_MAX_MINORS) {
351cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "too many devices found!\n");
352cef2cf07SJiri Slaby 		retval = -EIO;
353cef2cf07SJiri Slaby 		goto err_dis;
354cef2cf07SJiri Slaby 	}
355cef2cf07SJiri Slaby 
356cef2cf07SJiri Slaby 	phantom_devices[minor] = 1;
357cef2cf07SJiri Slaby 
358cef2cf07SJiri Slaby 	retval = pci_request_regions(pdev, "phantom");
35910ad5278SRahul Ruikar 	if (retval) {
36010ad5278SRahul Ruikar 		dev_err(&pdev->dev, "pci_request_regions failed!\n");
361cef2cf07SJiri Slaby 		goto err_null;
36210ad5278SRahul Ruikar 	}
363cef2cf07SJiri Slaby 
364cef2cf07SJiri Slaby 	retval = -ENOMEM;
365cef2cf07SJiri Slaby 	pht = kzalloc(sizeof(*pht), GFP_KERNEL);
366cef2cf07SJiri Slaby 	if (pht == NULL) {
367cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "unable to allocate device\n");
368cef2cf07SJiri Slaby 		goto err_reg;
369cef2cf07SJiri Slaby 	}
370cef2cf07SJiri Slaby 
371cef2cf07SJiri Slaby 	pht->caddr = pci_iomap(pdev, 0, 0);
372cef2cf07SJiri Slaby 	if (pht->caddr == NULL) {
373cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "can't remap conf space\n");
374cef2cf07SJiri Slaby 		goto err_fr;
375cef2cf07SJiri Slaby 	}
376cef2cf07SJiri Slaby 	pht->iaddr = pci_iomap(pdev, 2, 0);
377cef2cf07SJiri Slaby 	if (pht->iaddr == NULL) {
378cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "can't remap input space\n");
379cef2cf07SJiri Slaby 		goto err_unmc;
380cef2cf07SJiri Slaby 	}
381cef2cf07SJiri Slaby 	pht->oaddr = pci_iomap(pdev, 3, 0);
382cef2cf07SJiri Slaby 	if (pht->oaddr == NULL) {
383cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "can't remap output space\n");
384cef2cf07SJiri Slaby 		goto err_unmi;
385cef2cf07SJiri Slaby 	}
386cef2cf07SJiri Slaby 
387cef2cf07SJiri Slaby 	mutex_init(&pht->open_lock);
388bc552f77SJiri Slaby 	spin_lock_init(&pht->regs_lock);
389cef2cf07SJiri Slaby 	init_waitqueue_head(&pht->wait);
390cef2cf07SJiri Slaby 	cdev_init(&pht->cdev, &phantom_file_ops);
391cef2cf07SJiri Slaby 	pht->cdev.owner = THIS_MODULE;
392cef2cf07SJiri Slaby 
393cef2cf07SJiri Slaby 	iowrite32(0, pht->caddr + PHN_IRQCTL);
394c8511f94SJiri Slaby 	ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
395cef2cf07SJiri Slaby 	retval = request_irq(pdev->irq, phantom_isr,
396bb9da88dSMichael Opdenacker 			IRQF_SHARED, "phantom", pht);
397cef2cf07SJiri Slaby 	if (retval) {
398cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "can't establish ISR\n");
399cef2cf07SJiri Slaby 		goto err_unmo;
400cef2cf07SJiri Slaby 	}
401cef2cf07SJiri Slaby 
402cef2cf07SJiri Slaby 	retval = cdev_add(&pht->cdev, MKDEV(phantom_major, minor), 1);
403cef2cf07SJiri Slaby 	if (retval) {
404cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "chardev registration failed\n");
405cef2cf07SJiri Slaby 		goto err_irq;
406cef2cf07SJiri Slaby 	}
407cef2cf07SJiri Slaby 
408*f6c086efSGreg Kroah-Hartman 	if (IS_ERR(device_create(&phantom_class, &pdev->dev,
409a9b12619SGreg Kroah-Hartman 				 MKDEV(phantom_major, minor), NULL,
410a9b12619SGreg Kroah-Hartman 				 "phantom%u", minor)))
411cef2cf07SJiri Slaby 		dev_err(&pdev->dev, "can't create device\n");
412cef2cf07SJiri Slaby 
413cef2cf07SJiri Slaby 	pci_set_drvdata(pdev, pht);
414cef2cf07SJiri Slaby 
415cef2cf07SJiri Slaby 	return 0;
416cef2cf07SJiri Slaby err_irq:
417cef2cf07SJiri Slaby 	free_irq(pdev->irq, pht);
418cef2cf07SJiri Slaby err_unmo:
419cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->oaddr);
420cef2cf07SJiri Slaby err_unmi:
421cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->iaddr);
422cef2cf07SJiri Slaby err_unmc:
423cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->caddr);
424cef2cf07SJiri Slaby err_fr:
425cef2cf07SJiri Slaby 	kfree(pht);
426cef2cf07SJiri Slaby err_reg:
427cef2cf07SJiri Slaby 	pci_release_regions(pdev);
428cef2cf07SJiri Slaby err_null:
429cef2cf07SJiri Slaby 	phantom_devices[minor] = 0;
430cef2cf07SJiri Slaby err_dis:
431cef2cf07SJiri Slaby 	pci_disable_device(pdev);
432cef2cf07SJiri Slaby err:
433cef2cf07SJiri Slaby 	return retval;
434cef2cf07SJiri Slaby }
435cef2cf07SJiri Slaby 
phantom_remove(struct pci_dev * pdev)436486a5c28SBill Pemberton static void phantom_remove(struct pci_dev *pdev)
437cef2cf07SJiri Slaby {
438cef2cf07SJiri Slaby 	struct phantom_device *pht = pci_get_drvdata(pdev);
439cef2cf07SJiri Slaby 	unsigned int minor = MINOR(pht->cdev.dev);
440cef2cf07SJiri Slaby 
441*f6c086efSGreg Kroah-Hartman 	device_destroy(&phantom_class, MKDEV(phantom_major, minor));
442cef2cf07SJiri Slaby 
443cef2cf07SJiri Slaby 	cdev_del(&pht->cdev);
444cef2cf07SJiri Slaby 
445cef2cf07SJiri Slaby 	iowrite32(0, pht->caddr + PHN_IRQCTL);
446c8511f94SJiri Slaby 	ioread32(pht->caddr + PHN_IRQCTL); /* PCI posting */
447cef2cf07SJiri Slaby 	free_irq(pdev->irq, pht);
448cef2cf07SJiri Slaby 
449cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->oaddr);
450cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->iaddr);
451cef2cf07SJiri Slaby 	pci_iounmap(pdev, pht->caddr);
452cef2cf07SJiri Slaby 
453cef2cf07SJiri Slaby 	kfree(pht);
454cef2cf07SJiri Slaby 
455cef2cf07SJiri Slaby 	pci_release_regions(pdev);
456cef2cf07SJiri Slaby 
457cef2cf07SJiri Slaby 	phantom_devices[minor] = 0;
458cef2cf07SJiri Slaby 
459cef2cf07SJiri Slaby 	pci_disable_device(pdev);
460cef2cf07SJiri Slaby }
461cef2cf07SJiri Slaby 
phantom_suspend(struct device * dev_d)4626bbf5256SVaibhav Gupta static int __maybe_unused phantom_suspend(struct device *dev_d)
463cef2cf07SJiri Slaby {
4646bbf5256SVaibhav Gupta 	struct phantom_device *dev = dev_get_drvdata(dev_d);
465cef2cf07SJiri Slaby 
466cef2cf07SJiri Slaby 	iowrite32(0, dev->caddr + PHN_IRQCTL);
467c8511f94SJiri Slaby 	ioread32(dev->caddr + PHN_IRQCTL); /* PCI posting */
468cef2cf07SJiri Slaby 
4696bbf5256SVaibhav Gupta 	synchronize_irq(to_pci_dev(dev_d)->irq);
470aee8447cSJiri Slaby 
471cef2cf07SJiri Slaby 	return 0;
472cef2cf07SJiri Slaby }
473cef2cf07SJiri Slaby 
phantom_resume(struct device * dev_d)4746bbf5256SVaibhav Gupta static int __maybe_unused phantom_resume(struct device *dev_d)
475cef2cf07SJiri Slaby {
4766bbf5256SVaibhav Gupta 	struct phantom_device *dev = dev_get_drvdata(dev_d);
477cef2cf07SJiri Slaby 
478cef2cf07SJiri Slaby 	iowrite32(0, dev->caddr + PHN_IRQCTL);
479cef2cf07SJiri Slaby 
480cef2cf07SJiri Slaby 	return 0;
481cef2cf07SJiri Slaby }
482cef2cf07SJiri Slaby 
4832c685064SBill Pemberton static struct pci_device_id phantom_pci_tbl[] = {
48482f56087SJiri Slaby 	{ .vendor = PCI_VENDOR_ID_PLX, .device = PCI_DEVICE_ID_PLX_9050,
48582f56087SJiri Slaby 	  .subvendor = PCI_VENDOR_ID_PLX, .subdevice = PCI_DEVICE_ID_PLX_9050,
486cef2cf07SJiri Slaby 	  .class = PCI_CLASS_BRIDGE_OTHER << 8, .class_mask = 0xffff00 },
487cef2cf07SJiri Slaby 	{ 0, }
488cef2cf07SJiri Slaby };
489cef2cf07SJiri Slaby MODULE_DEVICE_TABLE(pci, phantom_pci_tbl);
490cef2cf07SJiri Slaby 
4916bbf5256SVaibhav Gupta static SIMPLE_DEV_PM_OPS(phantom_pm_ops, phantom_suspend, phantom_resume);
4926bbf5256SVaibhav Gupta 
493cef2cf07SJiri Slaby static struct pci_driver phantom_pci_driver = {
494cef2cf07SJiri Slaby 	.name = "phantom",
495cef2cf07SJiri Slaby 	.id_table = phantom_pci_tbl,
496cef2cf07SJiri Slaby 	.probe = phantom_probe,
4972d6bed9cSBill Pemberton 	.remove = phantom_remove,
4986bbf5256SVaibhav Gupta 	.driver.pm = &phantom_pm_ops,
499cef2cf07SJiri Slaby };
500cef2cf07SJiri Slaby 
5010933e2d9SAndi Kleen static CLASS_ATTR_STRING(version, 0444, PHANTOM_VERSION);
502cef2cf07SJiri Slaby 
phantom_init(void)503cef2cf07SJiri Slaby static int __init phantom_init(void)
504cef2cf07SJiri Slaby {
505cef2cf07SJiri Slaby 	int retval;
506cef2cf07SJiri Slaby 	dev_t dev;
507cef2cf07SJiri Slaby 
508*f6c086efSGreg Kroah-Hartman 	retval = class_register(&phantom_class);
509*f6c086efSGreg Kroah-Hartman 	if (retval) {
510cef2cf07SJiri Slaby 		printk(KERN_ERR "phantom: can't register phantom class\n");
511cef2cf07SJiri Slaby 		goto err;
512cef2cf07SJiri Slaby 	}
513*f6c086efSGreg Kroah-Hartman 	retval = class_create_file(&phantom_class, &class_attr_version.attr);
514cef2cf07SJiri Slaby 	if (retval) {
515cef2cf07SJiri Slaby 		printk(KERN_ERR "phantom: can't create sysfs version file\n");
516cef2cf07SJiri Slaby 		goto err_class;
517cef2cf07SJiri Slaby 	}
518cef2cf07SJiri Slaby 
519cef2cf07SJiri Slaby 	retval = alloc_chrdev_region(&dev, 0, PHANTOM_MAX_MINORS, "phantom");
520cef2cf07SJiri Slaby 	if (retval) {
521cef2cf07SJiri Slaby 		printk(KERN_ERR "phantom: can't register character device\n");
522cef2cf07SJiri Slaby 		goto err_attr;
523cef2cf07SJiri Slaby 	}
524cef2cf07SJiri Slaby 	phantom_major = MAJOR(dev);
525cef2cf07SJiri Slaby 
526cef2cf07SJiri Slaby 	retval = pci_register_driver(&phantom_pci_driver);
527cef2cf07SJiri Slaby 	if (retval) {
528cef2cf07SJiri Slaby 		printk(KERN_ERR "phantom: can't register pci driver\n");
529cef2cf07SJiri Slaby 		goto err_unchr;
530cef2cf07SJiri Slaby 	}
531cef2cf07SJiri Slaby 
532cef2cf07SJiri Slaby 	printk(KERN_INFO "Phantom Linux Driver, version " PHANTOM_VERSION ", "
533cef2cf07SJiri Slaby 			"init OK\n");
534cef2cf07SJiri Slaby 
535cef2cf07SJiri Slaby 	return 0;
536cef2cf07SJiri Slaby err_unchr:
537cef2cf07SJiri Slaby 	unregister_chrdev_region(dev, PHANTOM_MAX_MINORS);
538cef2cf07SJiri Slaby err_attr:
539*f6c086efSGreg Kroah-Hartman 	class_remove_file(&phantom_class, &class_attr_version.attr);
540cef2cf07SJiri Slaby err_class:
541*f6c086efSGreg Kroah-Hartman 	class_unregister(&phantom_class);
542cef2cf07SJiri Slaby err:
543cef2cf07SJiri Slaby 	return retval;
544cef2cf07SJiri Slaby }
545cef2cf07SJiri Slaby 
phantom_exit(void)546cef2cf07SJiri Slaby static void __exit phantom_exit(void)
547cef2cf07SJiri Slaby {
548cef2cf07SJiri Slaby 	pci_unregister_driver(&phantom_pci_driver);
549cef2cf07SJiri Slaby 
550cef2cf07SJiri Slaby 	unregister_chrdev_region(MKDEV(phantom_major, 0), PHANTOM_MAX_MINORS);
551cef2cf07SJiri Slaby 
552*f6c086efSGreg Kroah-Hartman 	class_remove_file(&phantom_class, &class_attr_version.attr);
553*f6c086efSGreg Kroah-Hartman 	class_unregister(&phantom_class);
554cef2cf07SJiri Slaby 
555cef2cf07SJiri Slaby 	pr_debug("phantom: module successfully removed\n");
556cef2cf07SJiri Slaby }
557cef2cf07SJiri Slaby 
558cef2cf07SJiri Slaby module_init(phantom_init);
559cef2cf07SJiri Slaby module_exit(phantom_exit);
560cef2cf07SJiri Slaby 
561cef2cf07SJiri Slaby MODULE_AUTHOR("Jiri Slaby <[email protected]>");
562ec905a18SJiri Slaby MODULE_DESCRIPTION("Sensable Phantom driver (PCI devices)");
563cef2cf07SJiri Slaby MODULE_LICENSE("GPL");
564cef2cf07SJiri Slaby MODULE_VERSION(PHANTOM_VERSION);
565