Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+136 -8
+100 -8
drivers/gpu/drm/amd/amdgpu/amdgpu_irq.c
··· 312 312 } 313 313 314 314 adev->irq.sources[src_id] = source; 315 + 315 316 return 0; 316 317 } 317 318 ··· 336 335 return; 337 336 } 338 337 339 - src = adev->irq.sources[src_id]; 340 - if (!src) { 341 - DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id); 342 - return; 343 - } 338 + if (adev->irq.virq[src_id]) { 339 + generic_handle_irq(irq_find_mapping(adev->irq.domain, src_id)); 340 + } else { 341 + src = adev->irq.sources[src_id]; 342 + if (!src) { 343 + DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id); 344 + return; 345 + } 344 346 345 - r = src->funcs->process(adev, src, entry); 346 - if (r) 347 - DRM_ERROR("error processing interrupt (%d)\n", r); 347 + r = src->funcs->process(adev, src, entry); 348 + if (r) 349 + DRM_ERROR("error processing interrupt (%d)\n", r); 350 + } 348 351 } 349 352 350 353 /** ··· 465 460 return false; 466 461 467 462 return !!atomic_read(&src->enabled_types[type]); 463 + } 464 + 465 + /* gen irq */ 466 + static void amdgpu_irq_mask(struct irq_data *irqd) 467 + { 468 + /* XXX */ 469 + } 470 + 471 + static void amdgpu_irq_unmask(struct irq_data *irqd) 472 + { 473 + /* XXX */ 474 + } 475 + 476 + static struct irq_chip amdgpu_irq_chip = { 477 + .name = "amdgpu-ih", 478 + .irq_mask = amdgpu_irq_mask, 479 + .irq_unmask = amdgpu_irq_unmask, 480 + }; 481 + 482 + static int amdgpu_irqdomain_map(struct irq_domain *d, 483 + unsigned int irq, irq_hw_number_t hwirq) 484 + { 485 + if (hwirq >= AMDGPU_MAX_IRQ_SRC_ID) 486 + return -EPERM; 487 + 488 + irq_set_chip_and_handler(irq, 489 + &amdgpu_irq_chip, handle_simple_irq); 490 + return 0; 491 + } 492 + 493 + static struct irq_domain_ops amdgpu_hw_irqdomain_ops = { 494 + .map = amdgpu_irqdomain_map, 495 + }; 496 + 497 + /** 498 + * amdgpu_irq_add_domain - create a linear irq domain 499 + * 500 + * @adev: amdgpu device pointer 501 + * 502 + * Create an irq domain for GPU interrupt sources 503 + * that may be driven by another driver (e.g., ACP). 504 + */ 505 + int amdgpu_irq_add_domain(struct amdgpu_device *adev) 506 + { 507 + adev->irq.domain = irq_domain_add_linear(NULL, AMDGPU_MAX_IRQ_SRC_ID, 508 + &amdgpu_hw_irqdomain_ops, adev); 509 + if (!adev->irq.domain) { 510 + DRM_ERROR("GPU irq add domain failed\n"); 511 + return -ENODEV; 512 + } 513 + 514 + return 0; 515 + } 516 + 517 + /** 518 + * amdgpu_irq_remove_domain - remove the irq domain 519 + * 520 + * @adev: amdgpu device pointer 521 + * 522 + * Remove the irq domain for GPU interrupt sources 523 + * that may be driven by another driver (e.g., ACP). 524 + */ 525 + void amdgpu_irq_remove_domain(struct amdgpu_device *adev) 526 + { 527 + if (adev->irq.domain) { 528 + irq_domain_remove(adev->irq.domain); 529 + adev->irq.domain = NULL; 530 + } 531 + } 532 + 533 + /** 534 + * amdgpu_irq_create_mapping - create a mapping between a domain irq and a 535 + * Linux irq 536 + * 537 + * @adev: amdgpu device pointer 538 + * @src_id: IH source id 539 + * 540 + * Create a mapping between a domain irq (GPU IH src id) and a Linux irq 541 + * Use this for components that generate a GPU interrupt, but are driven 542 + * by a different driver (e.g., ACP). 543 + * Returns the Linux irq. 544 + */ 545 + unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id) 546 + { 547 + adev->irq.virq[src_id] = irq_create_mapping(adev->irq.domain, src_id); 548 + 549 + return adev->irq.virq[src_id]; 468 550 }
+9
drivers/gpu/drm/amd/amdgpu/amdgpu_irq.h
··· 24 24 #ifndef __AMDGPU_IRQ_H__ 25 25 #define __AMDGPU_IRQ_H__ 26 26 27 + #include <linux/irqdomain.h> 27 28 #include "amdgpu_ih.h" 28 29 29 30 #define AMDGPU_MAX_IRQ_SRC_ID 0x100 ··· 66 65 /* interrupt ring */ 67 66 struct amdgpu_ih_ring ih; 68 67 const struct amdgpu_ih_funcs *ih_funcs; 68 + 69 + /* gen irq stuff */ 70 + struct irq_domain *domain; /* GPU irq controller domain */ 71 + unsigned virq[AMDGPU_MAX_IRQ_SRC_ID]; 69 72 }; 70 73 71 74 void amdgpu_irq_preinstall(struct drm_device *dev); ··· 94 89 unsigned type); 95 90 bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src, 96 91 unsigned type); 92 + 93 + int amdgpu_irq_add_domain(struct amdgpu_device *adev); 94 + void amdgpu_irq_remove_domain(struct amdgpu_device *adev); 95 + unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id); 97 96 98 97 #endif
+6
drivers/gpu/drm/amd/amdgpu/cik_ih.c
··· 274 274 static int cik_ih_early_init(void *handle) 275 275 { 276 276 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 277 + int ret; 278 + 279 + ret = amdgpu_irq_add_domain(adev); 280 + if (ret) 281 + return ret; 277 282 278 283 cik_ih_set_interrupt_funcs(adev); 279 284 ··· 305 300 306 301 amdgpu_irq_fini(adev); 307 302 amdgpu_ih_ring_fini(adev); 303 + amdgpu_irq_remove_domain(adev); 308 304 309 305 return 0; 310 306 }
+7
drivers/gpu/drm/amd/amdgpu/cz_ih.c
··· 253 253 static int cz_ih_early_init(void *handle) 254 254 { 255 255 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 256 + int ret; 257 + 258 + ret = amdgpu_irq_add_domain(adev); 259 + if (ret) 260 + return ret; 256 261 257 262 cz_ih_set_interrupt_funcs(adev); 263 + 258 264 return 0; 259 265 } 260 266 ··· 284 278 285 279 amdgpu_irq_fini(adev); 286 280 amdgpu_ih_ring_fini(adev); 281 + amdgpu_irq_remove_domain(adev); 287 282 288 283 return 0; 289 284 }
+7
drivers/gpu/drm/amd/amdgpu/iceland_ih.c
··· 253 253 static int iceland_ih_early_init(void *handle) 254 254 { 255 255 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 256 + int ret; 257 + 258 + ret = amdgpu_irq_add_domain(adev); 259 + if (ret) 260 + return ret; 256 261 257 262 iceland_ih_set_interrupt_funcs(adev); 263 + 258 264 return 0; 259 265 } 260 266 ··· 284 278 285 279 amdgpu_irq_fini(adev); 286 280 amdgpu_ih_ring_fini(adev); 281 + amdgpu_irq_remove_domain(adev); 287 282 288 283 return 0; 289 284 }
+7
drivers/gpu/drm/amd/amdgpu/tonga_ih.c
··· 273 273 static int tonga_ih_early_init(void *handle) 274 274 { 275 275 struct amdgpu_device *adev = (struct amdgpu_device *)handle; 276 + int ret; 277 + 278 + ret = amdgpu_irq_add_domain(adev); 279 + if (ret) 280 + return ret; 276 281 277 282 tonga_ih_set_interrupt_funcs(adev); 283 + 278 284 return 0; 279 285 } 280 286 ··· 307 301 308 302 amdgpu_irq_fini(adev); 309 303 amdgpu_ih_ring_fini(adev); 304 + amdgpu_irq_add_domain(adev); 310 305 311 306 return 0; 312 307 }