mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	drm/msm: add a5xx specific debugfs
Add some debugfs to dump out PFP and ME microcontroller state, as well as some of the queues (MEQ and ROQ). Also add a debugfs file to trigger a GPU reset (and reloading the firmware on next submit). Signed-off-by: Rob Clark <robdclark@gmail.com>
This commit is contained in:
		
							parent
							
								
									3f0689e663
								
							
						
					
					
						commit
						331dc0bc19
					
				| @ -62,6 +62,8 @@ msm-y := \ | ||||
| 	msm_ringbuffer.o \
 | ||||
| 	msm_submitqueue.o | ||||
| 
 | ||||
| msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o | ||||
| 
 | ||||
| msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o | ||||
| msm-$(CONFIG_COMMON_CLK) += mdp/mdp4/mdp4_lvds_pll.o | ||||
| msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o | ||||
|  | ||||
							
								
								
									
										188
									
								
								drivers/gpu/drm/msm/adreno/a5xx_debugfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								drivers/gpu/drm/msm/adreno/a5xx_debugfs.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| /* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
 | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License version 2 and | ||||
|  * only version 2 as published by the Free Software Foundation. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| 
 | ||||
| #include <linux/types.h> | ||||
| #include <linux/debugfs.h> | ||||
| #include <drm/drm_print.h> | ||||
| 
 | ||||
| #include "a5xx_gpu.h" | ||||
| 
 | ||||
| static int pfp_print(struct msm_gpu *gpu, struct drm_printer *p) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	drm_printf(p, "PFP state:\n"); | ||||
| 
 | ||||
| 	for (i = 0; i < 36; i++) { | ||||
| 		gpu_write(gpu, REG_A5XX_CP_PFP_STAT_ADDR, i); | ||||
| 		drm_printf(p, "  %02x: %08x\n", i, | ||||
| 			gpu_read(gpu, REG_A5XX_CP_PFP_STAT_DATA)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int me_print(struct msm_gpu *gpu, struct drm_printer *p) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	drm_printf(p, "ME state:\n"); | ||||
| 
 | ||||
| 	for (i = 0; i < 29; i++) { | ||||
| 		gpu_write(gpu, REG_A5XX_CP_ME_STAT_ADDR, i); | ||||
| 		drm_printf(p, "  %02x: %08x\n", i, | ||||
| 			gpu_read(gpu, REG_A5XX_CP_ME_STAT_DATA)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int meq_print(struct msm_gpu *gpu, struct drm_printer *p) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	drm_printf(p, "MEQ state:\n"); | ||||
| 	gpu_write(gpu, REG_A5XX_CP_MEQ_DBG_ADDR, 0); | ||||
| 
 | ||||
| 	for (i = 0; i < 64; i++) { | ||||
| 		drm_printf(p, "  %02x: %08x\n", i, | ||||
| 			gpu_read(gpu, REG_A5XX_CP_MEQ_DBG_DATA)); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int roq_print(struct msm_gpu *gpu, struct drm_printer *p) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	drm_printf(p, "ROQ state:\n"); | ||||
| 	gpu_write(gpu, REG_A5XX_CP_ROQ_DBG_ADDR, 0); | ||||
| 
 | ||||
| 	for (i = 0; i < 512 / 4; i++) { | ||||
| 		uint32_t val[4]; | ||||
| 		int j; | ||||
| 		for (j = 0; j < 4; j++) | ||||
| 			val[j] = gpu_read(gpu, REG_A5XX_CP_ROQ_DBG_DATA); | ||||
| 		drm_printf(p, "  %02x: %08x %08x %08x %08x\n", i, | ||||
| 			val[0], val[1], val[2], val[3]); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int show(struct seq_file *m, void *arg) | ||||
| { | ||||
| 	struct drm_info_node *node = (struct drm_info_node *) m->private; | ||||
| 	struct drm_device *dev = node->minor->dev; | ||||
| 	struct msm_drm_private *priv = dev->dev_private; | ||||
| 	struct drm_printer p = drm_seq_file_printer(m); | ||||
| 	int (*show)(struct msm_gpu *gpu, struct drm_printer *p) = | ||||
| 		node->info_ent->data; | ||||
| 
 | ||||
| 	return show(priv->gpu, &p); | ||||
| } | ||||
| 
 | ||||
| #define ENT(n) { .name = #n, .show = show, .data = n ##_print } | ||||
| static struct drm_info_list a5xx_debugfs_list[] = { | ||||
| 	ENT(pfp), | ||||
| 	ENT(me), | ||||
| 	ENT(meq), | ||||
| 	ENT(roq), | ||||
| }; | ||||
| 
 | ||||
| /* for debugfs files that can be written to, we can't use drm helper: */ | ||||
| static int | ||||
| reset_set(void *data, u64 val) | ||||
| { | ||||
| 	struct drm_device *dev = data; | ||||
| 	struct msm_drm_private *priv = dev->dev_private; | ||||
| 	struct msm_gpu *gpu = priv->gpu; | ||||
| 	struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); | ||||
| 	struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu); | ||||
| 
 | ||||
