[PATCH 3/4] drm/amd/amdgpu: Add soc_v1_0 reset handler

Alex Deucher <[email protected]>
Newsgroups org.freedesktop.lists.amd-gfx
Message-ID <[email protected]>
From: Asad Kamal <[email protected]>

Add soc_v1_0 reset handler for mode2 reset

v2: Remove XGMI parallel reset dispatch and xgmi_update_topology()
call, reset on soc_v1_0 is per-device only. (Lijo)

v3: Remove ras as will be plugged later as separate ip. Remove vcn from
ip mask as vcn not part of reset sequence yet (Lijo)

Signed-off-by: Asad Kamal <[email protected]>
Reviewed-by: Lijo Lazar <[email protected]>
Signed-off-by: Alex Deucher <[email protected]>
---
 drivers/gpu/drm/amd/amdgpu/soc_v1_0.c | 283 ++++++++++++++++++++++++++
 drivers/gpu/drm/amd/amdgpu/soc_v1_0.h |   2 +
 2 files changed, 285 insertions(+)

diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
index 5e40fb0cdaf2a..5b18a5c8114ff 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
+++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.c
@@ -30,6 +30,7 @@
 #include "sdma_v7_1.h"
 #include "gfx_v12_1.h"
 #include "amdgpu_video_codecs.h"
+#include "amdgpu_reset.h"
 
 #include "gc/gc_12_1_0_offset.h"
 #include "gc/gc_12_1_0_sh_mask.h"
@@ -398,6 +399,288 @@ const struct amdgpu_ip_block_version soc_v1_0_common_ip_block = {
 	.funcs = &soc_v1_0_common_ip_funcs,
 };
 
