• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Red Hat Inc.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20  * OTHER DEALINGS IN THE SOFTWARE.
21  */
22 #include <core/intr.h>
23 #include <core/device.h>
24 #include <core/subdev.h>
25 #include <subdev/pci.h>
26 #include <subdev/top.h>
27 
28 static int
nvkm_intr_xlat(struct nvkm_subdev * subdev,struct nvkm_intr * intr,enum nvkm_intr_type type,int * leaf,u32 * mask)29 nvkm_intr_xlat(struct nvkm_subdev *subdev, struct nvkm_intr *intr,
30 	       enum nvkm_intr_type type, int *leaf, u32 *mask)
31 {
32 	struct nvkm_device *device = subdev->device;
33 
34 	if (type < NVKM_INTR_VECTOR_0) {
35 		if (type == NVKM_INTR_SUBDEV) {
36 			const struct nvkm_intr_data *data = intr->data;
37 			struct nvkm_top_device *tdev;
38 
39 			while (data && data->mask) {
40 				if (data->type == NVKM_SUBDEV_TOP) {
41 					list_for_each_entry(tdev, &device->top->device, head) {
42 						if (tdev->intr >= 0 &&
43 						    tdev->type == subdev->type &&
44 						    tdev->inst == subdev->inst) {
45 							if (data->mask & BIT(tdev->intr)) {
46 								*leaf = data->leaf;
47 								*mask = BIT(tdev->intr);
48 								return 0;
49 							}
50 						}
51 					}
52 				} else
53 				if (data->type == subdev->type && data->inst == subdev->inst) {
54 					*leaf = data->leaf;
55 					*mask = data->mask;
56 					return 0;
57 				}
58 
59 				data++;
60 			}
61 		} else {
62 			return -ENOSYS;
63 		}
64 	} else {
65 		if (type < intr->leaves * sizeof(*intr->stat) * 8) {
66 			*leaf = type / 32;
67 			*mask = BIT(type % 32);
68 			return 0;
69 		}
70 	}
71 
72 	return -EINVAL;
73 }
74 
75 static struct nvkm_intr *
nvkm_intr_find(struct nvkm_subdev * subdev,enum nvkm_intr_type type,int * leaf,u32 * mask)76 nvkm_intr_find(struct nvkm_subdev *subdev, enum nvkm_intr_type type, int *leaf, u32 *mask)
77 {
78 	struct nvkm_intr *intr;
79 	int ret;
80 
81 	list_for_each_entry(intr, &subdev->device->intr.intr, head) {
82 		ret = nvkm_intr_xlat(subdev, intr, type, leaf, mask);
83 		if (ret == 0)
84 			return intr;
85 	}
86 
87 	return NULL;
88 }
89 
90 static void
nvkm_intr_allow_locked(struct nvkm_intr * intr,int leaf,u32 mask)91 nvkm_intr_allow_locked(struct nvkm_intr *intr, int leaf, u32 mask)
92 {
93 	intr->mask[leaf] |= mask;
94 	if (intr->func->allow) {
95 		if (intr->func->reset)
96 			intr->func->reset(intr, leaf, mask);
97 		intr->func->allow(intr, leaf, mask);
98 	}
99 }
100 
101 void
nvkm_intr_allow(struct nvkm_subdev * subdev,enum nvkm_intr_type type)102 nvkm_intr_allow(struct nvkm_subdev *subdev, enum nvkm_intr_type type)
103 {
104 	struct nvkm_device *device = subdev->device;
105 	struct nvkm_intr *intr;
106 	unsigned long flags;
107 	int leaf;
108 	u32 mask;
109 
110 	intr = nvkm_intr_find(subdev, type, &leaf, &mask);
111 	if (intr) {
112 		nvkm_debug(intr->subdev, "intr %d/%08x allowed by %s\n", leaf, mask, subdev->name);
113 		spin_lock_irqsave(&device->intr.lock, flags);
114 		nvkm_intr_allow_locked(intr, leaf, mask);
115 		spin_unlock_irqrestore(&device->intr.lock, flags);
116 	}
117 }
118 
119 static void
nvkm_intr_block_locked(struct nvkm_intr * intr,int leaf,u32 mask)120 nvkm_intr_block_locked(struct nvkm_intr *intr, int leaf, u32 mask)
121 {
122 	intr->mask[leaf] &= ~mask;
123 	if (intr->func->block)
124 		intr->func->block(intr, leaf, mask);
125 }
126 
127 void
nvkm_intr_block(struct nvkm_subdev * subdev,enum nvkm_intr_type type)128 nvkm_intr_block(struct nvkm_subdev *subdev, enum nvkm_intr_type type)
129 {
130 	struct nvkm_device *device = subdev->device;
131 	struct nvkm_intr *intr;
132 	unsigned long flags;
133 	int leaf;
134 	u32 mask;
135 
136 	intr = nvkm_intr_find(subdev, type, &leaf, &mask);
137 	if (intr) {
138 		nvkm_debug(intr->subdev, "intr %d/%08x blocked by %s\n", leaf, mask, subdev->name);
139 		spin_lock_irqsave(&device->intr.lock, flags);
140 		nvkm_intr_block_locked(intr, leaf, mask);
141 		spin_unlock_irqrestore(&device->intr.lock, flags);
142 	}
143 }
144 
145 static void
nvkm_intr_rearm_locked(struct nvkm_device * device)146 nvkm_intr_rearm_locked(struct nvkm_device *device)
147 {
148 	struct nvkm_intr *intr;
149 
150 	list_for_each_entry(intr, &device->intr.intr, head)
151 		intr->func->rearm(intr);
152 }
153 
154 static void
nvkm_intr_unarm_locked(struct nvkm_device * device)155 nvkm_intr_unarm_locked(struct nvkm_device *device)
156 {
157 	struct nvkm_intr *intr;
158 
159 	list_for_each_entry(intr, &device->intr.intr, head)
160 		intr->func->unarm(intr);
161 }
162 
163 static irqreturn_t
nvkm_intr(int irq,void * arg)164 nvkm_intr(int irq, void *arg)
165 {
166 	struct nvkm_device *device = arg;
167 	struct nvkm_intr *intr;
168 	struct nvkm_inth *inth;
169 	irqreturn_t ret = IRQ_NONE;
170 	bool pending = false;
171 	int prio, leaf;
172 
173 	/* Disable all top-level interrupt sources, and re-arm MSI interrupts. */
174 	spin_lock(&device->intr.lock);
175 	if (!device->intr.armed)
176 		goto done_unlock;
177 
178 	nvkm_intr_unarm_locked(device);
179 	nvkm_pci_msi_rearm(device);
180 
181 	/* Fetch pending interrupt masks. */
182 	list_for_each_entry(intr, &device->intr.intr, head) {
183 		if (intr->func->pending(intr))
184 			pending = true;
185 	}
186 
187 	if (!pending)
188 		goto done;
189 
190 	/* Check that GPU is still on the bus by reading NV_PMC_BOOT_0. */
191 	if (WARN_ON(nvkm_rd32(device, 0x000000) == 0xffffffff))
192 		goto done;
193 
194 	/* Execute handlers. */
195 	for (prio = 0; prio < ARRAY_SIZE(device->intr.prio); prio++) {
196 		list_for_each_entry(inth, &device->intr.prio[prio], head) {
197 			struct nvkm_intr *intr = inth->intr;
198 
199 			if (intr->stat[inth->leaf] & inth->mask) {
200 				if (atomic_read(&inth->allowed)) {
201 					if (intr->func->reset)
202 						intr->func->reset(intr, inth->leaf, inth->mask);
203 					if (inth->func(inth) == IRQ_HANDLED)
204 						ret = IRQ_HANDLED;
205 				}
206 			}
207 		}
208 	}
209 
210 	/* Nothing handled?  Some debugging/protection from IRQ storms is in order... */
211 	if (ret == IRQ_NONE) {
212 		list_for_each_entry(intr, &device->intr.intr, head) {
213 			for (leaf = 0; leaf < intr->leaves; leaf++) {
214 				if (intr->stat[leaf]) {
215 					nvkm_debug(intr->subdev, "intr%d: %08x\n",
216 						   leaf, intr->stat[leaf]);
217 					nvkm_intr_block_locked(intr, leaf, intr->stat[leaf]);
218 				}
219 			}
220 		}
221 	}
222 
223 done:
224 	/* Re-enable all top-level interrupt sources. */
225 	nvkm_intr_rearm_locked(device);
226 done_unlock:
227 	spin_unlock(&device->intr.lock);
228 	return ret;
229 }
230 
231 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)232 nvkm_intr_add(const struct nvkm_intr_func *func, const struct nvkm_intr_data *data,
233 	      struct nvkm_subdev *subdev, int leaves, struct nvkm_intr *intr)
234 {
235 	struct nvkm_device *device = subdev->device;
236 	int i;
237 
238 	intr->func = func;
239 	intr->data = data;
240 	intr->subdev = subdev;
241 	intr->leaves = leaves;
242 	intr->stat = kcalloc(leaves, sizeof(*intr->stat), GFP_KERNEL);
243 	intr->mask = kcalloc(leaves, sizeof(*intr->mask), GFP_KERNEL);
244 	if (!intr->stat || !intr->mask) {
245 		kfree(intr->stat);
246 		return -ENOMEM;
247 	}
248 
249 	if (intr->subdev->debug >= NV_DBG_DEBUG) {
250 		for (i = 0; i < intr->leaves; i++)
251 			intr->mask[i] = ~0;
252 	}
253 
254 	spin_lock_irq(&device->intr.lock);
255 	list_add_tail(&intr->head, &device->intr.intr);
256 	spin_unlock_irq(&device->intr.lock);
257 	return 0;
258 }
259 
260 static irqreturn_t
nvkm_intr_subdev(struct nvkm_inth * inth)261 nvkm_intr_subdev(struct nvkm_inth *inth)
262 {
263 	struct nvkm_subdev *subdev = container_of(inth, typeof(*subdev), inth);
264 
265 	nvkm_subdev_intr(subdev);
266 	return IRQ_HANDLED;
267 }
268 
269 static void
nvkm_intr_subdev_add_dev(struct nvkm_intr * intr,enum nvkm_subdev_type type,int inst)270 nvkm_intr_subdev_add_dev(struct nvkm_intr *intr, enum nvkm_subdev_type type, int inst)
271 {
272 	struct nvkm_subdev *subdev;
273 	enum nvkm_intr_prio prio;
274 	int ret;
275 
276 	subdev = nvkm_device_subdev(intr->subdev->device, type, inst);
277 	if (!subdev || !subdev->func->intr)
278 		return;
279 
280 	if (type == NVKM_ENGINE_DISP)
281 		prio = NVKM_INTR_PRIO_VBLANK;
282 	else
283 		prio = NVKM_INTR_PRIO_NORMAL;
284 
285 	ret = nvkm_inth_add(intr, NVKM_INTR_SUBDEV, prio, subdev, nvkm_intr_subdev, &subdev->inth);
286 	if (WARN_ON(ret))
287 		return;
288 
289 	nvkm_inth_allow(&subdev->inth);
290 }
291 
292 static void
nvkm_intr_subdev_add(struct nvkm_intr * intr)293 nvkm_intr_subdev_add(struct nvkm_intr *intr)
294 {
295 	const struct nvkm_intr_data *data;
296 	struct nvkm_device *device = intr->subdev->device;
297 	struct nvkm_top_device *tdev;
298 
299 	for (data = intr->data; data && data->mask; data++) {
300 		if (data->legacy) {
301 			if (data->type == NVKM_SUBDEV_TOP) {
302 				list_for_each_entry(tdev, &device->top->device, head) {
303 					if (tdev->intr < 0 || !(data->mask & BIT(tdev->intr)))
304 						continue;
305 
306 					nvkm_intr_subdev_add_dev(intr, tdev->type, tdev->inst);
307 				}
308 			} else {
309 				nvkm_intr_subdev_add_dev(intr, data->type, data->inst);
310 			}
311 		}
312 	}
313 }
314 
315 void
nvkm_intr_rearm(struct nvkm_device * device)316 nvkm_intr_rearm(struct nvkm_device *device)
317 {
318 	struct nvkm_intr *intr;
319 	int i;
320 
321 	if (unlikely(!device->intr.legacy_done)) {
322 		list_for_each_entry(intr, &device->intr.intr, head)
323 			nvkm_intr_subdev_add(intr);
324 		device->intr.legacy_done = true;
325 	}
326 
327 	spin_lock_irq(&device->intr.lock);
328 	list_for_each_entry(intr, &device->intr.intr, head) {
329 		for (i = 0; intr->func->block && i < intr->leaves; i++) {
330 			intr->func->block(intr, i, ~0);
331 			intr->func->allow(intr, i, intr->mask[i]);
332 		}
333 	}
334 
335 	nvkm_intr_rearm_locked(device);
336 	device->intr.armed = true;
337 	spin_unlock_irq(&device->intr.lock);
338 }
339 
340 void
nvkm_intr_unarm(struct nvkm_device * device)341 nvkm_intr_unarm(struct nvkm_device *device)
342 {
343 	spin_lock_irq(&device->intr.lock);
344 	nvkm_intr_unarm_locked(device);
345 	device->intr.armed = false;
346 	spin_unlock_irq(&device->intr.lock);
347 }
348 
349 int
nvkm_intr_install(struct nvkm_device * device)350 nvkm_intr_install(struct nvkm_device *device)
351 {
352 	int ret;
353 
354 	device->intr.irq = device->func->irq(device);
355 	if (device->intr.irq < 0)
356 		return device->intr.irq;
357 
358 	ret = request_irq(device->intr.irq, nvkm_intr, IRQF_SHARED, "nvkm", device);
359 	if (ret)
360 		return ret;
361 
362 	device->intr.alloc = true;
363 	return 0;
364 }
365 
366 void
nvkm_intr_dtor(struct nvkm_device * device)367 nvkm_intr_dtor(struct nvkm_device *device)
368 {
369 	struct nvkm_intr *intr, *intt;
370 
371 	list_for_each_entry_safe(intr, intt, &device->intr.intr, head) {
372 		list_del(&intr->head);
373 		kfree(intr->mask);
374 		kfree(intr->stat);
375 	}
376 
377 	if (device->intr.alloc)
378 		free_irq(device->intr.irq, device);
379 }
380 
381 void
nvkm_intr_ctor(struct nvkm_device * device)382 nvkm_intr_ctor(struct nvkm_device *device)
383 {
384 	int i;
385 
386 	INIT_LIST_HEAD(&device->intr.intr);
387 	for (i = 0; i < ARRAY_SIZE(device->intr.prio); i++)
388 		INIT_LIST_HEAD(&device->intr.prio[i]);
389 
390 	spin_lock_init(&device->intr.lock);
391 	device->intr.armed = false;
392 }
393 
394 void
nvkm_inth_block(struct nvkm_inth * inth)395 nvkm_inth_block(struct nvkm_inth *inth)
396 {
397 	if (unlikely(!inth->intr))
398 		return;
399 
400 	atomic_set(&inth->allowed, 0);
401 }
402 
403 void
nvkm_inth_allow(struct nvkm_inth * inth)404 nvkm_inth_allow(struct nvkm_inth *inth)
405 {
406 	struct nvkm_intr *intr = inth->intr;
407 	unsigned long flags;
408 
409 	if (unlikely(!inth->intr))
410 		return;
411 
412 	spin_lock_irqsave(&intr->subdev->device->intr.lock, flags);
413 	if (!atomic_xchg(&inth->allowed, 1)) {
414 		if ((intr->mask[inth->leaf] & inth->mask) != inth->mask)
415 			nvkm_intr_allow_locked(intr, inth->leaf, inth->mask);
416 	}
417 	spin_unlock_irqrestore(&intr->subdev->device->intr.lock, flags);
418 }
419 
420 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)421 nvkm_inth_add(struct nvkm_intr *intr, enum nvkm_intr_type type, enum nvkm_intr_prio prio,
422 	      struct nvkm_subdev *subdev, nvkm_inth_func func, struct nvkm_inth *inth)
423 {
424 	struct nvkm_device *device = subdev->device;
425 	int ret;
426 
427 	if (WARN_ON(inth->mask))
428 		return -EBUSY;
429 
430 	ret = nvkm_intr_xlat(subdev, intr, type, &inth->leaf, &inth->mask);
431 	if (ret)
432 		return ret;
433 
434 	nvkm_debug(intr->subdev, "intr %d/%08x requested by %s\n",
435 		   inth->leaf, inth->mask, subdev->name);
436 
437 	inth->intr = intr;
438 	inth->func = func;
439 	atomic_set(&inth->allowed, 0);
440 	list_add_tail(&inth->head, &device->intr.prio[prio]);
441 	return 0;
442 }
443