xref: /freebsd/sys/compat/linuxkpi/common/src/linux_interrupt.c (revision ebacd8013fe5f7fdf9f6a5b286f6680dd2891036)
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  * $FreeBSD$
30  */
31 
32 #include <linux/device.h>
33 #include <linux/interrupt.h>
34 #include <linux/pci.h>
35 
36 #include <sys/param.h>
37 #include <sys/bus.h>
38 #include <sys/rman.h>
39 #include <sys/interrupt.h>
40 
41 struct irq_ent {
42 	struct list_head	links;
43 	struct device	*dev;
44 	struct resource	*res;
45 	void		*arg;
46 	irqreturn_t	(*handler)(int, void *);
47 	irqreturn_t	(*thread_handler)(int, void *);
48 	void		*tag;
49 	unsigned int	irq;
50 };
51 
52 static inline int
53 lkpi_irq_rid(struct device *dev, unsigned int irq)
54 {
55 	/* check for MSI- or MSIX- interrupt */
56 	if (irq >= dev->irq_start && irq < dev->irq_end)
57 		return (irq - dev->irq_start + 1);
58 	else
59 		return (0);
60 }
61 
62 static inline struct irq_ent *
63 lkpi_irq_ent(struct device *dev, unsigned int irq)
64 {
65 	struct irq_ent *irqe;
66 
67 	list_for_each_entry(irqe, &dev->irqents, links)
68 		if (irqe->irq == irq)
69 			return (irqe);
70 
71 	return (NULL);
72 }
73 
74 static void
75 lkpi_irq_handler(void *ent)
76 {
77 	struct irq_ent *irqe;
78 
79 	if (linux_set_current_flags(curthread, M_NOWAIT))
80 		return;
81 
82 	irqe = ent;
83 	if (irqe->handler(irqe->irq, irqe->arg) == IRQ_WAKE_THREAD &&
84 	    irqe->thread_handler != NULL) {
85 		THREAD_SLEEPING_OK();
86 		irqe->thread_handler(irqe->irq, irqe->arg);
87 		THREAD_NO_SLEEPING();
88 	}
89 }
90 
91 static inline void
92 lkpi_irq_release(struct device *dev, struct irq_ent *irqe)
93 {
94 	if (irqe->tag != NULL)
95 		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
96 	if (irqe->res != NULL)
97 		bus_release_resource(dev->bsddev, SYS_RES_IRQ,
98 		    rman_get_rid(irqe->res), irqe->res);
99 	list_del(&irqe->links);
100 }
101 
102 static void
103 lkpi_devm_irq_release(struct device *dev, void *p)
104 {
105 	struct irq_ent *irqe;
106 
107 	if (dev == NULL || p == NULL)
108 		return;
109 
110 	irqe = p;
111 	lkpi_irq_release(dev, irqe);
112 }
113 
114 int
115 lkpi_request_irq(struct device *xdev, unsigned int irq,
116     irq_handler_t handler, irq_handler_t thread_handler,
117     unsigned long flags, const char *name, void *arg)
118 {
119 	struct resource *res;
120 	struct irq_ent *irqe;
121 	struct device *dev;
122 	int error;
123 	int rid;
124 
125 	dev = linux_pci_find_irq_dev(irq);
126 	if (dev == NULL)
127 		return -ENXIO;
128 	if (xdev != NULL && xdev != dev)
129 		return -ENXIO;
130 	rid = lkpi_irq_rid(dev, irq);
131 	res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid,
132 	    flags | RF_ACTIVE);
133 	if (res == NULL)
134 		return (-ENXIO);
135 	if (xdev != NULL)
136 		irqe = lkpi_devres_alloc(lkpi_devm_irq_release, sizeof(*irqe),
137 		    GFP_KERNEL | __GFP_ZERO);
138 	else
139 		irqe = kzalloc(sizeof(*irqe), GFP_KERNEL);
140 	irqe->dev = dev;
141 	irqe->res = res;
142 	irqe->arg = arg;
143 	irqe->handler = handler;
144 	irqe->thread_handler = thread_handler;
145 	irqe->irq = irq;
146 
147 	error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE,
148 	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
149 	if (error)
150 		goto errout;
151 	list_add(&irqe->links, &dev->irqents);
152 	if (xdev != NULL)
153 		devres_add(xdev, irqe);
154 
155 	return 0;
156 
157 errout:
158 	bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
159 	if (xdev != NULL)
160 		devres_free(irqe);
161 	else
162 		kfree(irqe);
163 	return (-error);
164 }
165 
166 int
167 lkpi_enable_irq(unsigned int irq)
168 {
169 	struct irq_ent *irqe;
170 	struct device *dev;
171 
172 	dev = linux_pci_find_irq_dev(irq);
173 	if (dev == NULL)
174 		return -EINVAL;
175 	irqe = lkpi_irq_ent(dev, irq);
176 	if (irqe == NULL || irqe->tag != NULL)
177 		return -EINVAL;
178 	return -bus_setup_intr(dev->bsddev, irqe->res, INTR_TYPE_NET | INTR_MPSAFE,
179 	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
180 }
181 
182 void
183 lkpi_disable_irq(unsigned int irq)
184 {
185 	struct irq_ent *irqe;
186 	struct device *dev;
187 
188 	dev = linux_pci_find_irq_dev(irq);
189 	if (dev == NULL)
190 		return;
191 	irqe = lkpi_irq_ent(dev, irq);
192 	if (irqe == NULL)
193 		return;
194 	if (irqe->tag != NULL)
195 		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
196 	irqe->tag = NULL;
197 }
198 
199 int
200 lkpi_bind_irq_to_cpu(unsigned int irq, int cpu_id)
201 {
202 	struct irq_ent *irqe;
203 	struct device *dev;
204 
205 	dev = linux_pci_find_irq_dev(irq);
206 	if (dev == NULL)
207 		return (-ENOENT);
208 
209 	irqe = lkpi_irq_ent(dev, irq);
210 	if (irqe == NULL)
211 		return (-ENOENT);
212 
213 	return (-bus_bind_intr(dev->bsddev, irqe->res, cpu_id));
214 }
215 
216 void
217 lkpi_free_irq(unsigned int irq, void *device __unused)
218 {
219 	struct irq_ent *irqe;
220 	struct device *dev;
221 
222 	dev = linux_pci_find_irq_dev(irq);
223 	if (dev == NULL)
224 		return;
225 	irqe = lkpi_irq_ent(dev, irq);
226 	if (irqe == NULL)
227 		return;
228 	lkpi_irq_release(dev, irqe);
229 	kfree(irqe);
230 }
231 
232 void
233 lkpi_devm_free_irq(struct device *xdev, unsigned int irq, void *p __unused)
234 {
235 	struct device *dev;
236 	struct irq_ent *irqe;
237 
238 	dev = linux_pci_find_irq_dev(irq);
239 	if (dev == NULL)
240 		return;
241 	if (xdev != dev)
242 		return;
243 	irqe = lkpi_irq_ent(dev, irq);
244 	if (irqe == NULL)
245 		return;
246 	lkpi_irq_release(dev, irqe);
247 	lkpi_devres_unlink(dev, irqe);
248 	lkpi_devres_free(irqe);
249 	return;
250 }
251