Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/compat/linuxkpi/common/src/linux_interrupt.c
39586 views
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
39
struct 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
50
static inline int
51
lkpi_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
60
static inline struct irq_ent *
61
lkpi_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
72
static void
73
lkpi_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
89
static inline void
90
lkpi_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
100
static void
101
lkpi_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
112
int
113
lkpi_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
158
errout:
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
167
int
168
lkpi_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
183
void
184
lkpi_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
200
int
201
lkpi_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
217
void
218
lkpi_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
233
void
234
lkpi_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
253