1727fd72fSBen Skeggs /* 2727fd72fSBen Skeggs * Copyright 2021 Red Hat Inc. 3727fd72fSBen Skeggs * 4727fd72fSBen Skeggs * Permission is hereby granted, free of charge, to any person obtaining a 5727fd72fSBen Skeggs * copy of this software and associated documentation files (the "Software"), 6727fd72fSBen Skeggs * to deal in the Software without restriction, including without limitation 7727fd72fSBen Skeggs * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8727fd72fSBen Skeggs * and/or sell copies of the Software, and to permit persons to whom the 9727fd72fSBen Skeggs * Software is furnished to do so, subject to the following conditions: 10727fd72fSBen Skeggs * 11727fd72fSBen Skeggs * The above copyright notice and this permission notice shall be included in 12727fd72fSBen Skeggs * all copies or substantial portions of the Software. 13727fd72fSBen Skeggs * 14727fd72fSBen Skeggs * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15727fd72fSBen Skeggs * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16727fd72fSBen Skeggs * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17727fd72fSBen Skeggs * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18727fd72fSBen Skeggs * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19727fd72fSBen Skeggs * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20727fd72fSBen Skeggs * OTHER DEALINGS IN THE SOFTWARE. 21727fd72fSBen Skeggs */ 22727fd72fSBen Skeggs #include <core/intr.h> 233ebd64aaSBen Skeggs #include <core/device.h> 243ebd64aaSBen Skeggs #include <core/subdev.h> 25727fd72fSBen Skeggs #include <subdev/pci.h> 263ebd64aaSBen Skeggs #include <subdev/top.h> 273ebd64aaSBen Skeggs 283ebd64aaSBen Skeggs static int 293ebd64aaSBen Skeggs nvkm_intr_xlat(struct nvkm_subdev *subdev, struct nvkm_intr *intr, 303ebd64aaSBen Skeggs enum nvkm_intr_type type, int *leaf, u32 *mask) 313ebd64aaSBen Skeggs { 323ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 333ebd64aaSBen Skeggs 343ebd64aaSBen Skeggs if (type < NVKM_INTR_VECTOR_0) { 353ebd64aaSBen Skeggs if (type == NVKM_INTR_SUBDEV) { 363ebd64aaSBen Skeggs const struct nvkm_intr_data *data = intr->data; 373ebd64aaSBen Skeggs struct nvkm_top_device *tdev; 383ebd64aaSBen Skeggs 393ebd64aaSBen Skeggs while (data && data->mask) { 403ebd64aaSBen Skeggs if (data->type == NVKM_SUBDEV_TOP) { 413ebd64aaSBen Skeggs list_for_each_entry(tdev, &device->top->device, head) { 423ebd64aaSBen Skeggs if (tdev->intr >= 0 && 433ebd64aaSBen Skeggs tdev->type == subdev->type && 443ebd64aaSBen Skeggs tdev->inst == subdev->inst) { 453ebd64aaSBen Skeggs if (data->mask & BIT(tdev->intr)) { 463ebd64aaSBen Skeggs *leaf = data->leaf; 473ebd64aaSBen Skeggs *mask = BIT(tdev->intr); 483ebd64aaSBen Skeggs return 0; 493ebd64aaSBen Skeggs } 503ebd64aaSBen Skeggs } 513ebd64aaSBen Skeggs } 523ebd64aaSBen Skeggs } else 533ebd64aaSBen Skeggs if (data->type == subdev->type && data->inst == subdev->inst) { 543ebd64aaSBen Skeggs *leaf = data->leaf; 553ebd64aaSBen Skeggs *mask = data->mask; 563ebd64aaSBen Skeggs return 0; 573ebd64aaSBen Skeggs } 583ebd64aaSBen Skeggs 593ebd64aaSBen Skeggs data++; 603ebd64aaSBen Skeggs } 613ebd64aaSBen Skeggs } else { 623ebd64aaSBen Skeggs return -ENOSYS; 633ebd64aaSBen Skeggs } 643ebd64aaSBen Skeggs } else { 653ebd64aaSBen Skeggs if (type < intr->leaves * sizeof(*intr->stat) * 8) { 663ebd64aaSBen Skeggs *leaf = type / 32; 673ebd64aaSBen Skeggs *mask = BIT(type % 32); 683ebd64aaSBen Skeggs return 0; 693ebd64aaSBen Skeggs } 703ebd64aaSBen Skeggs } 713ebd64aaSBen Skeggs 723ebd64aaSBen Skeggs return -EINVAL; 733ebd64aaSBen Skeggs } 743ebd64aaSBen Skeggs 753ebd64aaSBen Skeggs static struct nvkm_intr * 763ebd64aaSBen Skeggs nvkm_intr_find(struct nvkm_subdev *subdev, enum nvkm_intr_type type, int *leaf, u32 *mask) 773ebd64aaSBen Skeggs { 783ebd64aaSBen Skeggs struct nvkm_intr *intr; 793ebd64aaSBen Skeggs int ret; 803ebd64aaSBen Skeggs 813ebd64aaSBen Skeggs list_for_each_entry(intr, &subdev->device->intr.intr, head) { 823ebd64aaSBen Skeggs ret = nvkm_intr_xlat(subdev, intr, type, leaf, mask); 833ebd64aaSBen Skeggs if (ret == 0) 843ebd64aaSBen Skeggs return intr; 853ebd64aaSBen Skeggs } 863ebd64aaSBen Skeggs 873ebd64aaSBen Skeggs return NULL; 883ebd64aaSBen Skeggs } 893ebd64aaSBen Skeggs 903ebd64aaSBen Skeggs static void 913ebd64aaSBen Skeggs nvkm_intr_allow_locked(struct nvkm_intr *intr, int leaf, u32 mask) 923ebd64aaSBen Skeggs { 933ebd64aaSBen Skeggs intr->mask[leaf] |= mask; 943ebd64aaSBen Skeggs if (intr->func->allow) { 953ebd64aaSBen Skeggs if (intr->func->reset) 963ebd64aaSBen Skeggs intr->func->reset(intr, leaf, mask); 973ebd64aaSBen Skeggs intr->func->allow(intr, leaf, mask); 983ebd64aaSBen Skeggs } 993ebd64aaSBen Skeggs } 1003ebd64aaSBen Skeggs 1013ebd64aaSBen Skeggs void 1023ebd64aaSBen Skeggs nvkm_intr_allow(struct nvkm_subdev *subdev, enum nvkm_intr_type type) 1033ebd64aaSBen Skeggs { 1043ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 1053ebd64aaSBen Skeggs struct nvkm_intr *intr; 1063ebd64aaSBen Skeggs unsigned long flags; 1073ebd64aaSBen Skeggs int leaf; 1083ebd64aaSBen Skeggs u32 mask; 1093ebd64aaSBen Skeggs 1103ebd64aaSBen Skeggs intr = nvkm_intr_find(subdev, type, &leaf, &mask); 1113ebd64aaSBen Skeggs if (intr) { 1123ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x allowed by %s\n", leaf, mask, subdev->name); 1133ebd64aaSBen Skeggs spin_lock_irqsave(&device->intr.lock, flags); 1143ebd64aaSBen Skeggs nvkm_intr_allow_locked(intr, leaf, mask); 1153ebd64aaSBen Skeggs spin_unlock_irqrestore(&device->intr.lock, flags); 1163ebd64aaSBen Skeggs } 1173ebd64aaSBen Skeggs } 1183ebd64aaSBen Skeggs 1193ebd64aaSBen Skeggs static void 1203ebd64aaSBen Skeggs nvkm_intr_block_locked(struct nvkm_intr *intr, int leaf, u32 mask) 1213ebd64aaSBen Skeggs { 1223ebd64aaSBen Skeggs intr->mask[leaf] &= ~mask; 1233ebd64aaSBen Skeggs if (intr->func->block) 1243ebd64aaSBen Skeggs intr->func->block(intr, leaf, mask); 1253ebd64aaSBen Skeggs } 1263ebd64aaSBen Skeggs 1273ebd64aaSBen Skeggs void 1283ebd64aaSBen Skeggs nvkm_intr_block(struct nvkm_subdev *subdev, enum nvkm_intr_type type) 1293ebd64aaSBen Skeggs { 1303ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 1313ebd64aaSBen Skeggs struct nvkm_intr *intr; 1323ebd64aaSBen Skeggs unsigned long flags; 1333ebd64aaSBen Skeggs int leaf; 1343ebd64aaSBen Skeggs u32 mask; 1353ebd64aaSBen Skeggs 1363ebd64aaSBen Skeggs intr = nvkm_intr_find(subdev, type, &leaf, &mask); 1373ebd64aaSBen Skeggs if (intr) { 1383ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x blocked by %s\n", leaf, mask, subdev->name); 1393ebd64aaSBen Skeggs spin_lock_irqsave(&device->intr.lock, flags); 1403ebd64aaSBen Skeggs nvkm_intr_block_locked(intr, leaf, mask); 1413ebd64aaSBen Skeggs spin_unlock_irqrestore(&device->intr.lock, flags); 1423ebd64aaSBen Skeggs } 1433ebd64aaSBen Skeggs } 1443ebd64aaSBen Skeggs 145727fd72fSBen Skeggs static void 146727fd72fSBen Skeggs nvkm_intr_rearm_locked(struct nvkm_device *device) 147727fd72fSBen Skeggs { 1483ebd64aaSBen Skeggs struct nvkm_intr *intr; 1493ebd64aaSBen Skeggs 1503ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 1513ebd64aaSBen Skeggs intr->func->rearm(intr); 152727fd72fSBen Skeggs } 153727fd72fSBen Skeggs 154727fd72fSBen Skeggs static void 155727fd72fSBen Skeggs nvkm_intr_unarm_locked(struct nvkm_device *device) 156727fd72fSBen Skeggs { 1573ebd64aaSBen Skeggs struct nvkm_intr *intr; 1583ebd64aaSBen Skeggs 1593ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 1603ebd64aaSBen Skeggs intr->func->unarm(intr); 161727fd72fSBen Skeggs } 162727fd72fSBen Skeggs 163727fd72fSBen Skeggs static irqreturn_t 164727fd72fSBen Skeggs nvkm_intr(int irq, void *arg) 165727fd72fSBen Skeggs { 166727fd72fSBen Skeggs struct nvkm_device *device = arg; 1673ebd64aaSBen Skeggs struct nvkm_intr *intr; 1683ebd64aaSBen Skeggs struct nvkm_inth *inth; 169727fd72fSBen Skeggs irqreturn_t ret = IRQ_NONE; 170*fe76fe49SBen Skeggs bool pending = false; 1713ebd64aaSBen Skeggs int prio, leaf; 172727fd72fSBen Skeggs 1733ebd64aaSBen Skeggs /* Disable all top-level interrupt sources, and re-arm MSI interrupts. */ 174727fd72fSBen Skeggs spin_lock(&device->intr.lock); 175727fd72fSBen Skeggs if (!device->intr.armed) 176727fd72fSBen Skeggs goto done_unlock; 177727fd72fSBen Skeggs 178727fd72fSBen Skeggs nvkm_intr_unarm_locked(device); 179727fd72fSBen Skeggs nvkm_pci_msi_rearm(device); 180727fd72fSBen Skeggs 1813ebd64aaSBen Skeggs /* Fetch pending interrupt masks. */ 1823ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 1833ebd64aaSBen Skeggs if (intr->func->pending(intr)) 1843ebd64aaSBen Skeggs pending = true; 1853ebd64aaSBen Skeggs } 1863ebd64aaSBen Skeggs 1873ebd64aaSBen Skeggs if (!pending) 1883ebd64aaSBen Skeggs goto done; 1893ebd64aaSBen Skeggs 1903ebd64aaSBen Skeggs /* Check that GPU is still on the bus by reading NV_PMC_BOOT_0. */ 1913ebd64aaSBen Skeggs if (WARN_ON(nvkm_rd32(device, 0x000000) == 0xffffffff)) 1923ebd64aaSBen Skeggs goto done; 1933ebd64aaSBen Skeggs 1943ebd64aaSBen Skeggs /* Execute handlers. */ 1953ebd64aaSBen Skeggs for (prio = 0; prio < ARRAY_SIZE(device->intr.prio); prio++) { 1963ebd64aaSBen Skeggs list_for_each_entry(inth, &device->intr.prio[prio], head) { 1973ebd64aaSBen Skeggs struct nvkm_intr *intr = inth->intr; 1983ebd64aaSBen Skeggs 1993ebd64aaSBen Skeggs if (intr->stat[inth->leaf] & inth->mask) { 2003ebd64aaSBen Skeggs if (atomic_read(&inth->allowed)) { 2013ebd64aaSBen Skeggs if (intr->func->reset) 2023ebd64aaSBen Skeggs intr->func->reset(intr, inth->leaf, inth->mask); 2033ebd64aaSBen Skeggs if (inth->func(inth) == IRQ_HANDLED) 2043ebd64aaSBen Skeggs ret = IRQ_HANDLED; 2053ebd64aaSBen Skeggs } 2063ebd64aaSBen Skeggs } 2073ebd64aaSBen Skeggs } 2083ebd64aaSBen Skeggs } 2093ebd64aaSBen Skeggs 2103ebd64aaSBen Skeggs /* Nothing handled? Some debugging/protection from IRQ storms is in order... */ 2113ebd64aaSBen Skeggs if (ret == IRQ_NONE) { 2123ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 2133ebd64aaSBen Skeggs for (leaf = 0; leaf < intr->leaves; leaf++) { 2143ebd64aaSBen Skeggs if (intr->stat[leaf]) { 2153ebd64aaSBen Skeggs nvkm_warn(intr->subdev, "intr%d: %08x\n", 2163ebd64aaSBen Skeggs leaf, intr->stat[leaf]); 2173ebd64aaSBen Skeggs nvkm_intr_block_locked(intr, leaf, intr->stat[leaf]); 2183ebd64aaSBen Skeggs } 2193ebd64aaSBen Skeggs } 2203ebd64aaSBen Skeggs } 2213ebd64aaSBen Skeggs } 2223ebd64aaSBen Skeggs 2233ebd64aaSBen Skeggs done: 2243ebd64aaSBen Skeggs /* Re-enable all top-level interrupt sources. */ 225727fd72fSBen Skeggs nvkm_intr_rearm_locked(device); 226727fd72fSBen Skeggs done_unlock: 227727fd72fSBen Skeggs spin_unlock(&device->intr.lock); 228727fd72fSBen Skeggs return ret; 229727fd72fSBen Skeggs } 230727fd72fSBen Skeggs 2313ebd64aaSBen Skeggs int 2323ebd64aaSBen Skeggs nvkm_intr_add(const struct nvkm_intr_func *func, const struct nvkm_intr_data *data, 2333ebd64aaSBen Skeggs struct nvkm_subdev *subdev, int leaves, struct nvkm_intr *intr) 2343ebd64aaSBen Skeggs { 2353ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 2363ebd64aaSBen Skeggs int i; 2373ebd64aaSBen Skeggs 2383ebd64aaSBen Skeggs intr->func = func; 2393ebd64aaSBen Skeggs intr->data = data; 2403ebd64aaSBen Skeggs intr->subdev = subdev; 2413ebd64aaSBen Skeggs intr->leaves = leaves; 2423ebd64aaSBen Skeggs intr->stat = kcalloc(leaves, sizeof(*intr->stat), GFP_KERNEL); 2433ebd64aaSBen Skeggs intr->mask = kcalloc(leaves, sizeof(*intr->mask), GFP_KERNEL); 2443ebd64aaSBen Skeggs if (!intr->stat || !intr->mask) { 2453ebd64aaSBen Skeggs kfree(intr->stat); 2463ebd64aaSBen Skeggs return -ENOMEM; 2473ebd64aaSBen Skeggs } 2483ebd64aaSBen Skeggs 2493ebd64aaSBen Skeggs if (intr->subdev->debug >= NV_DBG_DEBUG) { 2503ebd64aaSBen Skeggs for (i = 0; i < intr->leaves; i++) 2513ebd64aaSBen Skeggs intr->mask[i] = ~0; 2523ebd64aaSBen Skeggs } 2533ebd64aaSBen Skeggs 2543ebd64aaSBen Skeggs spin_lock_irq(&device->intr.lock); 2553ebd64aaSBen Skeggs list_add_tail(&intr->head, &device->intr.intr); 2563ebd64aaSBen Skeggs spin_unlock_irq(&device->intr.lock); 2573ebd64aaSBen Skeggs return 0; 2583ebd64aaSBen Skeggs } 2593ebd64aaSBen Skeggs 260a7ab200aSBen Skeggs static irqreturn_t 261a7ab200aSBen Skeggs nvkm_intr_subdev(struct nvkm_inth *inth) 262a7ab200aSBen Skeggs { 263a7ab200aSBen Skeggs struct nvkm_subdev *subdev = container_of(inth, typeof(*subdev), inth); 264a7ab200aSBen Skeggs 265a7ab200aSBen Skeggs nvkm_subdev_intr(subdev); 266a7ab200aSBen Skeggs return IRQ_HANDLED; 267a7ab200aSBen Skeggs } 268a7ab200aSBen Skeggs 269a7ab200aSBen Skeggs static void 270a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(struct nvkm_intr *intr, enum nvkm_subdev_type type, int inst) 271a7ab200aSBen Skeggs { 272a7ab200aSBen Skeggs struct nvkm_subdev *subdev; 273a7ab200aSBen Skeggs enum nvkm_intr_prio prio; 274a7ab200aSBen Skeggs int ret; 275a7ab200aSBen Skeggs 276a7ab200aSBen Skeggs subdev = nvkm_device_subdev(intr->subdev->device, type, inst); 277a7ab200aSBen Skeggs if (!subdev || !subdev->func->intr) 278a7ab200aSBen Skeggs return; 279a7ab200aSBen Skeggs 280a7ab200aSBen Skeggs if (type == NVKM_ENGINE_DISP) 281a7ab200aSBen Skeggs prio = NVKM_INTR_PRIO_VBLANK; 282a7ab200aSBen Skeggs else 283a7ab200aSBen Skeggs prio = NVKM_INTR_PRIO_NORMAL; 284a7ab200aSBen Skeggs 285a7ab200aSBen Skeggs ret = nvkm_inth_add(intr, NVKM_INTR_SUBDEV, prio, subdev, nvkm_intr_subdev, &subdev->inth); 286a7ab200aSBen Skeggs if (WARN_ON(ret)) 287a7ab200aSBen Skeggs return; 288a7ab200aSBen Skeggs 289a7ab200aSBen Skeggs nvkm_inth_allow(&subdev->inth); 290a7ab200aSBen Skeggs } 291a7ab200aSBen Skeggs 292a7ab200aSBen Skeggs static void 293a7ab200aSBen Skeggs nvkm_intr_subdev_add(struct nvkm_intr *intr) 294a7ab200aSBen Skeggs { 295a7ab200aSBen Skeggs const struct nvkm_intr_data *data; 296a7ab200aSBen Skeggs struct nvkm_device *device = intr->subdev->device; 297a7ab200aSBen Skeggs struct nvkm_top_device *tdev; 298a7ab200aSBen Skeggs 299a7ab200aSBen Skeggs for (data = intr->data; data && data->mask; data++) { 300a7ab200aSBen Skeggs if (data->legacy) { 301a7ab200aSBen Skeggs if (data->type == NVKM_SUBDEV_TOP) { 302a7ab200aSBen Skeggs list_for_each_entry(tdev, &device->top->device, head) { 303a7ab200aSBen Skeggs if (tdev->intr < 0 || !(data->mask & BIT(tdev->intr))) 304a7ab200aSBen Skeggs continue; 305a7ab200aSBen Skeggs 306a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(intr, tdev->type, tdev->inst); 307a7ab200aSBen Skeggs } 308a7ab200aSBen Skeggs } else { 309a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(intr, data->type, data->inst); 310a7ab200aSBen Skeggs } 311a7ab200aSBen Skeggs } 312a7ab200aSBen Skeggs } 313a7ab200aSBen Skeggs } 314a7ab200aSBen Skeggs 315727fd72fSBen Skeggs void 316727fd72fSBen Skeggs nvkm_intr_rearm(struct nvkm_device *device) 317727fd72fSBen Skeggs { 3183ebd64aaSBen Skeggs struct nvkm_intr *intr; 3193ebd64aaSBen Skeggs int i; 3203ebd64aaSBen Skeggs 321a7ab200aSBen Skeggs if (unlikely(!device->intr.legacy_done)) { 322a7ab200aSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 323a7ab200aSBen Skeggs nvkm_intr_subdev_add(intr); 324a7ab200aSBen Skeggs device->intr.legacy_done = true; 325a7ab200aSBen Skeggs } 326a7ab200aSBen Skeggs 327727fd72fSBen Skeggs spin_lock_irq(&device->intr.lock); 3283ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 3293ebd64aaSBen Skeggs for (i = 0; intr->func->block && i < intr->leaves; i++) { 3303ebd64aaSBen Skeggs intr->func->block(intr, i, ~0); 3313ebd64aaSBen Skeggs intr->func->allow(intr, i, intr->mask[i]); 3323ebd64aaSBen Skeggs } 3333ebd64aaSBen Skeggs } 3343ebd64aaSBen Skeggs 335727fd72fSBen Skeggs nvkm_intr_rearm_locked(device); 336727fd72fSBen Skeggs device->intr.armed = true; 337727fd72fSBen Skeggs spin_unlock_irq(&device->intr.lock); 338727fd72fSBen Skeggs } 339727fd72fSBen Skeggs 340727fd72fSBen Skeggs void 341727fd72fSBen Skeggs nvkm_intr_unarm(struct nvkm_device *device) 342727fd72fSBen Skeggs { 343727fd72fSBen Skeggs spin_lock_irq(&device->intr.lock); 344727fd72fSBen Skeggs nvkm_intr_unarm_locked(device); 345727fd72fSBen Skeggs device->intr.armed = false; 346727fd72fSBen Skeggs spin_unlock_irq(&device->intr.lock); 347727fd72fSBen Skeggs } 348727fd72fSBen Skeggs 349727fd72fSBen Skeggs int 350727fd72fSBen Skeggs nvkm_intr_install(struct nvkm_device *device) 351727fd72fSBen Skeggs { 352727fd72fSBen Skeggs int ret; 353727fd72fSBen Skeggs 354727fd72fSBen Skeggs device->intr.irq = device->func->irq(device); 355727fd72fSBen Skeggs if (device->intr.irq < 0) 356727fd72fSBen Skeggs return device->intr.irq; 357727fd72fSBen Skeggs 358727fd72fSBen Skeggs ret = request_irq(device->intr.irq, nvkm_intr, IRQF_SHARED, "nvkm", device); 359727fd72fSBen Skeggs if (ret) 360727fd72fSBen Skeggs return ret; 361727fd72fSBen Skeggs 362727fd72fSBen Skeggs device->intr.alloc = true; 363727fd72fSBen Skeggs return 0; 364727fd72fSBen Skeggs } 365727fd72fSBen Skeggs 366727fd72fSBen Skeggs void 367727fd72fSBen Skeggs nvkm_intr_dtor(struct nvkm_device *device) 368727fd72fSBen Skeggs { 3693ebd64aaSBen Skeggs struct nvkm_intr *intr, *intt; 3703ebd64aaSBen Skeggs 3713ebd64aaSBen Skeggs list_for_each_entry_safe(intr, intt, &device->intr.intr, head) { 3723ebd64aaSBen Skeggs list_del(&intr->head); 3733ebd64aaSBen Skeggs kfree(intr->mask); 3743ebd64aaSBen Skeggs kfree(intr->stat); 3753ebd64aaSBen Skeggs } 3763ebd64aaSBen Skeggs 377727fd72fSBen Skeggs if (device->intr.alloc) 378727fd72fSBen Skeggs free_irq(device->intr.irq, device); 379727fd72fSBen Skeggs } 380727fd72fSBen Skeggs 381727fd72fSBen Skeggs void 382727fd72fSBen Skeggs nvkm_intr_ctor(struct nvkm_device *device) 383727fd72fSBen Skeggs { 3843ebd64aaSBen Skeggs int i; 3853ebd64aaSBen Skeggs 3863ebd64aaSBen Skeggs INIT_LIST_HEAD(&device->intr.intr); 3873ebd64aaSBen Skeggs for (i = 0; i < ARRAY_SIZE(device->intr.prio); i++) 3883ebd64aaSBen Skeggs INIT_LIST_HEAD(&device->intr.prio[i]); 3893ebd64aaSBen Skeggs 390727fd72fSBen Skeggs spin_lock_init(&device->intr.lock); 3913ebd64aaSBen Skeggs device->intr.armed = false; 3923ebd64aaSBen Skeggs } 3933ebd64aaSBen Skeggs 3943ebd64aaSBen Skeggs void 3953ebd64aaSBen Skeggs nvkm_inth_block(struct nvkm_inth *inth) 3963ebd64aaSBen Skeggs { 3973ebd64aaSBen Skeggs if (unlikely(!inth->intr)) 3983ebd64aaSBen Skeggs return; 3993ebd64aaSBen Skeggs 4003ebd64aaSBen Skeggs atomic_set(&inth->allowed, 0); 4013ebd64aaSBen Skeggs } 4023ebd64aaSBen Skeggs 4033ebd64aaSBen Skeggs void 4043ebd64aaSBen Skeggs nvkm_inth_allow(struct nvkm_inth *inth) 4053ebd64aaSBen Skeggs { 4063ebd64aaSBen Skeggs struct nvkm_intr *intr = inth->intr; 4073ebd64aaSBen Skeggs unsigned long flags; 4083ebd64aaSBen Skeggs 4093ebd64aaSBen Skeggs if (unlikely(!inth->intr)) 4103ebd64aaSBen Skeggs return; 4113ebd64aaSBen Skeggs 4123ebd64aaSBen Skeggs spin_lock_irqsave(&intr->subdev->device->intr.lock, flags); 4133ebd64aaSBen Skeggs if (!atomic_xchg(&inth->allowed, 1)) { 4143ebd64aaSBen Skeggs if ((intr->mask[inth->leaf] & inth->mask) != inth->mask) 4153ebd64aaSBen Skeggs nvkm_intr_allow_locked(intr, inth->leaf, inth->mask); 4163ebd64aaSBen Skeggs } 4173ebd64aaSBen Skeggs spin_unlock_irqrestore(&intr->subdev->device->intr.lock, flags); 4183ebd64aaSBen Skeggs } 4193ebd64aaSBen Skeggs 4203ebd64aaSBen Skeggs int 4213ebd64aaSBen Skeggs nvkm_inth_add(struct nvkm_intr *intr, enum nvkm_intr_type type, enum nvkm_intr_prio prio, 4223ebd64aaSBen Skeggs struct nvkm_subdev *subdev, nvkm_inth_func func, struct nvkm_inth *inth) 4233ebd64aaSBen Skeggs { 4243ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 4253ebd64aaSBen Skeggs int ret; 4263ebd64aaSBen Skeggs 4273ebd64aaSBen Skeggs if (WARN_ON(inth->mask)) 4283ebd64aaSBen Skeggs return -EBUSY; 4293ebd64aaSBen Skeggs 4303ebd64aaSBen Skeggs ret = nvkm_intr_xlat(subdev, intr, type, &inth->leaf, &inth->mask); 4313ebd64aaSBen Skeggs if (ret) 4323ebd64aaSBen Skeggs return ret; 4333ebd64aaSBen Skeggs 4343ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x requested by %s\n", 4353ebd64aaSBen Skeggs inth->leaf, inth->mask, subdev->name); 4363ebd64aaSBen Skeggs 4373ebd64aaSBen Skeggs inth->intr = intr; 4383ebd64aaSBen Skeggs inth->func = func; 4393ebd64aaSBen Skeggs atomic_set(&inth->allowed, 0); 4403ebd64aaSBen Skeggs list_add_tail(&inth->head, &device->intr.prio[prio]); 4413ebd64aaSBen Skeggs return 0; 442727fd72fSBen Skeggs } 443