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