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