| /* | 
 |  * Copyright 2008 Advanced Micro Devices, Inc. | 
 |  * Copyright 2008 Red Hat Inc. | 
 |  * Copyright 2009 Jerome Glisse. | 
 |  * | 
 |  * Permission is hereby granted, free of charge, to any person obtaining a | 
 |  * copy of this software and associated documentation files (the "Software"), | 
 |  * to deal in the Software without restriction, including without limitation | 
 |  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | 
 |  * and/or sell copies of the Software, and to permit persons to whom the | 
 |  * Software is furnished to do so, subject to the following conditions: | 
 |  * | 
 |  * The above copyright notice and this permission notice shall be included in | 
 |  * all copies or substantial portions of the Software. | 
 |  * | 
 |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
 |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
 |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | 
 |  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | 
 |  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 
 |  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | 
 |  * OTHER DEALINGS IN THE SOFTWARE. | 
 |  * | 
 |  */ | 
 |  | 
 | #include <linux/kthread.h> | 
 | #include <drm/drmP.h> | 
 | #include <linux/debugfs.h> | 
 | #include "amdgpu.h" | 
 |  | 
 | /* | 
 |  * Debugfs | 
 |  */ | 
 | int amdgpu_debugfs_add_files(struct amdgpu_device *adev, | 
 | 			     const struct drm_info_list *files, | 
 | 			     unsigned nfiles) | 
 | { | 
 | 	unsigned i; | 
 |  | 
 | 	for (i = 0; i < adev->debugfs_count; i++) { | 
 | 		if (adev->debugfs[i].files == files) { | 
 | 			/* Already registered */ | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	i = adev->debugfs_count + 1; | 
 | 	if (i > AMDGPU_DEBUGFS_MAX_COMPONENTS) { | 
 | 		DRM_ERROR("Reached maximum number of debugfs components.\n"); | 
 | 		DRM_ERROR("Report so we increase " | 
 | 			  "AMDGPU_DEBUGFS_MAX_COMPONENTS.\n"); | 
 | 		return -EINVAL; | 
 | 	} | 
 | 	adev->debugfs[adev->debugfs_count].files = files; | 
 | 	adev->debugfs[adev->debugfs_count].num_files = nfiles; | 
 | 	adev->debugfs_count = i; | 
 | #if defined(CONFIG_DEBUG_FS) | 
 | 	drm_debugfs_create_files(files, nfiles, | 
 | 				 adev->ddev->primary->debugfs_root, | 
 | 				 adev->ddev->primary); | 
 | #endif | 
 | 	return 0; | 
 | } | 
 |  | 
 | #if defined(CONFIG_DEBUG_FS) | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 | 	bool pm_pg_lock, use_bank; | 
 | 	unsigned instance_bank, sh_bank, se_bank; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* are we reading registers for which a PG lock is necessary? */ | 
 | 	pm_pg_lock = (*pos >> 23) & 1; | 
 |  | 
 | 	if (*pos & (1ULL << 62)) { | 
 | 		se_bank = (*pos & GENMASK_ULL(33, 24)) >> 24; | 
 | 		sh_bank = (*pos & GENMASK_ULL(43, 34)) >> 34; | 
 | 		instance_bank = (*pos & GENMASK_ULL(53, 44)) >> 44; | 
 |  | 
 | 		if (se_bank == 0x3FF) | 
 | 			se_bank = 0xFFFFFFFF; | 
 | 		if (sh_bank == 0x3FF) | 
 | 			sh_bank = 0xFFFFFFFF; | 
 | 		if (instance_bank == 0x3FF) | 
 | 			instance_bank = 0xFFFFFFFF; | 
 | 		use_bank = 1; | 
 | 	} else { | 
 | 		use_bank = 0; | 
 | 	} | 
 |  | 
 | 	*pos &= (1UL << 22) - 1; | 
 |  | 
 | 	if (use_bank) { | 
 | 		if ((sh_bank != 0xFFFFFFFF && sh_bank >= adev->gfx.config.max_sh_per_se) || | 
 | 		    (se_bank != 0xFFFFFFFF && se_bank >= adev->gfx.config.max_shader_engines)) | 
 | 			return -EINVAL; | 
 | 		mutex_lock(&adev->grbm_idx_mutex); | 
 | 		amdgpu_gfx_select_se_sh(adev, se_bank, | 
 | 					sh_bank, instance_bank); | 
 | 	} | 
 |  | 
 | 	if (pm_pg_lock) | 
 | 		mutex_lock(&adev->pm.mutex); | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		if (*pos > adev->rmmio_size) | 
 | 			goto end; | 
 |  | 
 | 		value = RREG32(*pos >> 2); | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) { | 
 | 			result = r; | 
 | 			goto end; | 
 | 		} | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | end: | 
 | 	if (use_bank) { | 
 | 		amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); | 
 | 		mutex_unlock(&adev->grbm_idx_mutex); | 
 | 	} | 
 |  | 
 | 	if (pm_pg_lock) | 
 | 		mutex_unlock(&adev->pm.mutex); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_write(struct file *f, const char __user *buf, | 
 | 					 size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 | 	bool pm_pg_lock, use_bank; | 
 | 	unsigned instance_bank, sh_bank, se_bank; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* are we reading registers for which a PG lock is necessary? */ | 
 | 	pm_pg_lock = (*pos >> 23) & 1; | 
 |  | 
 | 	if (*pos & (1ULL << 62)) { | 
 | 		se_bank = (*pos & GENMASK_ULL(33, 24)) >> 24; | 
 | 		sh_bank = (*pos & GENMASK_ULL(43, 34)) >> 34; | 
 | 		instance_bank = (*pos & GENMASK_ULL(53, 44)) >> 44; | 
 |  | 
 | 		if (se_bank == 0x3FF) | 
 | 			se_bank = 0xFFFFFFFF; | 
 | 		if (sh_bank == 0x3FF) | 
 | 			sh_bank = 0xFFFFFFFF; | 
 | 		if (instance_bank == 0x3FF) | 
 | 			instance_bank = 0xFFFFFFFF; | 
 | 		use_bank = 1; | 
 | 	} else { | 
 | 		use_bank = 0; | 
 | 	} | 
 |  | 
 | 	*pos &= (1UL << 22) - 1; | 
 |  | 
 | 	if (use_bank) { | 
 | 		if ((sh_bank != 0xFFFFFFFF && sh_bank >= adev->gfx.config.max_sh_per_se) || | 
 | 		    (se_bank != 0xFFFFFFFF && se_bank >= adev->gfx.config.max_shader_engines)) | 
 | 			return -EINVAL; | 
 | 		mutex_lock(&adev->grbm_idx_mutex); | 
 | 		amdgpu_gfx_select_se_sh(adev, se_bank, | 
 | 					sh_bank, instance_bank); | 
 | 	} | 
 |  | 
 | 	if (pm_pg_lock) | 
 | 		mutex_lock(&adev->pm.mutex); | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		if (*pos > adev->rmmio_size) | 
 | 			return result; | 
 |  | 
 | 		r = get_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		WREG32(*pos >> 2, value); | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	if (use_bank) { | 
 | 		amdgpu_gfx_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff); | 
 | 		mutex_unlock(&adev->grbm_idx_mutex); | 
 | 	} | 
 |  | 
 | 	if (pm_pg_lock) | 
 | 		mutex_unlock(&adev->pm.mutex); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_pcie_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = RREG32_PCIE(*pos >> 2); | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_pcie_write(struct file *f, const char __user *buf, | 
 | 					 size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		r = get_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		WREG32_PCIE(*pos >> 2, value); | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_didt_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = RREG32_DIDT(*pos >> 2); | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_didt_write(struct file *f, const char __user *buf, | 
 | 					 size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		r = get_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		WREG32_DIDT(*pos >> 2, value); | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_smc_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = RREG32_SMC(*pos); | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_regs_smc_write(struct file *f, const char __user *buf, | 
 | 					 size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		r = get_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		WREG32_SMC(*pos, value); | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_gca_config_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	ssize_t result = 0; | 
 | 	int r; | 
 | 	uint32_t *config, no_regs = 0; | 
 |  | 
 | 	if (size & 0x3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	config = kmalloc_array(256, sizeof(*config), GFP_KERNEL); | 
 | 	if (!config) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* version, increment each time something is added */ | 
 | 	config[no_regs++] = 3; | 
 | 	config[no_regs++] = adev->gfx.config.max_shader_engines; | 
 | 	config[no_regs++] = adev->gfx.config.max_tile_pipes; | 
 | 	config[no_regs++] = adev->gfx.config.max_cu_per_sh; | 
 | 	config[no_regs++] = adev->gfx.config.max_sh_per_se; | 
 | 	config[no_regs++] = adev->gfx.config.max_backends_per_se; | 
 | 	config[no_regs++] = adev->gfx.config.max_texture_channel_caches; | 
 | 	config[no_regs++] = adev->gfx.config.max_gprs; | 
 | 	config[no_regs++] = adev->gfx.config.max_gs_threads; | 
 | 	config[no_regs++] = adev->gfx.config.max_hw_contexts; | 
 | 	config[no_regs++] = adev->gfx.config.sc_prim_fifo_size_frontend; | 
 | 	config[no_regs++] = adev->gfx.config.sc_prim_fifo_size_backend; | 
 | 	config[no_regs++] = adev->gfx.config.sc_hiz_tile_fifo_size; | 
 | 	config[no_regs++] = adev->gfx.config.sc_earlyz_tile_fifo_size; | 
 | 	config[no_regs++] = adev->gfx.config.num_tile_pipes; | 
 | 	config[no_regs++] = adev->gfx.config.backend_enable_mask; | 
 | 	config[no_regs++] = adev->gfx.config.mem_max_burst_length_bytes; | 
 | 	config[no_regs++] = adev->gfx.config.mem_row_size_in_kb; | 
 | 	config[no_regs++] = adev->gfx.config.shader_engine_tile_size; | 
 | 	config[no_regs++] = adev->gfx.config.num_gpus; | 
 | 	config[no_regs++] = adev->gfx.config.multi_gpu_tile_size; | 
 | 	config[no_regs++] = adev->gfx.config.mc_arb_ramcfg; | 
 | 	config[no_regs++] = adev->gfx.config.gb_addr_config; | 
 | 	config[no_regs++] = adev->gfx.config.num_rbs; | 
 |  | 
 | 	/* rev==1 */ | 
 | 	config[no_regs++] = adev->rev_id; | 
 | 	config[no_regs++] = adev->pg_flags; | 
 | 	config[no_regs++] = adev->cg_flags; | 
 |  | 
 | 	/* rev==2 */ | 
 | 	config[no_regs++] = adev->family; | 
 | 	config[no_regs++] = adev->external_rev_id; | 
 |  | 
 | 	/* rev==3 */ | 
 | 	config[no_regs++] = adev->pdev->device; | 
 | 	config[no_regs++] = adev->pdev->revision; | 
 | 	config[no_regs++] = adev->pdev->subsystem_device; | 
 | 	config[no_regs++] = adev->pdev->subsystem_vendor; | 
 |  | 
 | 	while (size && (*pos < no_regs * 4)) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = config[*pos >> 2]; | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) { | 
 | 			kfree(config); | 
 | 			return r; | 
 | 		} | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		*pos += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	kfree(config); | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = file_inode(f)->i_private; | 
 | 	int idx, x, outsize, r, valuesize; | 
 | 	uint32_t values[16]; | 
 |  | 
 | 	if (size & 3 || *pos & 0x3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (amdgpu_dpm == 0) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* convert offset to sensor number */ | 
 | 	idx = *pos >> 2; | 
 |  | 
 | 	valuesize = sizeof(values); | 
 | 	if (adev->powerplay.pp_funcs && adev->powerplay.pp_funcs->read_sensor) | 
 | 		r = amdgpu_dpm_read_sensor(adev, idx, &values[0], &valuesize); | 
 | 	else | 
 | 		return -EINVAL; | 
 |  | 
 | 	if (size > valuesize) | 
 | 		return -EINVAL; | 
 |  | 
 | 	outsize = 0; | 
 | 	x = 0; | 
 | 	if (!r) { | 
 | 		while (size) { | 
 | 			r = put_user(values[x++], (int32_t *)buf); | 
 | 			buf += 4; | 
 | 			size -= 4; | 
 | 			outsize += 4; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return !r ? outsize : r; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_wave_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = f->f_inode->i_private; | 
 | 	int r, x; | 
 | 	ssize_t result=0; | 
 | 	uint32_t offset, se, sh, cu, wave, simd, data[32]; | 
 |  | 
 | 	if (size & 3 || *pos & 3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* decode offset */ | 
 | 	offset = (*pos & GENMASK_ULL(6, 0)); | 
 | 	se = (*pos & GENMASK_ULL(14, 7)) >> 7; | 
 | 	sh = (*pos & GENMASK_ULL(22, 15)) >> 15; | 
 | 	cu = (*pos & GENMASK_ULL(30, 23)) >> 23; | 
 | 	wave = (*pos & GENMASK_ULL(36, 31)) >> 31; | 
 | 	simd = (*pos & GENMASK_ULL(44, 37)) >> 37; | 
 |  | 
 | 	/* switch to the specific se/sh/cu */ | 
 | 	mutex_lock(&adev->grbm_idx_mutex); | 
 | 	amdgpu_gfx_select_se_sh(adev, se, sh, cu); | 
 |  | 
 | 	x = 0; | 
 | 	if (adev->gfx.funcs->read_wave_data) | 
 | 		adev->gfx.funcs->read_wave_data(adev, simd, wave, data, &x); | 
 |  | 
 | 	amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); | 
 | 	mutex_unlock(&adev->grbm_idx_mutex); | 
 |  | 
 | 	if (!x) | 
 | 		return -EINVAL; | 
 |  | 
 | 	while (size && (offset < x * 4)) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = data[offset >> 2]; | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) | 
 | 			return r; | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		offset += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, | 
 | 					size_t size, loff_t *pos) | 
 | { | 
 | 	struct amdgpu_device *adev = f->f_inode->i_private; | 
 | 	int r; | 
 | 	ssize_t result = 0; | 
 | 	uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data; | 
 |  | 
 | 	if (size & 3 || *pos & 3) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* decode offset */ | 
 | 	offset = *pos & GENMASK_ULL(11, 0); | 
 | 	se = (*pos & GENMASK_ULL(19, 12)) >> 12; | 
 | 	sh = (*pos & GENMASK_ULL(27, 20)) >> 20; | 
 | 	cu = (*pos & GENMASK_ULL(35, 28)) >> 28; | 
 | 	wave = (*pos & GENMASK_ULL(43, 36)) >> 36; | 
 | 	simd = (*pos & GENMASK_ULL(51, 44)) >> 44; | 
 | 	thread = (*pos & GENMASK_ULL(59, 52)) >> 52; | 
 | 	bank = (*pos & GENMASK_ULL(61, 60)) >> 60; | 
 |  | 
 | 	data = kmalloc_array(1024, sizeof(*data), GFP_KERNEL); | 
 | 	if (!data) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	/* switch to the specific se/sh/cu */ | 
 | 	mutex_lock(&adev->grbm_idx_mutex); | 
 | 	amdgpu_gfx_select_se_sh(adev, se, sh, cu); | 
 |  | 
 | 	if (bank == 0) { | 
 | 		if (adev->gfx.funcs->read_wave_vgprs) | 
 | 			adev->gfx.funcs->read_wave_vgprs(adev, simd, wave, thread, offset, size>>2, data); | 
 | 	} else { | 
 | 		if (adev->gfx.funcs->read_wave_sgprs) | 
 | 			adev->gfx.funcs->read_wave_sgprs(adev, simd, wave, offset, size>>2, data); | 
 | 	} | 
 |  | 
 | 	amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); | 
 | 	mutex_unlock(&adev->grbm_idx_mutex); | 
 |  | 
 | 	while (size) { | 
 | 		uint32_t value; | 
 |  | 
 | 		value = data[offset++]; | 
 | 		r = put_user(value, (uint32_t *)buf); | 
 | 		if (r) { | 
 | 			result = r; | 
 | 			goto err; | 
 | 		} | 
 |  | 
 | 		result += 4; | 
 | 		buf += 4; | 
 | 		size -= 4; | 
 | 	} | 
 |  | 
 | err: | 
 | 	kfree(data); | 
 | 	return result; | 
 | } | 
 |  | 
 | static const struct file_operations amdgpu_debugfs_regs_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_regs_read, | 
 | 	.write = amdgpu_debugfs_regs_write, | 
 | 	.llseek = default_llseek | 
 | }; | 
 | static const struct file_operations amdgpu_debugfs_regs_didt_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_regs_didt_read, | 
 | 	.write = amdgpu_debugfs_regs_didt_write, | 
 | 	.llseek = default_llseek | 
 | }; | 
 | static const struct file_operations amdgpu_debugfs_regs_pcie_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_regs_pcie_read, | 
 | 	.write = amdgpu_debugfs_regs_pcie_write, | 
 | 	.llseek = default_llseek | 
 | }; | 
 | static const struct file_operations amdgpu_debugfs_regs_smc_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_regs_smc_read, | 
 | 	.write = amdgpu_debugfs_regs_smc_write, | 
 | 	.llseek = default_llseek | 
 | }; | 
 |  | 
 | static const struct file_operations amdgpu_debugfs_gca_config_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_gca_config_read, | 
 | 	.llseek = default_llseek | 
 | }; | 
 |  | 
 | static const struct file_operations amdgpu_debugfs_sensors_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_sensor_read, | 
 | 	.llseek = default_llseek | 
 | }; | 
 |  | 
 | static const struct file_operations amdgpu_debugfs_wave_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_wave_read, | 
 | 	.llseek = default_llseek | 
 | }; | 
 | static const struct file_operations amdgpu_debugfs_gpr_fops = { | 
 | 	.owner = THIS_MODULE, | 
 | 	.read = amdgpu_debugfs_gpr_read, | 
 | 	.llseek = default_llseek | 
 | }; | 
 |  | 
 | static const struct file_operations *debugfs_regs[] = { | 
 | 	&amdgpu_debugfs_regs_fops, | 
 | 	&amdgpu_debugfs_regs_didt_fops, | 
 | 	&amdgpu_debugfs_regs_pcie_fops, | 
 | 	&amdgpu_debugfs_regs_smc_fops, | 
 | 	&amdgpu_debugfs_gca_config_fops, | 
 | 	&amdgpu_debugfs_sensors_fops, | 
 | 	&amdgpu_debugfs_wave_fops, | 
 | 	&amdgpu_debugfs_gpr_fops, | 
 | }; | 
 |  | 
 | static const char *debugfs_regs_names[] = { | 
 | 	"amdgpu_regs", | 
 | 	"amdgpu_regs_didt", | 
 | 	"amdgpu_regs_pcie", | 
 | 	"amdgpu_regs_smc", | 
 | 	"amdgpu_gca_config", | 
 | 	"amdgpu_sensors", | 
 | 	"amdgpu_wave", | 
 | 	"amdgpu_gpr", | 
 | }; | 
 |  | 
 | int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) | 
 | { | 
 | 	struct drm_minor *minor = adev->ddev->primary; | 
 | 	struct dentry *ent, *root = minor->debugfs_root; | 
 | 	unsigned i, j; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) { | 
 | 		ent = debugfs_create_file(debugfs_regs_names[i], | 
 | 					  S_IFREG | S_IRUGO, root, | 
 | 					  adev, debugfs_regs[i]); | 
 | 		if (IS_ERR(ent)) { | 
 | 			for (j = 0; j < i; j++) { | 
 | 				debugfs_remove(adev->debugfs_regs[i]); | 
 | 				adev->debugfs_regs[i] = NULL; | 
 | 			} | 
 | 			return PTR_ERR(ent); | 
 | 		} | 
 |  | 
 | 		if (!i) | 
 | 			i_size_write(ent->d_inode, adev->rmmio_size); | 
 | 		adev->debugfs_regs[i] = ent; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev) | 
 | { | 
 | 	unsigned i; | 
 |  | 
 | 	for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) { | 
 | 		if (adev->debugfs_regs[i]) { | 
 | 			debugfs_remove(adev->debugfs_regs[i]); | 
 | 			adev->debugfs_regs[i] = NULL; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int amdgpu_debugfs_test_ib(struct seq_file *m, void *data) | 
 | { | 
 | 	struct drm_info_node *node = (struct drm_info_node *) m->private; | 
 | 	struct drm_device *dev = node->minor->dev; | 
 | 	struct amdgpu_device *adev = dev->dev_private; | 
 | 	int r = 0, i; | 
 |  | 
 | 	/* hold on the scheduler */ | 
 | 	for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | 
 | 		struct amdgpu_ring *ring = adev->rings[i]; | 
 |  | 
 | 		if (!ring || !ring->sched.thread) | 
 | 			continue; | 
 | 		kthread_park(ring->sched.thread); | 
 | 	} | 
 |  | 
 | 	seq_printf(m, "run ib test:\n"); | 
 | 	r = amdgpu_ib_ring_tests(adev); | 
 | 	if (r) | 
 | 		seq_printf(m, "ib ring tests failed (%d).\n", r); | 
 | 	else | 
 | 		seq_printf(m, "ib ring tests passed.\n"); | 
 |  | 
 | 	/* go on the scheduler */ | 
 | 	for (i = 0; i < AMDGPU_MAX_RINGS; i++) { | 
 | 		struct amdgpu_ring *ring = adev->rings[i]; | 
 |  | 
 | 		if (!ring || !ring->sched.thread) | 
 | 			continue; | 
 | 		kthread_unpark(ring->sched.thread); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int amdgpu_debugfs_get_vbios_dump(struct seq_file *m, void *data) | 
 | { | 
 | 	struct drm_info_node *node = (struct drm_info_node *) m->private; | 
 | 	struct drm_device *dev = node->minor->dev; | 
 | 	struct amdgpu_device *adev = dev->dev_private; | 
 |  | 
 | 	seq_write(m, adev->bios, adev->bios_size); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int amdgpu_debugfs_evict_vram(struct seq_file *m, void *data) | 
 | { | 
 | 	struct drm_info_node *node = (struct drm_info_node *)m->private; | 
 | 	struct drm_device *dev = node->minor->dev; | 
 | 	struct amdgpu_device *adev = dev->dev_private; | 
 |  | 
 | 	seq_printf(m, "(%d)\n", amdgpu_bo_evict_vram(adev)); | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const struct drm_info_list amdgpu_debugfs_list[] = { | 
 | 	{"amdgpu_vbios", amdgpu_debugfs_get_vbios_dump}, | 
 | 	{"amdgpu_test_ib", &amdgpu_debugfs_test_ib}, | 
 | 	{"amdgpu_evict_vram", &amdgpu_debugfs_evict_vram} | 
 | }; | 
 |  | 
 | int amdgpu_debugfs_init(struct amdgpu_device *adev) | 
 | { | 
 | 	return amdgpu_debugfs_add_files(adev, amdgpu_debugfs_list, | 
 | 					ARRAY_SIZE(amdgpu_debugfs_list)); | 
 | } | 
 |  | 
 | #else | 
 | int amdgpu_debugfs_init(struct amdgpu_device *adev) | 
 | { | 
 | 	return 0; | 
 | } | 
 | int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) | 
 | { | 
 | 	return 0; | 
 | } | 
 | void amdgpu_debugfs_regs_cleanup(struct amdgpu_device *adev) { } | 
 | #endif |