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 28727fd72fSBen Skeggs #include <subdev/mc.h> 29727fd72fSBen Skeggs 303ebd64aaSBen Skeggs static int 313ebd64aaSBen Skeggs nvkm_intr_xlat(struct nvkm_subdev *subdev, struct nvkm_intr *intr, 323ebd64aaSBen Skeggs enum nvkm_intr_type type, int *leaf, u32 *mask) 333ebd64aaSBen Skeggs { 343ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 353ebd64aaSBen Skeggs 363ebd64aaSBen Skeggs if (type < NVKM_INTR_VECTOR_0) { 373ebd64aaSBen Skeggs if (type == NVKM_INTR_SUBDEV) { 383ebd64aaSBen Skeggs const struct nvkm_intr_data *data = intr->data; 393ebd64aaSBen Skeggs struct nvkm_top_device *tdev; 403ebd64aaSBen Skeggs 413ebd64aaSBen Skeggs while (data && data->mask) { 423ebd64aaSBen Skeggs if (data->type == NVKM_SUBDEV_TOP) { 433ebd64aaSBen Skeggs list_for_each_entry(tdev, &device->top->device, head) { 443ebd64aaSBen Skeggs if (tdev->intr >= 0 && 453ebd64aaSBen Skeggs tdev->type == subdev->type && 463ebd64aaSBen Skeggs tdev->inst == subdev->inst) { 473ebd64aaSBen Skeggs if (data->mask & BIT(tdev->intr)) { 483ebd64aaSBen Skeggs *leaf = data->leaf; 493ebd64aaSBen Skeggs *mask = BIT(tdev->intr); 503ebd64aaSBen Skeggs return 0; 513ebd64aaSBen Skeggs } 523ebd64aaSBen Skeggs } 533ebd64aaSBen Skeggs } 543ebd64aaSBen Skeggs } else 553ebd64aaSBen Skeggs if (data->type == subdev->type && data->inst == subdev->inst) { 563ebd64aaSBen Skeggs *leaf = data->leaf; 573ebd64aaSBen Skeggs *mask = data->mask; 583ebd64aaSBen Skeggs return 0; 593ebd64aaSBen Skeggs } 603ebd64aaSBen Skeggs 613ebd64aaSBen Skeggs data++; 623ebd64aaSBen Skeggs } 633ebd64aaSBen Skeggs } else { 643ebd64aaSBen Skeggs return -ENOSYS; 653ebd64aaSBen Skeggs } 663ebd64aaSBen Skeggs } else { 673ebd64aaSBen Skeggs if (type < intr->leaves * sizeof(*intr->stat) * 8) { 683ebd64aaSBen Skeggs *leaf = type / 32; 693ebd64aaSBen Skeggs *mask = BIT(type % 32); 703ebd64aaSBen Skeggs return 0; 713ebd64aaSBen Skeggs } 723ebd64aaSBen Skeggs } 733ebd64aaSBen Skeggs 743ebd64aaSBen Skeggs return -EINVAL; 753ebd64aaSBen Skeggs } 763ebd64aaSBen Skeggs 773ebd64aaSBen Skeggs static struct nvkm_intr * 783ebd64aaSBen Skeggs nvkm_intr_find(struct nvkm_subdev *subdev, enum nvkm_intr_type type, int *leaf, u32 *mask) 793ebd64aaSBen Skeggs { 803ebd64aaSBen Skeggs struct nvkm_intr *intr; 813ebd64aaSBen Skeggs int ret; 823ebd64aaSBen Skeggs 833ebd64aaSBen Skeggs list_for_each_entry(intr, &subdev->device->intr.intr, head) { 843ebd64aaSBen Skeggs ret = nvkm_intr_xlat(subdev, intr, type, leaf, mask); 853ebd64aaSBen Skeggs if (ret == 0) 863ebd64aaSBen Skeggs return intr; 873ebd64aaSBen Skeggs } 883ebd64aaSBen Skeggs 893ebd64aaSBen Skeggs return NULL; 903ebd64aaSBen Skeggs } 913ebd64aaSBen Skeggs 923ebd64aaSBen Skeggs static void 933ebd64aaSBen Skeggs nvkm_intr_allow_locked(struct nvkm_intr *intr, int leaf, u32 mask) 943ebd64aaSBen Skeggs { 953ebd64aaSBen Skeggs intr->mask[leaf] |= mask; 963ebd64aaSBen Skeggs if (intr->func->allow) { 973ebd64aaSBen Skeggs if (intr->func->reset) 983ebd64aaSBen Skeggs intr->func->reset(intr, leaf, mask); 993ebd64aaSBen Skeggs intr->func->allow(intr, leaf, mask); 1003ebd64aaSBen Skeggs } 1013ebd64aaSBen Skeggs } 1023ebd64aaSBen Skeggs 1033ebd64aaSBen Skeggs void 1043ebd64aaSBen Skeggs nvkm_intr_allow(struct nvkm_subdev *subdev, enum nvkm_intr_type type) 1053ebd64aaSBen Skeggs { 1063ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 1073ebd64aaSBen Skeggs struct nvkm_intr *intr; 1083ebd64aaSBen Skeggs unsigned long flags; 1093ebd64aaSBen Skeggs int leaf; 1103ebd64aaSBen Skeggs u32 mask; 1113ebd64aaSBen Skeggs 1123ebd64aaSBen Skeggs intr = nvkm_intr_find(subdev, type, &leaf, &mask); 1133ebd64aaSBen Skeggs if (intr) { 1143ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x allowed by %s\n", leaf, mask, subdev->name); 1153ebd64aaSBen Skeggs spin_lock_irqsave(&device->intr.lock, flags); 1163ebd64aaSBen Skeggs nvkm_intr_allow_locked(intr, leaf, mask); 1173ebd64aaSBen Skeggs spin_unlock_irqrestore(&device->intr.lock, flags); 1183ebd64aaSBen Skeggs } 1193ebd64aaSBen Skeggs } 1203ebd64aaSBen Skeggs 1213ebd64aaSBen Skeggs static void 1223ebd64aaSBen Skeggs nvkm_intr_block_locked(struct nvkm_intr *intr, int leaf, u32 mask) 1233ebd64aaSBen Skeggs { 1243ebd64aaSBen Skeggs intr->mask[leaf] &= ~mask; 1253ebd64aaSBen Skeggs if (intr->func->block) 1263ebd64aaSBen Skeggs intr->func->block(intr, leaf, mask); 1273ebd64aaSBen Skeggs } 1283ebd64aaSBen Skeggs 1293ebd64aaSBen Skeggs void 1303ebd64aaSBen Skeggs nvkm_intr_block(struct nvkm_subdev *subdev, enum nvkm_intr_type type) 1313ebd64aaSBen Skeggs { 1323ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 1333ebd64aaSBen Skeggs struct nvkm_intr *intr; 1343ebd64aaSBen Skeggs unsigned long flags; 1353ebd64aaSBen Skeggs int leaf; 1363ebd64aaSBen Skeggs u32 mask; 1373ebd64aaSBen Skeggs 1383ebd64aaSBen Skeggs intr = nvkm_intr_find(subdev, type, &leaf, &mask); 1393ebd64aaSBen Skeggs if (intr) { 1403ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x blocked by %s\n", leaf, mask, subdev->name); 1413ebd64aaSBen Skeggs spin_lock_irqsave(&device->intr.lock, flags); 1423ebd64aaSBen Skeggs nvkm_intr_block_locked(intr, leaf, mask); 1433ebd64aaSBen Skeggs spin_unlock_irqrestore(&device->intr.lock, flags); 1443ebd64aaSBen Skeggs } 1453ebd64aaSBen Skeggs } 1463ebd64aaSBen Skeggs 147727fd72fSBen Skeggs static void 148727fd72fSBen Skeggs nvkm_intr_rearm_locked(struct nvkm_device *device) 149727fd72fSBen Skeggs { 1503ebd64aaSBen Skeggs struct nvkm_intr *intr; 1513ebd64aaSBen Skeggs 1523ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 1533ebd64aaSBen Skeggs intr->func->rearm(intr); 154727fd72fSBen Skeggs nvkm_mc_intr_rearm(device); 155727fd72fSBen Skeggs } 156727fd72fSBen Skeggs 157727fd72fSBen Skeggs static void 158727fd72fSBen Skeggs nvkm_intr_unarm_locked(struct nvkm_device *device) 159727fd72fSBen Skeggs { 1603ebd64aaSBen Skeggs struct nvkm_intr *intr; 1613ebd64aaSBen Skeggs 1623ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 1633ebd64aaSBen Skeggs intr->func->unarm(intr); 164727fd72fSBen Skeggs nvkm_mc_intr_unarm(device); 165727fd72fSBen Skeggs } 166727fd72fSBen Skeggs 167727fd72fSBen Skeggs static irqreturn_t 168727fd72fSBen Skeggs nvkm_intr(int irq, void *arg) 169727fd72fSBen Skeggs { 170727fd72fSBen Skeggs struct nvkm_device *device = arg; 1713ebd64aaSBen Skeggs struct nvkm_intr *intr; 1723ebd64aaSBen Skeggs struct nvkm_inth *inth; 173727fd72fSBen Skeggs irqreturn_t ret = IRQ_NONE; 1743ebd64aaSBen Skeggs bool pending = false, handled; 1753ebd64aaSBen Skeggs int prio, leaf; 176727fd72fSBen Skeggs 1773ebd64aaSBen Skeggs /* Disable all top-level interrupt sources, and re-arm MSI interrupts. */ 178727fd72fSBen Skeggs spin_lock(&device->intr.lock); 179727fd72fSBen Skeggs if (!device->intr.armed) 180727fd72fSBen Skeggs goto done_unlock; 181727fd72fSBen Skeggs 182727fd72fSBen Skeggs nvkm_intr_unarm_locked(device); 183727fd72fSBen Skeggs nvkm_pci_msi_rearm(device); 184727fd72fSBen Skeggs 1853ebd64aaSBen Skeggs /* Fetch pending interrupt masks. */ 1863ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 1873ebd64aaSBen Skeggs if (intr->func->pending(intr)) 1883ebd64aaSBen Skeggs pending = true; 1893ebd64aaSBen Skeggs } 1903ebd64aaSBen Skeggs 191727fd72fSBen Skeggs nvkm_mc_intr(device, &handled); 192727fd72fSBen Skeggs if (handled) 193727fd72fSBen Skeggs ret = IRQ_HANDLED; 194727fd72fSBen Skeggs 1953ebd64aaSBen Skeggs if (!pending) 1963ebd64aaSBen Skeggs goto done; 1973ebd64aaSBen Skeggs 1983ebd64aaSBen Skeggs /* Check that GPU is still on the bus by reading NV_PMC_BOOT_0. */ 1993ebd64aaSBen Skeggs if (WARN_ON(nvkm_rd32(device, 0x000000) == 0xffffffff)) 2003ebd64aaSBen Skeggs goto done; 2013ebd64aaSBen Skeggs 2023ebd64aaSBen Skeggs /* Execute handlers. */ 2033ebd64aaSBen Skeggs for (prio = 0; prio < ARRAY_SIZE(device->intr.prio); prio++) { 2043ebd64aaSBen Skeggs list_for_each_entry(inth, &device->intr.prio[prio], head) { 2053ebd64aaSBen Skeggs struct nvkm_intr *intr = inth->intr; 2063ebd64aaSBen Skeggs 2073ebd64aaSBen Skeggs if (intr->stat[inth->leaf] & inth->mask) { 2083ebd64aaSBen Skeggs if (atomic_read(&inth->allowed)) { 2093ebd64aaSBen Skeggs if (intr->func->reset) 2103ebd64aaSBen Skeggs intr->func->reset(intr, inth->leaf, inth->mask); 2113ebd64aaSBen Skeggs if (inth->func(inth) == IRQ_HANDLED) 2123ebd64aaSBen Skeggs ret = IRQ_HANDLED; 2133ebd64aaSBen Skeggs } 2143ebd64aaSBen Skeggs } 2153ebd64aaSBen Skeggs } 2163ebd64aaSBen Skeggs } 2173ebd64aaSBen Skeggs 2183ebd64aaSBen Skeggs /* Nothing handled? Some debugging/protection from IRQ storms is in order... */ 2193ebd64aaSBen Skeggs if (ret == IRQ_NONE) { 2203ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 2213ebd64aaSBen Skeggs for (leaf = 0; leaf < intr->leaves; leaf++) { 2223ebd64aaSBen Skeggs if (intr->stat[leaf]) { 2233ebd64aaSBen Skeggs nvkm_warn(intr->subdev, "intr%d: %08x\n", 2243ebd64aaSBen Skeggs leaf, intr->stat[leaf]); 2253ebd64aaSBen Skeggs nvkm_intr_block_locked(intr, leaf, intr->stat[leaf]); 2263ebd64aaSBen Skeggs } 2273ebd64aaSBen Skeggs } 2283ebd64aaSBen Skeggs } 2293ebd64aaSBen Skeggs } 2303ebd64aaSBen Skeggs 2313ebd64aaSBen Skeggs done: 2323ebd64aaSBen Skeggs /* Re-enable all top-level interrupt sources. */ 233727fd72fSBen Skeggs nvkm_intr_rearm_locked(device); 234727fd72fSBen Skeggs done_unlock: 235727fd72fSBen Skeggs spin_unlock(&device->intr.lock); 236727fd72fSBen Skeggs return ret; 237727fd72fSBen Skeggs } 238727fd72fSBen Skeggs 2393ebd64aaSBen Skeggs int 2403ebd64aaSBen Skeggs nvkm_intr_add(const struct nvkm_intr_func *func, const struct nvkm_intr_data *data, 2413ebd64aaSBen Skeggs struct nvkm_subdev *subdev, int leaves, struct nvkm_intr *intr) 2423ebd64aaSBen Skeggs { 2433ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 2443ebd64aaSBen Skeggs int i; 2453ebd64aaSBen Skeggs 2463ebd64aaSBen Skeggs intr->func = func; 2473ebd64aaSBen Skeggs intr->data = data; 2483ebd64aaSBen Skeggs intr->subdev = subdev; 2493ebd64aaSBen Skeggs intr->leaves = leaves; 2503ebd64aaSBen Skeggs intr->stat = kcalloc(leaves, sizeof(*intr->stat), GFP_KERNEL); 2513ebd64aaSBen Skeggs intr->mask = kcalloc(leaves, sizeof(*intr->mask), GFP_KERNEL); 2523ebd64aaSBen Skeggs if (!intr->stat || !intr->mask) { 2533ebd64aaSBen Skeggs kfree(intr->stat); 2543ebd64aaSBen Skeggs return -ENOMEM; 2553ebd64aaSBen Skeggs } 2563ebd64aaSBen Skeggs 2573ebd64aaSBen Skeggs if (intr->subdev->debug >= NV_DBG_DEBUG) { 2583ebd64aaSBen Skeggs for (i = 0; i < intr->leaves; i++) 2593ebd64aaSBen Skeggs intr->mask[i] = ~0; 2603ebd64aaSBen Skeggs } 2613ebd64aaSBen Skeggs 2623ebd64aaSBen Skeggs spin_lock_irq(&device->intr.lock); 2633ebd64aaSBen Skeggs list_add_tail(&intr->head, &device->intr.intr); 2643ebd64aaSBen Skeggs spin_unlock_irq(&device->intr.lock); 2653ebd64aaSBen Skeggs return 0; 2663ebd64aaSBen Skeggs } 2673ebd64aaSBen Skeggs 268*a7ab200aSBen Skeggs static irqreturn_t 269*a7ab200aSBen Skeggs nvkm_intr_subdev(struct nvkm_inth *inth) 270*a7ab200aSBen Skeggs { 271*a7ab200aSBen Skeggs struct nvkm_subdev *subdev = container_of(inth, typeof(*subdev), inth); 272*a7ab200aSBen Skeggs 273*a7ab200aSBen Skeggs nvkm_subdev_intr(subdev); 274*a7ab200aSBen Skeggs return IRQ_HANDLED; 275*a7ab200aSBen Skeggs } 276*a7ab200aSBen Skeggs 277*a7ab200aSBen Skeggs static void 278*a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(struct nvkm_intr *intr, enum nvkm_subdev_type type, int inst) 279*a7ab200aSBen Skeggs { 280*a7ab200aSBen Skeggs struct nvkm_subdev *subdev; 281*a7ab200aSBen Skeggs enum nvkm_intr_prio prio; 282*a7ab200aSBen Skeggs int ret; 283*a7ab200aSBen Skeggs 284*a7ab200aSBen Skeggs subdev = nvkm_device_subdev(intr->subdev->device, type, inst); 285*a7ab200aSBen Skeggs if (!subdev || !subdev->func->intr) 286*a7ab200aSBen Skeggs return; 287*a7ab200aSBen Skeggs 288*a7ab200aSBen Skeggs if (type == NVKM_ENGINE_DISP) 289*a7ab200aSBen Skeggs prio = NVKM_INTR_PRIO_VBLANK; 290*a7ab200aSBen Skeggs else 291*a7ab200aSBen Skeggs prio = NVKM_INTR_PRIO_NORMAL; 292*a7ab200aSBen Skeggs 293*a7ab200aSBen Skeggs ret = nvkm_inth_add(intr, NVKM_INTR_SUBDEV, prio, subdev, nvkm_intr_subdev, &subdev->inth); 294*a7ab200aSBen Skeggs if (WARN_ON(ret)) 295*a7ab200aSBen Skeggs return; 296*a7ab200aSBen Skeggs 297*a7ab200aSBen Skeggs nvkm_inth_allow(&subdev->inth); 298*a7ab200aSBen Skeggs } 299*a7ab200aSBen Skeggs 300*a7ab200aSBen Skeggs static void 301*a7ab200aSBen Skeggs nvkm_intr_subdev_add(struct nvkm_intr *intr) 302*a7ab200aSBen Skeggs { 303*a7ab200aSBen Skeggs const struct nvkm_intr_data *data; 304*a7ab200aSBen Skeggs struct nvkm_device *device = intr->subdev->device; 305*a7ab200aSBen Skeggs struct nvkm_top_device *tdev; 306*a7ab200aSBen Skeggs 307*a7ab200aSBen Skeggs for (data = intr->data; data && data->mask; data++) { 308*a7ab200aSBen Skeggs if (data->legacy) { 309*a7ab200aSBen Skeggs if (data->type == NVKM_SUBDEV_TOP) { 310*a7ab200aSBen Skeggs list_for_each_entry(tdev, &device->top->device, head) { 311*a7ab200aSBen Skeggs if (tdev->intr < 0 || !(data->mask & BIT(tdev->intr))) 312*a7ab200aSBen Skeggs continue; 313*a7ab200aSBen Skeggs 314*a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(intr, tdev->type, tdev->inst); 315*a7ab200aSBen Skeggs } 316*a7ab200aSBen Skeggs } else { 317*a7ab200aSBen Skeggs nvkm_intr_subdev_add_dev(intr, data->type, data->inst); 318*a7ab200aSBen Skeggs } 319*a7ab200aSBen Skeggs } 320*a7ab200aSBen Skeggs } 321*a7ab200aSBen Skeggs } 322*a7ab200aSBen Skeggs 323727fd72fSBen Skeggs void 324727fd72fSBen Skeggs nvkm_intr_rearm(struct nvkm_device *device) 325727fd72fSBen Skeggs { 3263ebd64aaSBen Skeggs struct nvkm_intr *intr; 3273ebd64aaSBen Skeggs int i; 3283ebd64aaSBen Skeggs 329*a7ab200aSBen Skeggs if (unlikely(!device->intr.legacy_done)) { 330*a7ab200aSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) 331*a7ab200aSBen Skeggs nvkm_intr_subdev_add(intr); 332*a7ab200aSBen Skeggs device->intr.legacy_done = true; 333*a7ab200aSBen Skeggs } 334*a7ab200aSBen Skeggs 335727fd72fSBen Skeggs spin_lock_irq(&device->intr.lock); 3363ebd64aaSBen Skeggs list_for_each_entry(intr, &device->intr.intr, head) { 3373ebd64aaSBen Skeggs for (i = 0; intr->func->block && i < intr->leaves; i++) { 3383ebd64aaSBen Skeggs intr->func->block(intr, i, ~0); 3393ebd64aaSBen Skeggs intr->func->allow(intr, i, intr->mask[i]); 3403ebd64aaSBen Skeggs } 3413ebd64aaSBen Skeggs } 3423ebd64aaSBen Skeggs 343727fd72fSBen Skeggs nvkm_intr_rearm_locked(device); 344727fd72fSBen Skeggs device->intr.armed = true; 345727fd72fSBen Skeggs spin_unlock_irq(&device->intr.lock); 346727fd72fSBen Skeggs } 347727fd72fSBen Skeggs 348727fd72fSBen Skeggs void 349727fd72fSBen Skeggs nvkm_intr_unarm(struct nvkm_device *device) 350727fd72fSBen Skeggs { 351727fd72fSBen Skeggs spin_lock_irq(&device->intr.lock); 352727fd72fSBen Skeggs nvkm_intr_unarm_locked(device); 353727fd72fSBen Skeggs device->intr.armed = false; 354727fd72fSBen Skeggs spin_unlock_irq(&device->intr.lock); 355727fd72fSBen Skeggs } 356727fd72fSBen Skeggs 357727fd72fSBen Skeggs int 358727fd72fSBen Skeggs nvkm_intr_install(struct nvkm_device *device) 359727fd72fSBen Skeggs { 360727fd72fSBen Skeggs int ret; 361727fd72fSBen Skeggs 362727fd72fSBen Skeggs device->intr.irq = device->func->irq(device); 363727fd72fSBen Skeggs if (device->intr.irq < 0) 364727fd72fSBen Skeggs return device->intr.irq; 365727fd72fSBen Skeggs 366727fd72fSBen Skeggs ret = request_irq(device->intr.irq, nvkm_intr, IRQF_SHARED, "nvkm", device); 367727fd72fSBen Skeggs if (ret) 368727fd72fSBen Skeggs return ret; 369727fd72fSBen Skeggs 370727fd72fSBen Skeggs device->intr.alloc = true; 371727fd72fSBen Skeggs return 0; 372727fd72fSBen Skeggs } 373727fd72fSBen Skeggs 374727fd72fSBen Skeggs void 375727fd72fSBen Skeggs nvkm_intr_dtor(struct nvkm_device *device) 376727fd72fSBen Skeggs { 3773ebd64aaSBen Skeggs struct nvkm_intr *intr, *intt; 3783ebd64aaSBen Skeggs 3793ebd64aaSBen Skeggs list_for_each_entry_safe(intr, intt, &device->intr.intr, head) { 3803ebd64aaSBen Skeggs list_del(&intr->head); 3813ebd64aaSBen Skeggs kfree(intr->mask); 3823ebd64aaSBen Skeggs kfree(intr->stat); 3833ebd64aaSBen Skeggs } 3843ebd64aaSBen Skeggs 385727fd72fSBen Skeggs if (device->intr.alloc) 386727fd72fSBen Skeggs free_irq(device->intr.irq, device); 387727fd72fSBen Skeggs } 388727fd72fSBen Skeggs 389727fd72fSBen Skeggs void 390727fd72fSBen Skeggs nvkm_intr_ctor(struct nvkm_device *device) 391727fd72fSBen Skeggs { 3923ebd64aaSBen Skeggs int i; 3933ebd64aaSBen Skeggs 3943ebd64aaSBen Skeggs INIT_LIST_HEAD(&device->intr.intr); 3953ebd64aaSBen Skeggs for (i = 0; i < ARRAY_SIZE(device->intr.prio); i++) 3963ebd64aaSBen Skeggs INIT_LIST_HEAD(&device->intr.prio[i]); 3973ebd64aaSBen Skeggs 398727fd72fSBen Skeggs spin_lock_init(&device->intr.lock); 3993ebd64aaSBen Skeggs device->intr.armed = false; 4003ebd64aaSBen Skeggs } 4013ebd64aaSBen Skeggs 4023ebd64aaSBen Skeggs void 4033ebd64aaSBen Skeggs nvkm_inth_block(struct nvkm_inth *inth) 4043ebd64aaSBen Skeggs { 4053ebd64aaSBen Skeggs if (unlikely(!inth->intr)) 4063ebd64aaSBen Skeggs return; 4073ebd64aaSBen Skeggs 4083ebd64aaSBen Skeggs atomic_set(&inth->allowed, 0); 4093ebd64aaSBen Skeggs } 4103ebd64aaSBen Skeggs 4113ebd64aaSBen Skeggs void 4123ebd64aaSBen Skeggs nvkm_inth_allow(struct nvkm_inth *inth) 4133ebd64aaSBen Skeggs { 4143ebd64aaSBen Skeggs struct nvkm_intr *intr = inth->intr; 4153ebd64aaSBen Skeggs unsigned long flags; 4163ebd64aaSBen Skeggs 4173ebd64aaSBen Skeggs if (unlikely(!inth->intr)) 4183ebd64aaSBen Skeggs return; 4193ebd64aaSBen Skeggs 4203ebd64aaSBen Skeggs spin_lock_irqsave(&intr->subdev->device->intr.lock, flags); 4213ebd64aaSBen Skeggs if (!atomic_xchg(&inth->allowed, 1)) { 4223ebd64aaSBen Skeggs if ((intr->mask[inth->leaf] & inth->mask) != inth->mask) 4233ebd64aaSBen Skeggs nvkm_intr_allow_locked(intr, inth->leaf, inth->mask); 4243ebd64aaSBen Skeggs } 4253ebd64aaSBen Skeggs spin_unlock_irqrestore(&intr->subdev->device->intr.lock, flags); 4263ebd64aaSBen Skeggs } 4273ebd64aaSBen Skeggs 4283ebd64aaSBen Skeggs int 4293ebd64aaSBen Skeggs nvkm_inth_add(struct nvkm_intr *intr, enum nvkm_intr_type type, enum nvkm_intr_prio prio, 4303ebd64aaSBen Skeggs struct nvkm_subdev *subdev, nvkm_inth_func func, struct nvkm_inth *inth) 4313ebd64aaSBen Skeggs { 4323ebd64aaSBen Skeggs struct nvkm_device *device = subdev->device; 4333ebd64aaSBen Skeggs int ret; 4343ebd64aaSBen Skeggs 4353ebd64aaSBen Skeggs if (WARN_ON(inth->mask)) 4363ebd64aaSBen Skeggs return -EBUSY; 4373ebd64aaSBen Skeggs 4383ebd64aaSBen Skeggs ret = nvkm_intr_xlat(subdev, intr, type, &inth->leaf, &inth->mask); 4393ebd64aaSBen Skeggs if (ret) 4403ebd64aaSBen Skeggs return ret; 4413ebd64aaSBen Skeggs 4423ebd64aaSBen Skeggs nvkm_debug(intr->subdev, "intr %d/%08x requested by %s\n", 4433ebd64aaSBen Skeggs inth->leaf, inth->mask, subdev->name); 4443ebd64aaSBen Skeggs 4453ebd64aaSBen Skeggs inth->intr = intr; 4463ebd64aaSBen Skeggs inth->func = func; 4473ebd64aaSBen Skeggs atomic_set(&inth->allowed, 0); 4483ebd64aaSBen Skeggs list_add_tail(&inth->head, &device->intr.prio[prio]); 4493ebd64aaSBen Skeggs return 0; 450727fd72fSBen Skeggs } 451