14Srgrimes/*-
24Srgrimes * Copyright (c) 2010 Isilon Systems, Inc.
34Srgrimes * Copyright (c) 2010 iX Systems, Inc.
44Srgrimes * Copyright (c) 2010 Panasas, Inc.
54Srgrimes * Copyright (c) 2013-2015 Mellanox Technologies, Ltd.
64Srgrimes * All rights reserved.
74Srgrimes *
84Srgrimes * Redistribution and use in source and binary forms, with or without
94Srgrimes * modification, are permitted provided that the following conditions
104Srgrimes * are met:
114Srgrimes * 1. Redistributions of source code must retain the above copyright
124Srgrimes *    notice unmodified, this list of conditions, and the following
134Srgrimes *    disclaimer.
144Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
154Srgrimes *    notice, this list of conditions and the following disclaimer in the
164Srgrimes *    documentation and/or other materials provided with the distribution.
174Srgrimes *
184Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
194Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
204Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
214Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
224Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
234Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
244Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
254Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
264Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
274Srgrimes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
284Srgrimes */
29620Srgrimes
30620Srgrimes#include <linux/device.h>
314Srgrimes#include <linux/interrupt.h>
324Srgrimes#include <linux/pci.h>
33115683Sobrien
34115683Sobrien#include <sys/param.h>
35115683Sobrien#include <sys/bus.h>
362056Swollman#include <sys/rman.h>
372056Swollman#include <sys/interrupt.h>
382056Swollman
394Srgrimesstruct irq_ent {
4028270Swollman	struct list_head	links;
4128270Swollman	struct device	*dev;
4228270Swollman	struct resource	*res;
4312607Sbde	void		*arg;
4428270Swollman	irqreturn_t	(*handler)(int, void *);
4528270Swollman	irqreturn_t	(*thread_handler)(int, void *);
464Srgrimes	void		*tag;
474Srgrimes	unsigned int	irq;
484Srgrimes};
494Srgrimes
504Srgrimesstatic inline int
518876Srgrimeslkpi_irq_rid(struct device *dev, unsigned int irq)
524Srgrimes{
534Srgrimes	/* check for MSI- or MSIX- interrupt */
544Srgrimes	if (irq >= dev->irq_start && irq < dev->irq_end)
554Srgrimes		return (irq - dev->irq_start + 1);
564Srgrimes	else
57143063Sjoerg		return (0);
58143063Sjoerg}
59143063Sjoerg
60143063Sjoergstatic inline struct irq_ent *
61143063Sjoerglkpi_irq_ent(struct device *dev, unsigned int irq)
62126891Strhodes{
63126891Strhodes	struct irq_ent *irqe;
64126891Strhodes
65126891Strhodes	list_for_each_entry(irqe, &dev->irqents, links)
66126891Strhodes		if (irqe->irq == irq)
67126891Strhodes			return (irqe);
68126891Strhodes
69126891Strhodes	return (NULL);
70126891Strhodes}
71126891Strhodes
72126891Strhodesstatic void
73126891Strhodeslkpi_irq_handler(void *ent)
74126891Strhodes{
75126891Strhodes	struct irq_ent *irqe;
764Srgrimes
774Srgrimes	if (linux_set_current_flags(curthread, M_NOWAIT))
78143063Sjoerg		return;
79126891Strhodes
80126891Strhodes	irqe = ent;
81126891Strhodes	if (irqe->handler(irqe->irq, irqe->arg) == IRQ_WAKE_THREAD &&
82126891Strhodes	    irqe->thread_handler != NULL) {
83126891Strhodes		THREAD_SLEEPING_OK();
84126891Strhodes		irqe->thread_handler(irqe->irq, irqe->arg);
85126891Strhodes		THREAD_NO_SLEEPING();
86126891Strhodes	}
87126891Strhodes}
88126891Strhodes
89126891Strhodesstatic inline void
90126891Strhodeslkpi_irq_release(struct device *dev, struct irq_ent *irqe)
91126891Strhodes{
92126891Strhodes	if (irqe->tag != NULL)
93126891Strhodes		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
94126891Strhodes	if (irqe->res != NULL)
95126891Strhodes		bus_release_resource(dev->bsddev, SYS_RES_IRQ,
96126891Strhodes		    rman_get_rid(irqe->res), irqe->res);
97126891Strhodes	list_del(&irqe->links);
98126891Strhodes}
99126891Strhodes
100126891Strhodesstatic void
101126891Strhodeslkpi_devm_irq_release(struct device *dev, void *p)
102126891Strhodes{
103126891Strhodes	struct irq_ent *irqe;
104126891Strhodes
105126891Strhodes	if (dev == NULL || p == NULL)
106126891Strhodes		return;
107126891Strhodes
108126891Strhodes	irqe = p;
109126891Strhodes	lkpi_irq_release(dev, irqe);
110126891Strhodes}
111126891Strhodes
112126891Strhodesint
113126891Strhodeslkpi_request_irq(struct device *xdev, unsigned int irq,
114126891Strhodes    irq_handler_t handler, irq_handler_t thread_handler,
115126891Strhodes    unsigned long flags, const char *name, void *arg)
116126891Strhodes{
117126891Strhodes	struct resource *res;
118126891Strhodes	struct irq_ent *irqe;
119126891Strhodes	struct device *dev;
120126891Strhodes	unsigned resflags;
121126891Strhodes	int error;
122126891Strhodes	int rid;
123126891Strhodes
124126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
125126891Strhodes	if (dev == NULL)
126126891Strhodes		return -ENXIO;
127126891Strhodes	if (xdev != NULL && xdev != dev)
128126891Strhodes		return -ENXIO;
129126891Strhodes	rid = lkpi_irq_rid(dev, irq);
130126891Strhodes	resflags = RF_ACTIVE;
131126891Strhodes	if ((flags & IRQF_SHARED) != 0)
132126891Strhodes		resflags |= RF_SHAREABLE;
133126891Strhodes	res = bus_alloc_resource_any(dev->bsddev, SYS_RES_IRQ, &rid, resflags);
134126891Strhodes	if (res == NULL)
135126891Strhodes		return (-ENXIO);
136126891Strhodes	if (xdev != NULL)
137126891Strhodes		irqe = lkpi_devres_alloc(lkpi_devm_irq_release, sizeof(*irqe),
138126891Strhodes		    GFP_KERNEL | __GFP_ZERO);
139126891Strhodes	else
140126891Strhodes		irqe = kzalloc(sizeof(*irqe), GFP_KERNEL);
141126891Strhodes	irqe->dev = dev;
142126891Strhodes	irqe->res = res;
143126891Strhodes	irqe->arg = arg;
144126891Strhodes	irqe->handler = handler;
145126891Strhodes	irqe->thread_handler = thread_handler;
146126891Strhodes	irqe->irq = irq;
147126891Strhodes
148126891Strhodes	error = bus_setup_intr(dev->bsddev, res, INTR_TYPE_NET | INTR_MPSAFE,
149126891Strhodes	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
150126891Strhodes	if (error)
151126891Strhodes		goto errout;
152126891Strhodes	list_add(&irqe->links, &dev->irqents);
153126891Strhodes	if (xdev != NULL)
154126891Strhodes		devres_add(xdev, irqe);
155126891Strhodes
156126891Strhodes	return 0;
157126891Strhodes
158126891Strhodeserrout:
159126891Strhodes	bus_release_resource(dev->bsddev, SYS_RES_IRQ, rid, irqe->res);
160126891Strhodes	if (xdev != NULL)
161126891Strhodes		devres_free(irqe);
162126891Strhodes	else
163126891Strhodes		kfree(irqe);
164126891Strhodes	return (-error);
165126891Strhodes}
166126891Strhodes
167126891Strhodesint
168126891Strhodeslkpi_enable_irq(unsigned int irq)
169126891Strhodes{
170126891Strhodes	struct irq_ent *irqe;
171126891Strhodes	struct device *dev;
172126891Strhodes
173126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
174126891Strhodes	if (dev == NULL)
175126891Strhodes		return -EINVAL;
176126891Strhodes	irqe = lkpi_irq_ent(dev, irq);
177126891Strhodes	if (irqe == NULL || irqe->tag != NULL)
178126891Strhodes		return -EINVAL;
179126891Strhodes	return -bus_setup_intr(dev->bsddev, irqe->res, INTR_TYPE_NET | INTR_MPSAFE,
180126891Strhodes	    NULL, lkpi_irq_handler, irqe, &irqe->tag);
181126891Strhodes}
182126891Strhodes
183126891Strhodesvoid
184126891Strhodeslkpi_disable_irq(unsigned int irq)
185126891Strhodes{
186126891Strhodes	struct irq_ent *irqe;
187126891Strhodes	struct device *dev;
188126891Strhodes
189126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
190126891Strhodes	if (dev == NULL)
191126891Strhodes		return;
192126891Strhodes	irqe = lkpi_irq_ent(dev, irq);
193126891Strhodes	if (irqe == NULL)
194126891Strhodes		return;
195126891Strhodes	if (irqe->tag != NULL)
196126891Strhodes		bus_teardown_intr(dev->bsddev, irqe->res, irqe->tag);
197126891Strhodes	irqe->tag = NULL;
198126891Strhodes}
199126891Strhodes
200126891Strhodesint
201126891Strhodeslkpi_bind_irq_to_cpu(unsigned int irq, int cpu_id)
202126891Strhodes{
203126891Strhodes	struct irq_ent *irqe;
204126891Strhodes	struct device *dev;
205126891Strhodes
206126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
207126891Strhodes	if (dev == NULL)
208126891Strhodes		return (-ENOENT);
209126891Strhodes
210126891Strhodes	irqe = lkpi_irq_ent(dev, irq);
211126891Strhodes	if (irqe == NULL)
212126891Strhodes		return (-ENOENT);
213126891Strhodes
214126891Strhodes	return (-bus_bind_intr(dev->bsddev, irqe->res, cpu_id));
215126891Strhodes}
216126891Strhodes
217126891Strhodesvoid
218126891Strhodeslkpi_free_irq(unsigned int irq, void *device __unused)
219126891Strhodes{
220126891Strhodes	struct irq_ent *irqe;
221126891Strhodes	struct device *dev;
222126891Strhodes
223126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
224126891Strhodes	if (dev == NULL)
225126891Strhodes		return;
226126891Strhodes	irqe = lkpi_irq_ent(dev, irq);
227126891Strhodes	if (irqe == NULL)
228126891Strhodes		return;
229126891Strhodes	lkpi_irq_release(dev, irqe);
230126891Strhodes	kfree(irqe);
231126891Strhodes}
232126891Strhodes
233126891Strhodesvoid
234126891Strhodeslkpi_devm_free_irq(struct device *xdev, unsigned int irq, void *p __unused)
235126891Strhodes{
236126891Strhodes	struct device *dev;
237126891Strhodes	struct irq_ent *irqe;
238126891Strhodes
239126891Strhodes	dev = lkpi_pci_find_irq_dev(irq);
240126891Strhodes	if (dev == NULL)
241126891Strhodes		return;
242126891Strhodes	if (xdev != dev)
243126891Strhodes		return;
244126891Strhodes	irqe = lkpi_irq_ent(dev, irq);
245126891Strhodes	if (irqe == NULL)
246126891Strhodes		return;
247126891Strhodes	lkpi_irq_release(dev, irqe);
248126891Strhodes	lkpi_devres_unlink(dev, irqe);
249126891Strhodes	lkpi_devres_free(irqe);
2504Srgrimes	return;
25198648Sjdp}
25298648Sjdp