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