| 	if (!capable(CAP_SYS_ADMIN)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	/* TODO do we care about trying to make sure the GPU is idle?
 | ||||
| 	 * Since this is just a debug feature limited to CAP_SYS_ADMIN, | ||||
| 	 * maybe it is fine to let the user keep both pieces if they | ||||
| 	 * try to reset an active GPU. | ||||
| 	 */ | ||||
| 
 | ||||
| 	mutex_lock(&dev->struct_mutex); | ||||
| 
 | ||||
| 	if (adreno_gpu->pm4) { | ||||
| 		release_firmware(adreno_gpu->pm4); | ||||
| 		adreno_gpu->pm4 = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (adreno_gpu->pfp) { | ||||
| 		release_firmware(adreno_gpu->pfp); | ||||
| 		adreno_gpu->pfp = NULL; | ||||
| 	} | ||||
| 	if (a5xx_gpu->pm4_bo) { | ||||
| 		if (a5xx_gpu->pm4_iova) | ||||
| 			msm_gem_put_iova(a5xx_gpu->pm4_bo, gpu->aspace); | ||||
| 		drm_gem_object_unreference(a5xx_gpu->pm4_bo); | ||||
| 		a5xx_gpu->pm4_bo = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (a5xx_gpu->pfp_bo) { | ||||
| 		if (a5xx_gpu->pfp_iova) | ||||
| 			msm_gem_put_iova(a5xx_gpu->pfp_bo, gpu->aspace); | ||||
| 		drm_gem_object_unreference(a5xx_gpu->pfp_bo); | ||||
| 		a5xx_gpu->pfp_bo = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	gpu->needs_hw_init = true; | ||||
| 
 | ||||
| 	pm_runtime_get_sync(&gpu->pdev->dev); | ||||
| 	gpu->funcs->recover(gpu); | ||||
| 
 | ||||
| 	pm_runtime_put_sync(&gpu->pdev->dev); | ||||
| 	mutex_unlock(&dev->struct_mutex); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| DEFINE_SIMPLE_ATTRIBUTE(reset_fops, NULL, reset_set, "%llx\n"); | ||||
| 
 | ||||
| 
 | ||||
| int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor) | ||||
| { | ||||
| 	struct drm_device *dev = minor->dev; | ||||
| 	struct dentry *ent; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!minor) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	ret = drm_debugfs_create_files(a5xx_debugfs_list, | ||||
| 			ARRAY_SIZE(a5xx_debugfs_list), | ||||
| 			minor->debugfs_root, minor); | ||||
| 
 | ||||
| 	if (ret) { | ||||
| 		dev_err(dev->dev, "could not install a5xx_debugfs_list\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	ent = debugfs_create_file("reset", S_IWUGO, | ||||
| 		minor->debugfs_root, | ||||
| 		dev, &reset_fops); | ||||
| 	if (!ent) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| @ -1195,6 +1195,7 @@ static const struct adreno_gpu_funcs funcs = { | ||||
| 		.destroy = a5xx_destroy, | ||||
| #ifdef CONFIG_DEBUG_FS | ||||
| 		.show = a5xx_show, | ||||
| 		.debugfs_init = a5xx_debugfs_init, | ||||
| #endif | ||||
| 		.gpu_busy = a5xx_gpu_busy, | ||||
| 	}, | ||||
|  | ||||
| @ -49,6 +49,10 @@ struct a5xx_gpu { | ||||
| 
 | ||||
| #define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base) | ||||
| 
 | ||||
| #ifdef CONFIG_DEBUG_FS | ||||
| int a5xx_debugfs_init(struct msm_gpu *gpu, struct drm_minor *minor); | ||||
| #endif | ||||
| 
 | ||||
| /*
 | ||||
|  * In order to do lockless preemption we use a simple state machine to progress | ||||
|  * through the process. | ||||
|  | ||||
| @ -150,6 +150,12 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev) | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (gpu->funcs->debugfs_init) { | ||||
| 		gpu->funcs->debugfs_init(gpu, dev->primary); | ||||
| 		gpu->funcs->debugfs_init(gpu, dev->render); | ||||
| 		gpu->funcs->debugfs_init(gpu, dev->control); | ||||
| 	} | ||||
| 
 | ||||
| 	return gpu; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -161,8 +161,11 @@ int msm_debugfs_init(struct drm_minor *minor) | ||||
| 		return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (priv->kms->funcs->debugfs_init) | ||||
| 	if (priv->kms->funcs->debugfs_init) { | ||||
| 		ret = priv->kms->funcs->debugfs_init(priv->kms, minor); | ||||
| 		if (ret) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | ||||
| @ -65,6 +65,8 @@ struct msm_gpu_funcs { | ||||
| #ifdef CONFIG_DEBUG_FS | ||||
| 	/* show GPU status in debugfs: */ | ||||
| 	void (*show)(struct msm_gpu *gpu, struct seq_file *m); | ||||
| 	/* for generation specific debugfs: */ | ||||
| 	int (*debugfs_init)(struct msm_gpu *gpu, struct drm_minor *minor); | ||||
| #endif | ||||
| 	int (*gpu_busy)(struct msm_gpu *gpu, uint64_t *value); | ||||
| }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Rob Clark
						Rob Clark