1/*-
2 * Copyright (c) 2010 Isilon Systems, Inc.
3 * Copyright (c) 2010 iX Systems, Inc.
4 * Copyright (c) 2010 Panasas, Inc.
5 * Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice unmodified, this list of conditions, and the following
13 *    disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include <linux/device.h>
31#include <linux/interrupt.h>
32#include <linux/pci.h>
33
34#include <sys/param.h>
35#include <sys/bus.h>
36#include <sys/rman.h>
37#include <sys/interrupt.h>
38
39struct irq_ent {
40	struct list_head	links;
41	struct device	*dev;
42	struct resource	*res;
43	void		*arg;
44	irqreturn_t	(*handler)(int, void *);
45	irqreturn_t	(*thread_handler)(int, void *);
46	void		*tag;
47	unsigned int	irq;
48};
49
50static inline int
51lkpi_irq_rid(struct device *dev, unsigned int irq)
52{
53	/* check for MSI- or MSIX- interrupt */
54	if (irq >= dev->irq_start && irq < dev->irq_end)
55		return (irq - dev->irq_start + 1);
56	else
57		return (0);
58}
59
60static inline struct irq_ent *
61lkpi_irq_ent(struct device *dev, unsigned int irq)
62{
63	struct irq_ent *irqe;
64
65	list_for_each_entry(irqe, &dev->irqents, links)
66		if (irqe->irq == irq)
67			return (irqe);
68
69	return (NULL);
70}
71
72static void
73lkpi_irq_handler(void *ent)
74{
75	struct irq_ent *irqe;
76
77	if (linux_set_current_flags(curthread, M_NOWAIT))
78		return;
79
80	irqe = ent;
81	if (irqe->handler(irqe->irq, irqe->arg) == IRQ_WAKE_THREAD &&
82	    irqe->thread_handler != NULL) {
83		THREAD_SLEEPING_OK();
84		irqe->thread_handler(irqe->irq, irqe->arg);
85		THREAD_NO_SLEEPING();
86	}
87}
88
89static inline void
90lkpi_irq_release(struct device *dev, struct irq_ent *irqe)
91{
92	if (irqe->tag != NULL)
93		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
94	if (irqe->res != NULL)
95		bus_release_resource(dev->bsddev, SYS_RES_IRQ,
96		    rman_get_rid(irqe->res), irqe->res);
97	list_del(&irqe->links);
98}
99
100static void
101lkpi_devm_irq_release(struct device *dev, void *p)
102{
103	struct irq_ent *irqe;
104
105	if (dev == NULL || p == NULL)
106		return;
107
108	irqe = p;
109	lkpi_irq_release(dev, irqe);
110}
111
112int
113lkpi_request_irq(struct device *xdev, unsigned int irq,
114    irq_handler_t handler, irq_handler_t thread_handler,
115    unsigned long flags, const char *name, void *arg)
116{
117	struct resource *res;
118	struct irq_ent *irqe;
119	struct device *dev;
120	unsigned resflags;
121	int error;
122	int rid;
123
124	dev = lkpi_pci_find_irq_dev(irq);
125	if (dev == NULL)
126		return -ENXIO;
127	if (xdev != NULL && xdev != dev)
128		return -ENXIO;
129	rid = lkpi_irq_rid(dev, irq);
130	resflags = RF_ACTIVE;
131	if ((flags & IRQF_SHARED) != 0)
132		resflags |= RF_SHAREABLE;
133	res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid, resflags);
134	if (res == NULL)
135		return (-ENXIO);
136	if (xdev != NULL)
137		irqe = lkpi_devres_alloc(lkpi_devm_irq_release, sizeof(*irqe),
138		    GFP_KERNEL | __GFP_ZERO);
139	else
140		irqe = kzalloc(sizeof(*irqe), GFP_KERNEL);
141	irqe->dev = dev;
142	irqe->res = res;
143	irqe->arg = arg;
144	irqe->handler = handler;
145	irqe->thread_handler = thread_handler;
146	irqe->irq = irq;
147
148	error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE,
149	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
150	if (error)
151		goto errout;
152	list_add(&irqe->links, &dev->irqents);
153	if (xdev != NULL)
154		devres_add(xdev, irqe);
155
156	return 0;
157
158errout:
159	bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
160	if (xdev != NULL)
161		devres_free(irqe);
162	else
163		kfree(irqe);
164	return (-error);
165}
166
167int
168lkpi_enable_irq(unsigned int irq)
169{
170	struct irq_ent *irqe;
171	struct device *dev;
172
173	dev = lkpi_pci_find_irq_dev(irq);
174	if (dev == NULL)
175		return -EINVAL;
176	irqe = lkpi_irq_ent(dev, irq);
177	if (irqe == NULL || irqe->tag != NULL)
178		return -EINVAL;
179	return -bus_setup_intr(dev->bsddev, irqe->res, INTR_TYPE_NET | INTR_MPSAFE,
180	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
181}
182
183void
184lkpi_disable_irq(unsigned int irq)
185{
186	struct irq_ent *irqe;
187	struct device *dev;
188
189	dev = lkpi_pci_find_irq_dev(irq);
190	if (dev == NULL)
191		return;
192	irqe = lkpi_irq_ent(dev, irq);
193	if (irqe == NULL)
194		return;
195	if (irqe->tag != NULL)
196		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
197	irqe->tag = NULL;
198}
199
200int
201lkpi_bind_irq_to_cpu(unsigned int irq, int cpu_id)
202{
203	struct irq_ent *irqe;
204	struct device *dev;
205
206	dev = lkpi_pci_find_irq_dev(irq);
207	if (dev == NULL)
208		return (-ENOENT);
209
210	irqe = lkpi_irq_ent(dev, irq);
211	if (irqe == NULL)
212		return (-ENOENT);
213
214	return (-bus_bind_intr(dev->bsddev, irqe->res, cpu_id));
215}
216
217void
218lkpi_free_irq(unsigned int irq, void *device __unused)
219{
220	struct irq_ent *irqe;
221	struct device *dev;
222
223	dev = lkpi_pci_find_irq_dev(irq);
224	if (dev == NULL)
225		return;
226	irqe = lkpi_irq_ent(dev, irq);
227	if (irqe == NULL)
228		return;
229	lkpi_irq_release(dev, irqe);
230	kfree(irqe);
231}
232
233void
234lkpi_devm_free_irq(struct device *xdev, unsigned int irq, void *p __unused)
235{
236	struct device *dev;
237	struct irq_ent *irqe;
238
239	dev = lkpi_pci_find_irq_dev(irq);
240	if (dev == NULL)
241		return;
242	if (xdev != dev)
243		return;
244	irqe = lkpi_irq_ent(dev, irq);
245	if (irqe == NULL)
246		return;
247	lkpi_irq_release(dev, irqe);
248	lkpi_devres_unlink(dev, irqe);
249	lkpi_devres_free(irqe);
250	return;
251}
252