1 /* 2 * Copyright (c) 2018-2019 François Tigeot <ftigeot@wolfpond.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <linux/interrupt.h> 28 #include <linux/device.h> 29 #include <drm/drmP.h> 30 31 #include <sys/bus.h> 32 #include <bus/pci/pcivar.h> 33 34 struct irq_data { 35 unsigned int irq; 36 void *dev_id; 37 irq_handler_t handler; 38 const char *name; 39 int rid; 40 struct resource *resource; 41 void *cookiep; 42 struct lwkt_serialize irq_lock; 43 SLIST_ENTRY(irq_data) id_irq_entries; 44 }; 45 46 struct lock irqdata_lock = LOCK_INITIALIZER("dlidl", 0, LK_CANRECURSE); 47 48 SLIST_HEAD(irq_data_list_head, irq_data) irq_list = SLIST_HEAD_INITIALIZER(irq_list); 49 50 /* DragonFly irq handler, used to invoke Linux ones */ 51 static void 52 linux_irq_handler(void *arg) 53 { 54 struct irq_data *irq_entry = arg; 55 56 irq_entry->handler(irq_entry->irq, irq_entry->dev_id); 57 } 58 59 /* 60 * dev is a struct drm_device* 61 * returns: zero on success, non-zero on failure 62 */ 63 int 64 request_irq(unsigned int irq, irq_handler_t handler, 65 unsigned long flags, const char *name, void *dev) 66 { 67 int error; 68 struct irq_data *irq_entry; 69 struct drm_device *ddev = dev; 70 device_t bdev = ddev->dev->bsddev; 71 72 irq_entry = kmalloc(sizeof(*irq_entry), M_DRM, M_WAITOK); 73 74 /* From drm_init_pdev() */ 75 irq_entry->rid = ddev->pdev->_irqrid; 76 irq_entry->resource = ddev->pdev->_irqr; 77 78 irq_entry->irq = irq; 79 irq_entry->dev_id = dev; 80 irq_entry->handler = handler; 81 irq_entry->name = name; 82 lwkt_serialize_init(&irq_entry->irq_lock); 83 84 error = bus_setup_intr(bdev, irq_entry->resource, INTR_MPSAFE, 85 linux_irq_handler, irq_entry, &irq_entry->cookiep, 86 &irq_entry->irq_lock); 87 if (error) { 88 kprintf("request_irq: failed in bus_setup_intr()\n"); 89 bus_release_resource(bdev, SYS_RES_IRQ, 90 irq_entry->rid, irq_entry->resource); 91 kfree(irq_entry); 92 return -error; 93 } 94 lockmgr(&irqdata_lock, LK_EXCLUSIVE); 95 SLIST_INSERT_HEAD(&irq_list, irq_entry, id_irq_entries); 96 lockmgr(&irqdata_lock, LK_RELEASE); 97 98 return 0; 99 } 100 101 /* dev_id is a struct drm_device* */ 102 void 103 free_irq(unsigned int irq, void *dev_id) 104 { 105 struct irq_data *irq_entry, *tmp_ie; 106 struct drm_device *ddev = dev_id; 107 device_t bsddev = ddev->dev->bsddev; 108 struct resource *res = ddev->pdev->_irqr; 109 int found = 0; 110 111 SLIST_FOREACH_MUTABLE(irq_entry, &irq_list, id_irq_entries, tmp_ie) { 112 if ((irq_entry->irq == irq) && (irq_entry->dev_id == dev_id)) { 113 found = 1; 114 break; 115 } 116 } 117 118 if (!found) { 119 kprintf("free_irq: irq %d for dev_id %p was not registered\n", 120 irq, dev_id); 121 return; 122 } 123 124 bus_teardown_intr(bsddev, res, irq_entry->cookiep); 125 bus_release_resource(bsddev, SYS_RES_IRQ, irq_entry->rid, res); 126 if (ddev->pdev->_irq_type == PCI_INTR_TYPE_MSI) 127 pci_release_msi(bsddev); 128 129 lockmgr(&irqdata_lock, LK_EXCLUSIVE); 130 SLIST_REMOVE(&irq_list, irq_entry, irq_data, id_irq_entries); 131 lockmgr(&irqdata_lock, LK_RELEASE); 132 kfree(irq_entry); 133 } 134 135 void 136 disable_irq(unsigned int irq) 137 { 138 struct irq_data *irq_entry; 139 struct drm_device *ddev; 140 device_t bsddev; 141 142 SLIST_FOREACH(irq_entry, &irq_list, id_irq_entries) { 143 if (irq_entry->irq == irq) 144 break; 145 } 146 147 kprintf("disabling irq %d\n", irq); 148 149 ddev = irq_entry->dev_id; 150 bsddev = ddev->dev->bsddev; 151 bus_teardown_intr(bsddev, irq_entry->resource, irq_entry->cookiep); 152 } 153 154 void 155 enable_irq(unsigned int irq) 156 { 157 struct irq_data *irq_entry; 158 struct drm_device *ddev; 159 device_t bsddev; 160 161 SLIST_FOREACH(irq_entry, &irq_list, id_irq_entries) { 162 if (irq_entry->irq == irq) 163 break; 164 } 165 166 kprintf("enabling irq %d\n", irq); 167 168 ddev = irq_entry->dev_id; 169 bsddev = ddev->dev->bsddev; 170 bus_setup_intr(bsddev, irq_entry->resource, INTR_MPSAFE, 171 linux_irq_handler, irq_entry, &irq_entry->cookiep, 172 &irq_entry->irq_lock); 173 } 174