Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5f232365 authored by Alex Deucher's avatar Alex Deucher
Browse files

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: default avatarDave Airlie <airlied@redhat.com>
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent ba228ac8
Loading
Loading
Loading
Loading
+100 −8
Original line number Diff line number Diff line
@@ -312,6 +312,7 @@ int amdgpu_irq_add_id(struct amdgpu_device *adev, unsigned src_id,
	}

	adev->irq.sources[src_id] = source;

	return 0;
}

@@ -335,6 +336,9 @@ void amdgpu_irq_dispatch(struct amdgpu_device *adev,
		return;
	}

	if (adev->irq.virq[src_id]) {
		generic_handle_irq(irq_find_mapping(adev->irq.domain, src_id));
	} else {
		src = adev->irq.sources[src_id];
		if (!src) {
			DRM_DEBUG("Unhandled interrupt src_id: %d\n", src_id);
@@ -345,6 +349,7 @@ void amdgpu_irq_dispatch(struct amdgpu_device *adev,
		if (r)
			DRM_ERROR("error processing interrupt (%d)\n", r);
	}
}

/**
 * amdgpu_irq_update - update hw interrupt state
@@ -461,3 +466,90 @@ bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src,

	return !!atomic_read(&src->enabled_types[type]);
}

/* gen irq */
static void amdgpu_irq_mask(struct irq_data *irqd)
{
	/* XXX */
}

static void amdgpu_irq_unmask(struct irq_data *irqd)
{
	/* XXX */
}

static struct irq_chip amdgpu_irq_chip = {
	.name = "amdgpu-ih",
	.irq_mask = amdgpu_irq_mask,
	.irq_unmask = amdgpu_irq_unmask,
};

static int amdgpu_irqdomain_map(struct irq_domain *d,
				unsigned int irq, irq_hw_number_t hwirq)
{
	if (hwirq >= AMDGPU_MAX_IRQ_SRC_ID)
		return -EPERM;

	irq_set_chip_and_handler(irq,
				 &amdgpu_irq_chip, handle_simple_irq);
	return 0;
}

static struct irq_domain_ops amdgpu_hw_irqdomain_ops = {
	.map = amdgpu_irqdomain_map,
};

/**
 * amdgpu_irq_add_domain - create a linear irq domain
 *
 * @adev: amdgpu device pointer
 *
 * Create an irq domain for GPU interrupt sources
 * that may be driven by another driver (e.g., ACP).
 */
int amdgpu_irq_add_domain(struct amdgpu_device *adev)
{
	adev->irq.domain = irq_domain_add_linear(NULL, AMDGPU_MAX_IRQ_SRC_ID,
						 &amdgpu_hw_irqdomain_ops, adev);
	if (!adev->irq.domain) {
		DRM_ERROR("GPU irq add domain failed\n");
		return -ENODEV;
	}

	return 0;
}

/**
 * amdgpu_irq_remove_domain - remove the irq domain
 *
 * @adev: amdgpu device pointer
 *
 * Remove the irq domain for GPU interrupt sources
 * that may be driven by another driver (e.g., ACP).
 */
void amdgpu_irq_remove_domain(struct amdgpu_device *adev)
{
	if (adev->irq.domain) {
		irq_domain_remove(adev->irq.domain);
		adev->irq.domain = NULL;
	}
}

/**
 * amdgpu_irq_create_mapping - create a mapping between a domain irq and a
 *                             Linux irq
 *
 * @adev: amdgpu device pointer
 * @src_id: IH source id
 *
 * Create a mapping between a domain irq (GPU IH src id) and a Linux irq
 * Use this for components that generate a GPU interrupt, but are driven
 * by a different driver (e.g., ACP).
 * Returns the Linux irq.
 */
unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id)
{
	adev->irq.virq[src_id] = irq_create_mapping(adev->irq.domain, src_id);

	return adev->irq.virq[src_id];
}
+9 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#ifndef __AMDGPU_IRQ_H__
#define __AMDGPU_IRQ_H__

#include <linux/irqdomain.h>
#include "amdgpu_ih.h"

#define AMDGPU_MAX_IRQ_SRC_ID	0x100
@@ -65,6 +66,10 @@ struct amdgpu_irq {
	/* interrupt ring */
	struct amdgpu_ih_ring		ih;
	const struct amdgpu_ih_funcs	*ih_funcs;

	/* gen irq stuff */
	struct irq_domain		*domain; /* GPU irq controller domain */
	unsigned			virq[AMDGPU_MAX_IRQ_SRC_ID];
};

void amdgpu_irq_preinstall(struct drm_device *dev);
@@ -90,4 +95,8 @@ int amdgpu_irq_put(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
bool amdgpu_irq_enabled(struct amdgpu_device *adev, struct amdgpu_irq_src *src,
			unsigned type);

int amdgpu_irq_add_domain(struct amdgpu_device *adev);
void amdgpu_irq_remove_domain(struct amdgpu_device *adev);
unsigned amdgpu_irq_create_mapping(struct amdgpu_device *adev, unsigned src_id);

#endif
+6 −0
Original line number Diff line number Diff line
@@ -274,6 +274,11 @@ static void cik_ih_set_rptr(struct amdgpu_device *adev)
static int cik_ih_early_init(void *handle)
{
	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
	int ret;

	ret = amdgpu_irq_add_domain(adev);
	if (ret)
		return ret;

	cik_ih_set_interrupt_funcs(adev);

@@ -300,6 +305,7 @@ static int cik_ih_sw_fini(void *handle)

	amdgpu_irq_fini(adev);
	amdgpu_ih_ring_fini(adev);
	amdgpu_irq_remove_domain(adev);

	return 0;
}
+7 −0
Original line number Diff line number Diff line
@@ -253,8 +253,14 @@ static void cz_ih_set_rptr(struct amdgpu_device *adev)
static int cz_ih_early_init(void *handle)
{
	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
	int ret;

	ret = amdgpu_irq_add_domain(adev);
	if (ret)
		return ret;

	cz_ih_set_interrupt_funcs(adev);

	return 0;
}

@@ -278,6 +284,7 @@ static int cz_ih_sw_fini(void *handle)

	amdgpu_irq_fini(adev);
	amdgpu_ih_ring_fini(adev);
	amdgpu_irq_remove_domain(adev);

	return 0;
}
+7 −0
Original line number Diff line number Diff line
@@ -253,8 +253,14 @@ static void iceland_ih_set_rptr(struct amdgpu_device *adev)
static int iceland_ih_early_init(void *handle)
{
	struct amdgpu_device *adev = (struct amdgpu_device *)handle;
	int ret;

	ret = amdgpu_irq_add_domain(adev);
	if (ret)
		return ret;

	iceland_ih_set_interrupt_funcs(adev);

	return 0;
}

@@ -278,6 +284,7 @@ static int iceland_ih_sw_fini(void *handle)

	amdgpu_irq_fini(adev);
	amdgpu_ih_ring_fini(adev);
	amdgpu_irq_remove_domain(adev);

	return 0;
}
Loading