diff options
author | Alex Deucher <alexander.deucher@amd.com> | 2015-11-06 01:29:08 -0500 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2016-01-11 09:52:57 -0500 |
commit | 5f2323658e4829ffb893553297e64795a90cbcd9 (patch) | |
tree | 7862a0b646c43f8e525eca9a98e2705bdd0bbb77 /drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | |
parent | ba228ac8f512c9cd09cb4245c424ab1632da0c24 (diff) |
drm/amdgpu: add irq domain support
Hardware blocks on the GPU like ACP generate interrupts in
the GPU interrupt controller, but are driven by a separate
driver. Add an irq domain to the GPU driver so that
blocks like ACP can register a Linux interrupt.
Acked-by: Dave Airlie <airlied@redhat.com>
Acked-by: Christian König <christian.koenig@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | 108 |
1 files changed, 100 insertions, 8 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c index 7c42ff670080..3006182c5d0d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c | |||
@@ -312,6 +312,7 @@ int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id, | |||
312 | } | 312 | } |
313 | 313 | ||
314 | adev->irq.sources[src_id] = source; | 314 | adev->irq.sources[src_id] = source; |
315 | |||
315 | return 0; | 316 | return 0; |
316 | } | 317 | } |
317 | 318 | ||
@@ -335,15 +336,19 @@ void amdgpu_irq_dispatch(struct amdgpu_device *adev, | |||
335 | return; | 336 | return; |
336 | } | 337 | } |
337 | 338 | ||
338 | src = adev->irq.sources[src_id]; | 339 | if (adev->irq.virq[src_id]) { |
339 | if (!src) { | 340 | generic_handle_irq(irq_find_mapping(adev->irq.domain, src_id)); |
340 | DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id); | 341 | } else { |
341 | return; | 342 | src = adev->irq.sources[src_id]; |
342 | } | 343 | if (!src) { |
344 | DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id); | ||
345 | return; | ||
346 | } | ||
343 | 347 | ||
344 | r = src->funcs->process(adev, src, entry); | 348 | r = src->funcs->process(adev, src, entry); |
345 | if (r) | 349 | if (r) |
346 | DRM_ERROR("error processing interrupt (%d)\n", r); | 350 | DRM_ERROR("error processing interrupt (%d)\n", r); |
351 | } | ||
347 | } | 352 | } |
348 | 353 | ||
349 | /** | 354 | /** |
@@ -461,3 +466,90 @@ bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src, | |||
461 | 466 | ||
462 | return !!atomic_read(&src->enabled_types[type]); | 467 | return !!atomic_read(&src->enabled_types[type]); |
463 | } | 468 | } |
469 | |||
470 | /* gen irq */ | ||
471 | static void amdgpu_irq_mask(struct irq_data *irqd) | ||
472 | { | ||
473 | /* XXX */ | ||
474 | } | ||
475 | |||
476 | static void amdgpu_irq_unmask(struct irq_data *irqd) | ||
477 | { | ||
478 | /* XXX */ | ||
479 | } | ||
480 | |||
481 | static struct irq_chip amdgpu_irq_chip = { | ||
482 | .name = "amdgpu-ih", | ||
483 | .irq_mask = amdgpu_irq_mask, | ||
484 | .irq_unmask = amdgpu_irq_unmask, | ||
485 | }; | ||
486 | |||
487 | static int amdgpu_irqdomain_map(struct irq_domain *d, | ||
488 | unsigned int irq, irq_hw_number_t hwirq) | ||
489 | { | ||
490 | if (hwirq >= AMDGPU_MAX_IRQ_SRC_ID) | ||
491 | return -EPERM; | ||
492 | |||
493 | irq_set_chip_and_handler(irq, | ||
494 | &amdgpu_irq_chip, handle_simple_irq); | ||
495 | return 0; | ||
496 | } | ||
497 | |||
498 | static struct irq_domain_ops amdgpu_hw_irqdomain_ops = { | ||
499 | .map = amdgpu_irqdomain_map, | ||
500 | }; | ||
501 | |||
502 | /** | ||
503 | * amdgpu_irq_add_domain - create a linear irq domain | ||
504 | * | ||
505 | * @adev: amdgpu device pointer | ||
506 | * | ||
507 | * Create an irq domain for GPU interrupt sources | ||
508 | * that may be driven by another driver (e.g., ACP). | ||
509 | */ | ||
510 | int amdgpu_irq_add_domain(struct amdgpu_device *adev) | ||
511 | { | ||
512 | adev->irq.domain = irq_domain_add_linear(NULL, AMDGPU_MAX_IRQ_SRC_ID, | ||
513 | &amdgpu_hw_irqdomain_ops, adev); | ||
514 | if (!adev->irq.domain) { | ||
515 | DRM_ERROR("GPU irq add domain failed\n"); | ||
516 | return -ENODEV; | ||
517 | } | ||
518 | |||
519 | return 0; | ||
520 | } | ||
521 | |||
522 | /** | ||
523 | * amdgpu_irq_remove_domain - remove the irq domain | ||
524 | * | ||
525 | * @adev: amdgpu device pointer | ||
526 | * | ||
527 | * Remove the irq domain for GPU interrupt sources | ||
528 | * that may be driven by another driver (e.g., ACP). | ||
529 | */ | ||
530 | void amdgpu_irq_remove_domain(struct amdgpu_device *adev) | ||
531 | { | ||
532 | if (adev->irq.domain) { | ||
533 | irq_domain_remove(adev->irq.domain); | ||
534 | adev->irq.domain = NULL; | ||
535 | } | ||
536 | } | ||
537 | |||
538 | /** | ||
539 | * amdgpu_irq_create_mapping - create a mapping between a domain irq and a | ||
540 | * Linux irq | ||
541 | * | ||
542 | * @adev: amdgpu device pointer | ||
543 | * @src_id: IH source id | ||
544 | * | ||
545 | * Create a mapping between a domain irq (GPU IH src id) and a Linux irq | ||
546 | * Use this for components that generate a GPU interrupt, but are driven | ||
547 | * by a different driver (e.g., ACP). | ||
548 | * Returns the Linux irq. | ||
549 | */ | ||
550 | unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id) | ||
551 | { | ||
552 | adev->irq.virq[src_id] = irq_create_mapping(adev->irq.domain, src_id); | ||
553 | |||
554 | return adev->irq.virq[src_id]; | ||
555 | } | ||