+static struct amdgpu_reset_handler *
+soc_v1_0_get_reset_handler(struct amdgpu_reset_control *reset_ctl,
+			   struct amdgpu_reset_context *reset_context)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)reset_ctl->handle;
+	struct amdgpu_reset_handler *handler;
+	enum amd_reset_method method;
+	int i;
+
+	method = (reset_context->method == AMD_RESET_METHOD_NONE) ?
+		amdgpu_asic_reset_method(adev) :  reset_context->method;
+	for_each_handler(i, handler, reset_ctl) {
+		if (handler->reset_method == method)
+			return handler;
+	}
+
+	return NULL;
+}
+
+static inline u32 soc_v1_0_get_ip_block_mask(struct amdgpu_device *adev)
+{
+	u32 ip_block_mask = BIT(AMD_IP_BLOCK_TYPE_GFX) |
+				 BIT(AMD_IP_BLOCK_TYPE_MES) |
+				 BIT(AMD_IP_BLOCK_TYPE_SDMA) |
+				 BIT(AMD_IP_BLOCK_TYPE_IH);
+
+	return ip_block_mask;
+}
+
+static int soc_v1_0_mode2_suspend_ip(struct amdgpu_device *adev)
+{
+	u32 ip_block_mask = soc_v1_0_get_ip_block_mask(adev);
+	u32 ip_block;
+	int r, i;
+
+	amdgpu_device_set_cg_state(adev, AMD_CG_STATE_UNGATE);
+
+	/* SDMA suspend not required */
+	ip_block_mask &= ~BIT(AMD_IP_BLOCK_TYPE_SDMA);
+	for (i = adev->num_ip_blocks - 1; i >= 0; i--) {
+		if (!adev->ip_blocks[i].status.valid)
+			continue;
+		ip_block = BIT(adev->ip_blocks[i].version->type);
+		if (!(ip_block_mask & ip_block))
+			continue;
+
+		r = amdgpu_ip_block_suspend(&adev->ip_blocks[i]);
+		if (r)
+			return r;
+	}
+
+	return 0;
+}
+
+static int
+soc_v1_0_mode2_prepare_hwcontext(struct amdgpu_reset_control *reset_ctl,
+				 struct amdgpu_reset_context *reset_context)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)reset_ctl->handle;
+
+	return soc_v1_0_mode2_suspend_ip(adev);
+}
+
+static int soc_v1_0_mode2_reset(struct amdgpu_device *adev)
+{
+	adev->asic_reset_res = amdgpu_dpm_mode2_reset(adev);
+	return adev->asic_reset_res;
+}
+
+static void soc_v1_0_async_reset(struct work_struct *work)
+{
+	struct amdgpu_reset_handler *handler;
+	struct amdgpu_reset_control *reset_ctl =
+		container_of(work, struct amdgpu_reset_control, reset_work);
+	struct amdgpu_device *adev = (struct amdgpu_device *)reset_ctl->handle;
+	int i;
+
+	for_each_handler(i, handler, reset_ctl) {
+		if (handler->reset_method == reset_ctl->active_reset) {
+			dev_dbg(adev->dev, "Resetting device\n");
+			handler->do_reset(adev);
+			break;
+		}
+	}
+}
+
+static int
+soc_v1_0_mode2_perform_reset(struct amdgpu_reset_control *reset_ctl,
+			     struct amdgpu_reset_context *reset_context)
+{
+	struct amdgpu_device *adev = (struct amdgpu_device *)reset_ctl->handle;
+	struct list_head *reset_device_list = reset_context->reset_device_list;
+	struct amdgpu_device *tmp_adev = NULL;
+	int r = 0;
+
+	dev_dbg(adev->dev, "soc_v1_0 perform hw reset\n");
+
+	if (!reset_device_list)
+		return -EINVAL;
+
+	list_for_each_entry(tmp_adev, reset_device_list, reset_list) {
+		mutex_lock(&tmp_adev->reset_cntl->reset_lock);
+		tmp_adev->reset_cntl->active_reset = AMD_RESET_METHOD_MODE2;
+	}
+
+	list_for_each_entry(tmp_adev, reset_device_list, reset_list) {
+		r = soc_v1_0_mode2_reset(tmp_adev);
+		if (r) {
+			dev_err(tmp_adev->dev,
+				"ASIC reset failed with error, %d for drm dev, %s",
+				r, adev_to_drm(tmp_adev)->unique);
+			break;
+		}
+	}
+
+	list_for_each_entry(tmp_adev, reset_device_list, reset_list) {
+		mutex_unlock(&tmp_adev->reset_cntl->reset_lock);
+		tmp_adev->reset_cntl->active_reset = AMD_RESET_METHOD_NONE;
+	}
+
+	return r;
+}
+
+static int soc_v1_0_mode2_restore_ip(struct amdgpu_device *adev)
+{
+	u32 ip_block_mask = soc_v1_0_get_ip_block_mask(adev);
+	struct amdgpu_ip_block *ih_block;
+	u32 ip_block;
+	int i, r;
+
+	if (ip_block_mask & BIT(AMD_IP_BLOCK_TYPE_IH)) {
+		ih_block = amdgpu_device_ip_get_ip_block(adev,
+							 AMD_IP_BLOCK_TYPE_IH);
+		if (unlikely(!ih_block)) {
+			dev_err(adev->dev, "Failed to get IH handle\n");
+			return -EINVAL;
+		}
+		r = amdgpu_ip_block_resume(ih_block);
+		if (r)
+			return r;
+	}
+
+	/* IH Block resume completed can be removed from ip_block_mask */
+	ip_block_mask &= ~BIT(AMD_IP_BLOCK_TYPE_IH);
+
+	/* Reinit GFXHUB */
+	adev->gfxhub.funcs->init(adev);
+	r = adev->gfxhub.funcs->gart_enable(adev);
+	if (r) {
+		dev_err(adev->dev, "GFXHUB gart reenable failed after reset\n");
+		return r;
+	}
+
+	for (i = 0; i < adev->num_ip_blocks; i++) {
+		if (!adev->ip_blocks[i].status.valid)
+			continue;
+		ip_block = BIT(adev->ip_blocks[i].version->type);
+		if (!(ip_block_mask & ip_block))
+			continue;
+		r = amdgpu_ip_block_resume(&adev->ip_blocks[i]);
+		if (r)
+			return r;
+	}
+
+	for (i = 0; i < adev->num_ip_blocks; i++) {
+		if (!adev->ip_blocks[i].status.valid)
+			continue;
+		ip_block = BIT(adev->ip_blocks[i].version->type);
+		if (!(ip_block_mask & ip_block))
+			continue;
+
+		if (adev->ip_blocks[i].version->funcs->late_init) {
+			r = adev->ip_blocks[i].version->funcs->late_init(&adev->ip_blocks[i]);
+			if (r) {
+				dev_err(adev->dev,
+					"late_init of IP block <%s> failed %d after reset\n",
+					adev->ip_blocks[i].version->funcs->name,
+					r);
+				return r;
+			}
+		}
+		adev->ip_blocks[i].status.late_initialized = true;
+	}
+
+	amdgpu_device_set_cg_state(adev, AMD_CG_STATE_GATE);
+
+	return r;
+}
+
+static int
+soc_v1_0_mode2_restore_hwcontext(struct amdgpu_reset_control *reset_ctl,
+				 struct amdgpu_reset_context *reset_context)
+{
+	struct list_head *reset_device_list = reset_context->reset_device_list;
+	struct amdgpu_device *tmp_adev = NULL;
+	int r;
+
+	if (!reset_device_list)
+		return -EINVAL;
+
+	list_for_each_entry(tmp_adev, reset_device_list, reset_list) {
+		amdgpu_set_init_level(tmp_adev,
+				      AMDGPU_INIT_LEVEL_RESET_RECOVERY);
+		dev_info(tmp_adev->dev,
+			 "GPU reset succeeded, trying to resume\n");
+		amdgpu_ras_clear_err_state(tmp_adev);
+		r = soc_v1_0_mode2_restore_ip(tmp_adev);
+		if (r)
+			goto end;
+
+		/*
+		 * Add this ASIC as tracked as reset was already
+		 * complete successfully.
+		 */
+		amdgpu_register_gpu_instance(tmp_adev);
+
+		amdgpu_ras_resume(tmp_adev);
+
+		if (!r) {
+			amdgpu_set_init_level(tmp_adev,
+					      AMDGPU_INIT_LEVEL_DEFAULT);
+			amdgpu_irq_gpu_reset_resume_helper(tmp_adev);
+
+			r = amdgpu_ib_ring_tests(tmp_adev);
+			if (r) {
+				dev_err(tmp_adev->dev,
+					"ib ring test failed (%d).\n", r);
+				r = -EAGAIN;
+				tmp_adev->asic_reset_res = r;
+				goto end;
+			}
+		}
+	}
+
+end:
+	return r;
+}
+
+static struct amdgpu_reset_handler soc_v1_0_mode2_handler = {
+	.reset_method		= AMD_RESET_METHOD_MODE2,
+	.prepare_env		= NULL,
+	.prepare_hwcontext	= soc_v1_0_mode2_prepare_hwcontext,
+	.perform_reset		= soc_v1_0_mode2_perform_reset,
+	.restore_hwcontext	= soc_v1_0_mode2_restore_hwcontext,
+	.restore_env		= NULL,
+	.do_reset		= soc_v1_0_mode2_reset,
+};
+
+static struct amdgpu_reset_handler
+	*soc_v1_0_rst_handlers[AMDGPU_RESET_MAX_HANDLERS] = {
+		&soc_v1_0_mode2_handler,
+	};
+
+int soc_v1_0_reset_init(struct amdgpu_device *adev)
+{
+	struct amdgpu_reset_control *reset_ctl;
+
+	reset_ctl = kzalloc_obj(*reset_ctl, GFP_KERNEL);
+	if (!reset_ctl)
+		return -ENOMEM;
+
+	reset_ctl->handle = adev;
+	reset_ctl->async_reset = soc_v1_0_async_reset;
+	reset_ctl->active_reset = AMD_RESET_METHOD_NONE;
+	reset_ctl->get_reset_handler = soc_v1_0_get_reset_handler;
+
+	INIT_WORK(&reset_ctl->reset_work, reset_ctl->async_reset);
+	/* Only mode2 is handled through reset control now */
+	reset_ctl->reset_handlers = &soc_v1_0_rst_handlers;
+
+	adev->reset_cntl = reset_ctl;
+
+	return 0;
+}
+
+int soc_v1_0_reset_fini(struct amdgpu_device *adev)
+{
+	kfree(adev->reset_cntl);
+	adev->reset_cntl = NULL;
+	return 0;
+}
+
 static enum amdgpu_gfx_partition __soc_v1_0_calc_xcp_mode(struct amdgpu_xcp_mgr *xcp_mgr)
 {
 	struct amdgpu_device *adev = xcp_mgr->adev;
diff --git a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.h b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.h
index 3cbc20eb16155..82752964d2c1a 100644
--- a/drivers/gpu/drm/amd/amdgpu/soc_v1_0.h
+++ b/drivers/gpu/drm/amd/amdgpu/soc_v1_0.h
@@ -36,5 +36,7 @@ bool soc_v1_0_mid1_reg_range(uint32_t reg);
 uint32_t soc_v1_0_normalize_xcc_reg_offset(uint32_t reg);
 uint32_t soc_v1_0_normalize_reg_offset(uint32_t reg);
 u64 soc_v1_0_encode_ext_smn_addressing(int ext_id);
+int soc_v1_0_reset_init(struct amdgpu_device *adev);
+int soc_v1_0_reset_fini(struct amdgpu_device *adev);
 
 #endif
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.