diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c | 209 |
1 files changed, 187 insertions, 22 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 174badca27e7..6716ac281c49 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -25,6 +25,9 @@ #include "amdgpu.h" #include "amdgpu_sdma.h" #include "amdgpu_ras.h" +#include "amdgpu_reset.h" +#include "gc/gc_10_1_0_offset.h" +#include "gc/gc_10_3_0_sh_mask.h" #define AMDGPU_CSA_SDMA_SIZE 64 /* SDMA CSA reside in the 3rd page of CSA */ @@ -75,22 +78,14 @@ uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, if (amdgpu_sriov_vf(adev) || vmid == 0 || !adev->gfx.mcbp) return 0; - if (ring->is_mes_queue) { - uint32_t offset = 0; + r = amdgpu_sdma_get_index_from_ring(ring, &index); - offset = offsetof(struct amdgpu_mes_ctx_meta_data, - sdma[ring->idx].sdma_meta_data); - csa_mc_addr = amdgpu_mes_ctx_get_offs_gpu_addr(ring, offset); - } else { - r = amdgpu_sdma_get_index_from_ring(ring, &index); - - if (r || index > 31) - csa_mc_addr = 0; - else - csa_mc_addr = amdgpu_csa_vaddr(adev) + - AMDGPU_CSA_SDMA_OFFSET + - index * AMDGPU_CSA_SDMA_SIZE; - } + if (r || index > 31) + csa_mc_addr = 0; + else + csa_mc_addr = amdgpu_csa_vaddr(adev) + + AMDGPU_CSA_SDMA_OFFSET + + index * AMDGPU_CSA_SDMA_SIZE; return csa_mc_addr; } @@ -355,23 +350,44 @@ int amdgpu_sdma_ras_sw_init(struct amdgpu_device *adev) static int amdgpu_debugfs_sdma_sched_mask_set(void *data, u64 val) { struct amdgpu_device *adev = (struct amdgpu_device *)data; - u32 i; + u64 i, num_ring; u64 mask = 0; - struct amdgpu_ring *ring; + struct amdgpu_ring *ring, *page = NULL; if (!adev) return -ENODEV; - mask = BIT_ULL(adev->sdma.num_instances) - 1; + /* Determine the number of rings per SDMA instance + * (1 for sdma gfx ring, 2 if page queue exists) + */ + if (adev->sdma.has_page_queue) + num_ring = 2; + else + num_ring = 1; + + /* Calculate the maximum possible mask value + * based on the number of SDMA instances and rings + */ + mask = BIT_ULL(adev->sdma.num_instances * num_ring) - 1; + if ((val & mask) == 0) return -EINVAL; for (i = 0; i < adev->sdma.num_instances; ++i) { ring = &adev->sdma.instance[i].ring; - if (val & BIT_ULL(i)) + if (adev->sdma.has_page_queue) + page = &adev->sdma.instance[i].page; + if (val & BIT_ULL(i * num_ring)) ring->sched.ready = true; else ring->sched.ready = false; + + if (page) { + if (val & BIT_ULL(i * num_ring + 1)) + page->sched.ready = true; + else + page->sched.ready = false; + } } /* publish sched.ready flag update effective immediately across smp */ smp_rmb(); @@ -381,16 +397,37 @@ static int amdgpu_debugfs_sdma_sched_mask_set(void *data, u64 val) static int amdgpu_debugfs_sdma_sched_mask_get(void *data, u64 *val) { struct amdgpu_device *adev = (struct amdgpu_device *)data; - u32 i; + u64 i, num_ring; u64 mask = 0; - struct amdgpu_ring *ring; + struct amdgpu_ring *ring, *page = NULL; if (!adev) return -ENODEV; + + /* Determine the number of rings per SDMA instance + * (1 for sdma gfx ring, 2 if page queue exists) + */ + if (adev->sdma.has_page_queue) + num_ring = 2; + else + num_ring = 1; + for (i = 0; i < adev->sdma.num_instances; ++i) { ring = &adev->sdma.instance[i].ring; + if (adev->sdma.has_page_queue) + page = &adev->sdma.instance[i].page; + if (ring->sched.ready) - mask |= 1 << i; + mask |= BIT_ULL(i * num_ring); + else + mask &= ~BIT_ULL(i * num_ring); + + if (page) { + if (page->sched.ready) + mask |= BIT_ULL(i * num_ring + 1); + else + mask &= ~BIT_ULL(i * num_ring + 1); + } } *val = mask; @@ -460,3 +497,131 @@ void amdgpu_sdma_sysfs_reset_mask_fini(struct amdgpu_device *adev) device_remove_file(adev->dev, &dev_attr_sdma_reset_mask); } } + +struct amdgpu_ring *amdgpu_sdma_get_shared_ring(struct amdgpu_device *adev, struct amdgpu_ring *ring) +{ + if (adev->sdma.has_page_queue && + (ring->me < adev->sdma.num_instances) && + (ring == &adev->sdma.instance[ring->me].ring)) + return &adev->sdma.instance[ring->me].page; + else + return NULL; +} + +/** +* amdgpu_sdma_is_shared_inv_eng - Check if a ring is an SDMA ring that shares a VM invalidation engine +* @adev: Pointer to the AMDGPU device structure +* @ring: Pointer to the ring structure to check +* +* This function checks if the given ring is an SDMA ring that shares a VM invalidation engine. +* It returns true if the ring is such an SDMA ring, false otherwise. +*/ +bool amdgpu_sdma_is_shared_inv_eng(struct amdgpu_device *adev, struct amdgpu_ring *ring) +{ + int i = ring->me; + + if (!adev->sdma.has_page_queue || i >= adev->sdma.num_instances) + return false; + + if (amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 3) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 4, 4) || + amdgpu_ip_version(adev, GC_HWIP, 0) == IP_VERSION(9, 5, 0)) + return (ring == &adev->sdma.instance[i].page); + else + return false; +} + +static int amdgpu_sdma_soft_reset(struct amdgpu_device *adev, u32 instance_id) +{ + struct amdgpu_sdma_instance *sdma_instance = &adev->sdma.instance[instance_id]; + int r = -EOPNOTSUPP; + + switch (amdgpu_ip_version(adev, SDMA0_HWIP, 0)) { + case IP_VERSION(4, 4, 2): + case IP_VERSION(4, 4, 4): + case IP_VERSION(4, 4, 5): + /* For SDMA 4.x, use the existing DPM interface for backward compatibility */ + r = amdgpu_dpm_reset_sdma(adev, 1 << instance_id); + break; + case IP_VERSION(5, 0, 0): + case IP_VERSION(5, 0, 1): + case IP_VERSION(5, 0, 2): + case IP_VERSION(5, 0, 5): + case IP_VERSION(5, 2, 0): + case IP_VERSION(5, 2, 2): + case IP_VERSION(5, 2, 4): + case IP_VERSION(5, 2, 5): + case IP_VERSION(5, 2, 6): + case IP_VERSION(5, 2, 3): + case IP_VERSION(5, 2, 1): + case IP_VERSION(5, 2, 7): + if (sdma_instance->funcs->soft_reset_kernel_queue) + r = sdma_instance->funcs->soft_reset_kernel_queue(adev, instance_id); + break; + default: + break; + } + + return r; +} + +/** + * amdgpu_sdma_reset_engine - Reset a specific SDMA engine + * @adev: Pointer to the AMDGPU device + * @instance_id: ID of the SDMA engine instance to reset + * + * Returns: 0 on success, or a negative error code on failure. + */ +int amdgpu_sdma_reset_engine(struct amdgpu_device *adev, uint32_t instance_id) +{ + int ret = 0; + struct amdgpu_sdma_instance *sdma_instance = &adev->sdma.instance[instance_id]; + struct amdgpu_ring *gfx_ring = &sdma_instance->ring; + struct amdgpu_ring *page_ring = &sdma_instance->page; + bool gfx_sched_stopped = false, page_sched_stopped = false; + + mutex_lock(&sdma_instance->engine_reset_mutex); + /* Stop the scheduler's work queue for the GFX and page rings if they are running. + * This ensures that no new tasks are submitted to the queues while + * the reset is in progress. + */ + if (!amdgpu_ring_sched_ready(gfx_ring)) { + drm_sched_wqueue_stop(&gfx_ring->sched); + gfx_sched_stopped = true; + } + + if (adev->sdma.has_page_queue && !amdgpu_ring_sched_ready(page_ring)) { + drm_sched_wqueue_stop(&page_ring->sched); + page_sched_stopped = true; + } + + if (sdma_instance->funcs->stop_kernel_queue) + sdma_instance->funcs->stop_kernel_queue(gfx_ring); + + /* Perform the SDMA reset for the specified instance */ + ret = amdgpu_sdma_soft_reset(adev, instance_id); + if (ret) { + dev_err(adev->dev, "Failed to reset SDMA instance %u\n", instance_id); + goto exit; + } + + if (sdma_instance->funcs->start_kernel_queue) + sdma_instance->funcs->start_kernel_queue(gfx_ring); + +exit: + /* Restart the scheduler's work queue for the GFX and page rings + * if they were stopped by this function. This allows new tasks + * to be submitted to the queues after the reset is complete. + */ + if (!ret) { + if (gfx_sched_stopped && amdgpu_ring_sched_ready(gfx_ring)) { + drm_sched_wqueue_start(&gfx_ring->sched); + } + if (page_sched_stopped && amdgpu_ring_sched_ready(page_ring)) { + drm_sched_wqueue_start(&page_ring->sched); + } + } + mutex_unlock(&sdma_instance->engine_reset_mutex); + + return ret; +} |