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
nvkm_intr_xlat(struct nvkm_subdev * subdev,struct nvkm_intr * intr,enum nvkm_intr_type type,int * leaf,u32 * mask)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 *
nvkm_intr_find(struct nvkm_subdev * subdev,enum nvkm_intr_type type,int * leaf,u32 * mask)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
nvkm_intr_allow_locked(struct nvkm_intr * intr,int leaf,u32 mask)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
nvkm_intr_allow(struct nvkm_subdev * subdev,enum nvkm_intr_type type)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
nvkm_intr_block_locked(struct nvkm_intr * intr,int leaf,u32 mask)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
nvkm_intr_block(struct nvkm_subdev * subdev,enum nvkm_intr_type type)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
nvkm_intr_rearm_locked(struct nvkm_device * device)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
nvkm_intr_unarm_locked(struct nvkm_device * device)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
nvkm_intr(int irq,void * arg)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;
170fe76fe49SBen 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]) {
215*ba1efd8eSBen Skeggs nvkm_debug(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
nvkm_intr_add(const struct nvkm_intr_func * func,const struct nvkm_intr_data * data,struct nvkm_subdev * subdev,int leaves,struct nvkm_intr * intr)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
nvkm_intr_subdev(struct nvkm_inth * inth)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
nvkm_intr_subdev_add_dev(struct nvkm_intr * intr,enum nvkm_subdev_type type,int inst)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
nvkm_intr_subdev_add(struct nvkm_intr * intr)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
nvkm_intr_rearm(struct nvkm_device * device)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
nvkm_intr_unarm(struct nvkm_device * device)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
nvkm_intr_install(struct nvkm_device * device)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
nvkm_intr_dtor(struct nvkm_device * device)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
nvkm_intr_ctor(struct nvkm_device * device)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
nvkm_inth_block(struct nvkm_inth * inth)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
nvkm_inth_allow(struct nvkm_inth * inth)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
nvkm_inth_add(struct nvkm_intr * intr,enum nvkm_intr_type type,enum nvkm_intr_prio prio,struct nvkm_subdev * subdev,nvkm_inth_func func,struct nvkm_inth * inth)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