/* * 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. *
*/
/** * amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes * * @read: True if reading * @f: open file handle * @buf: User buffer to write/read to * @size: Number of bytes to write/read * @pos: Offset to seek to * * This debugfs entry has special meaning on the offset being sought. * Various bits have different meanings: * * Bit 62: Indicates a GRBM bank switch is needed * Bit 61: Indicates a SRBM bank switch is needed (implies bit 62 is * zero) * Bits 24..33: The SE or ME selector if needed * Bits 34..43: The SH (or SA) or PIPE selector if needed * Bits 44..53: The INSTANCE (or CU/WGP) or QUEUE selector if needed * * Bit 23: Indicates that the PM power gating lock should be held * This is necessary to read registers that might be * unreliable during a power gating transistion. * * The lower bits are the BYTE offset of the register to read. This * allows reading multiple registers in a single call and having * the returned size reflect that.
*/ staticint amdgpu_debugfs_process_reg_op(bool 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, use_ring; unsignedint instance_bank, sh_bank, se_bank, me, pipe, queue, vmid;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
data = kcalloc(1024, sizeof(*data), GFP_KERNEL); if (!data) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return -ENOMEM;
}
/* switch to the specific se/sh/cu */
mutex_lock(&adev->grbm_idx_mutex);
amdgpu_gfx_select_se_sh(adev, rd->id.se, rd->id.sh, rd->id.cu, rd->id.xcc_id);
if (!rd->id.gpr_or_wave) {
x = 0; if (adev->gfx.funcs->read_wave_data)
adev->gfx.funcs->read_wave_data(adev, rd->id.xcc_id, rd->id.simd, rd->id.wave, data, &x);
} else {
x = size >> 2; if (rd->id.gpr.vpgr_or_sgpr) { if (adev->gfx.funcs->read_wave_vgprs)
adev->gfx.funcs->read_wave_vgprs(adev, rd->id.xcc_id, rd->id.simd, rd->id.wave, rd->id.gpr.thread, *pos, size>>2, data);
} else { if (adev->gfx.funcs->read_wave_sgprs)
adev->gfx.funcs->read_wave_sgprs(adev, rd->id.xcc_id, rd->id.simd, rd->id.wave, *pos, size>>2, data);
}
}
staticlong amdgpu_debugfs_gprwave_ioctl(struct file *f, unsignedint cmd, unsignedlong data)
{ struct amdgpu_debugfs_gprwave_data *rd = f->private_data; int r = 0;
mutex_lock(&rd->lock);
switch (cmd) { case AMDGPU_DEBUGFS_GPRWAVE_IOC_SET_STATE: if (copy_from_user(&rd->id,
(struct amdgpu_debugfs_gprwave_iocdata *)data, sizeof(rd->id)))
r = -EFAULT; goto done; default:
r = -EINVAL; goto done;
}
done:
mutex_unlock(&rd->lock); return r;
}
/** * amdgpu_debugfs_regs_pcie_read - Read from a PCIE register * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to read. This * allows reading multiple registers in a single call and having * the returned size reflect that.
*/ 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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
if (upper_32_bits(*pos))
value = RREG32_PCIE_EXT(*pos); else
value = RREG32_PCIE(*pos);
r = put_user(value, (uint32_t *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_regs_pcie_write - Write to a PCIE register * * @f: open file handle * @buf: User buffer to write data from * @size: Number of bytes to write * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to write. This * allows writing multiple registers in a single call and having * the returned size reflect that.
*/ static ssize_t amdgpu_debugfs_regs_pcie_write(struct file *f, constchar __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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
r = get_user(value, (uint32_t *)buf); if (r) goto out;
if (upper_32_bits(*pos))
WREG32_PCIE_EXT(*pos, value); else
WREG32_PCIE(*pos, value);
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_regs_didt_read - Read from a DIDT register * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to read. This * allows reading multiple registers in a single call and having * the returned size reflect that.
*/ 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;
if (!adev->didt_rreg) return -EOPNOTSUPP;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
value = RREG32_DIDT(*pos >> 2);
r = put_user(value, (uint32_t *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_regs_didt_write - Write to a DIDT register * * @f: open file handle * @buf: User buffer to write data from * @size: Number of bytes to write * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to write. This * allows writing multiple registers in a single call and having * the returned size reflect that.
*/ static ssize_t amdgpu_debugfs_regs_didt_write(struct file *f, constchar __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;
if (!adev->didt_wreg) return -EOPNOTSUPP;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
r = get_user(value, (uint32_t *)buf); if (r) goto out;
WREG32_DIDT(*pos >> 2, value);
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_regs_smc_read - Read from a SMC register * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to read. This * allows reading multiple registers in a single call and having * the returned size reflect that.
*/ 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 (!adev->smc_rreg) return -EOPNOTSUPP;
if (size & 0x3 || *pos & 0x3) return -EINVAL;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
value = RREG32_SMC(*pos);
r = put_user(value, (uint32_t *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_regs_smc_write - Write to a SMC register * * @f: open file handle * @buf: User buffer to write data from * @size: Number of bytes to write * @pos: Offset to seek to * * The lower bits are the BYTE offset of the register to write. This * allows writing multiple registers in a single call and having * the returned size reflect that.
*/ static ssize_t amdgpu_debugfs_regs_smc_write(struct file *f, constchar __user *buf,
size_t size, loff_t *pos)
{ struct amdgpu_device *adev = file_inode(f)->i_private;
ssize_t result = 0; int r;
if (!adev->smc_wreg) return -EOPNOTSUPP;
if (size & 0x3 || *pos & 0x3) return -EINVAL;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
r = get_user(value, (uint32_t *)buf); if (r) goto out;
WREG32_SMC(*pos, value);
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
amdgpu_virt_disable_access_debugfs(adev); return r;
}
/** * amdgpu_debugfs_gca_config_read - Read from gfx config data * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * This file is used to access configuration data in a somewhat * stable fashion. The format is a series of DWORDs with the first * indicating which revision it is. New content is appended to the * end so that older software can still read the data.
*/
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;
}
/** * amdgpu_debugfs_sensor_read - Read from the powerplay sensors * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The offset is treated as the BYTE address of one of the sensors * enumerated in amd/include/kgd_pp_interface.h under the * 'amd_pp_sensors' enumeration. For instance to read the UVD VCLK * you would use the offset 3 * 4 = 12.
*/ 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 (!adev->pm.dpm_enabled) return -EINVAL;
/* convert offset to sensor number */
idx = *pos >> 2;
valuesize = sizeof(values);
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_virt_enable_access_debugfs(adev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
r = amdgpu_dpm_read_sensor(adev, idx, &values[0], &valuesize);
/** amdgpu_debugfs_wave_read - Read WAVE STATUS data * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The offset being sought changes which wave that the status data * will be returned for. The bits are used as follows: * * Bits 0..6: Byte offset into data * Bits 7..14: SE selector * Bits 15..22: SH/SA selector * Bits 23..30: CU/{WGP+SIMD} selector * Bits 31..36: WAVE ID selector * Bits 37..44: SIMD ID selector * * The returned data begins with one DWORD of version information * Followed by WAVE STATUS registers relevant to the GFX IP version * being used. See gfx_v8_0_read_wave_data() for an example output.
*/ 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];
/** amdgpu_debugfs_gpr_read - Read wave gprs * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * The offset being sought changes which wave that the status data * will be returned for. The bits are used as follows: * * Bits 0..11: Byte offset into data * Bits 12..19: SE selector * Bits 20..27: SH/SA selector * Bits 28..35: CU/{WGP+SIMD} selector * Bits 36..43: WAVE ID selector * Bits 37..44: SIMD ID selector * Bits 52..59: Thread selector * Bits 60..61: Bank selector (VGPR=0,SGPR=1) * * The return data comes from the SGPR or VGPR register bank for * the selected operational unit.
*/ 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;
/** * amdgpu_debugfs_gfxoff_residency_read - Read GFXOFF residency * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to * * Read the last residency value logged. It doesn't auto update, one needs to * stop logging before getting the current value.
*/ static ssize_t amdgpu_debugfs_gfxoff_residency_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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
r = amdgpu_get_gfx_off_residency(adev, &value); if (r) goto out;
r = put_user(value, (uint32_t *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
}
/** * amdgpu_debugfs_gfxoff_residency_write - Log GFXOFF Residency * * @f: open file handle * @buf: User buffer to write data from * @size: Number of bytes to write * @pos: Offset to seek to * * Write a 32-bit non-zero to start logging; write a 32-bit zero to stop
*/ static ssize_t amdgpu_debugfs_gfxoff_residency_write(struct file *f, constchar __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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
u32 value;
r = get_user(value, (uint32_t *)buf); if (r) goto out;
amdgpu_set_gfx_off_residency(adev, value ? true : false);
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
}
/** * amdgpu_debugfs_gfxoff_count_read - Read GFXOFF entry count * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to
*/ static ssize_t amdgpu_debugfs_gfxoff_count_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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
u64 value = 0;
r = amdgpu_get_gfx_off_entrycount(adev, &value); if (r) goto out;
r = put_user(value, (u64 *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
}
/** * amdgpu_debugfs_gfxoff_write - Enable/disable GFXOFF * * @f: open file handle * @buf: User buffer to write data from * @size: Number of bytes to write * @pos: Offset to seek to * * Write a 32-bit zero to disable or a 32-bit non-zero to enable
*/ static ssize_t amdgpu_debugfs_gfxoff_write(struct file *f, constchar __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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
uint32_t value;
r = get_user(value, (uint32_t *)buf); if (r) goto out;
amdgpu_gfx_off_ctrl(adev, value ? true : false);
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
return r;
}
/** * amdgpu_debugfs_gfxoff_read - read gfxoff status * * @f: open file handle * @buf: User buffer to store read data in * @size: Number of bytes to read * @pos: Offset to seek to
*/ static ssize_t amdgpu_debugfs_gfxoff_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;
r = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (r < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return r;
}
while (size) {
u32 value = adev->gfx.gfx_off_state;
r = put_user(value, (u32 *)buf); if (r) goto out;
result += 4;
buf += 4;
*pos += 4;
size -= 4;
}
r = result;
out:
pm_runtime_mark_last_busy(adev_to_drm(adev)->dev);
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev);
/** * amdgpu_debugfs_regs_init - Initialize debugfs entries that provide * register access. * * @adev: The device to attach the debugfs entries to
*/ int amdgpu_debugfs_regs_init(struct amdgpu_device *adev)
{ struct drm_minor *minor = adev_to_drm(adev)->primary; struct dentry *ent, *root = minor->debugfs_root; unsignedint i;
for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
ent = debugfs_create_file(debugfs_regs_names[i],
S_IFREG | 0400, root,
adev, debugfs_regs[i]); if (!i && !IS_ERR_OR_NULL(ent))
i_size_write(ent->d_inode, adev->rmmio_size);
}
/* Avoid accidently unparking the sched thread during GPU reset */
r = down_read_killable(&adev->reset_domain->sem); if (r) goto pro_end;
/* stop the scheduler */
drm_sched_wqueue_stop(&ring->sched);
/* preempt the IB */
r = amdgpu_ring_preempt_ib(ring); if (r) {
DRM_WARN("failed to preempt ring %d\n", ring->idx); goto failure;
}
amdgpu_fence_process(ring);
if (atomic_read(&ring->fence_drv.last_seq) !=
ring->fence_drv.sync_seq) {
DRM_INFO("ring %d was preempted\n", ring->idx);
amdgpu_ib_preempt_mark_partial_job(ring);
/* swap out the old fences */
amdgpu_ib_preempt_fences_swap(ring, fences);
amdgpu_fence_driver_force_completion(ring);
/* resubmit unfinished jobs */
amdgpu_ib_preempt_job_recovery(&ring->sched);
/* wait for jobs finished */
amdgpu_fence_wait_empty(ring);
/* signal the old fences */
amdgpu_ib_preempt_signal_fences(fences, length);
}
failure: /* restart the scheduler */
drm_sched_wqueue_start(&ring->sched);
up_read(&adev->reset_domain->sem);
pro_end:
kfree(fences);
return r;
}
staticint amdgpu_debugfs_sclk_set(void *data, u64 val)
{ int ret = 0;
uint32_t max_freq, min_freq; struct amdgpu_device *adev = (struct amdgpu_device *)data;
if (amdgpu_sriov_multi_vf_mode(adev)) return -EINVAL;
ret = pm_runtime_get_sync(adev_to_drm(adev)->dev); if (ret < 0) {
pm_runtime_put_autosuspend(adev_to_drm(adev)->dev); return ret;
}
ret = amdgpu_dpm_get_dpm_freq_range(adev, PP_SCLK, &min_freq, &max_freq); if (ret == -EOPNOTSUPP) {
ret = 0; goto out;
} if (ret || val > max_freq || val < min_freq) {
ret = -EINVAL; goto out;
}
ret = amdgpu_dpm_set_soft_freq_range(adev, PP_SCLK, (uint32_t)val, (uint32_t)val); if (ret)
ret = -EINVAL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.