Commit aaa36a97 authored by Alex Deucher's avatar Alex Deucher

drm/amdgpu: Add initial VI support

This adds initial support for VI asics.  This
includes Iceland, Tonga, and Carrizo.  Our inital
focus as been Carrizo, so there are still gaps in
support for Tonga and Iceland, notably power
management.
Acked-by: default avatarChristian König <christian.koenig@amd.com>
Acked-by: default avatarJammy Zhou <Jammy.Zhou@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent a2e73f56
...@@ -18,29 +18,57 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \ ...@@ -18,29 +18,57 @@ amdgpu-y += amdgpu_device.o amdgpu_kms.o \
amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \ amdgpu_prime.o amdgpu_vm.o amdgpu_ib.o amdgpu_pll.o \
amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o amdgpu_ucode.o amdgpu_bo_list.o amdgpu_ctx.o amdgpu_sync.o
# add asic specific block
amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o gmc_v7_0.o cik_ih.o kv_smc.o kv_dpm.o \ amdgpu-$(CONFIG_DRM_AMDGPU_CIK)+= cik.o gmc_v7_0.o cik_ih.o kv_smc.o kv_dpm.o \
ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o ci_smc.o ci_dpm.o dce_v8_0.o gfx_v7_0.o cik_sdma.o uvd_v4_2.o vce_v2_0.o
amdgpu-y += \
vi.o
# add GMC block
amdgpu-y += \
gmc_v8_0.o
# add IH block # add IH block
amdgpu-y += \ amdgpu-y += \
amdgpu_irq.o \ amdgpu_irq.o \
amdgpu_ih.o amdgpu_ih.o \
iceland_ih.o \
tonga_ih.o \
cz_ih.o
# add SMC block # add SMC block
amdgpu-y += \ amdgpu-y += \
amdgpu_dpm.o amdgpu_dpm.o \
cz_smc.o cz_dpm.o \
tonga_smc.o tonga_dpm.o \
iceland_smc.o iceland_dpm.o
# add DCE block
amdgpu-y += \
dce_v10_0.o \
dce_v11_0.o
# add GFX block # add GFX block
amdgpu-y += \ amdgpu-y += \
amdgpu_gfx.o amdgpu_gfx.o \
gfx_v8_0.o
# add async DMA block
amdgpu-y += \
sdma_v2_4.o \
sdma_v3_0.o
# add UVD block # add UVD block
amdgpu-y += \ amdgpu-y += \
amdgpu_uvd.o amdgpu_uvd.o \
uvd_v5_0.o \
uvd_v6_0.o
# add VCE block # add VCE block
amdgpu-y += \ amdgpu-y += \
amdgpu_vce.o amdgpu_vce.o \
vce_v3_0.o
amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o amdgpu-$(CONFIG_COMPAT) += amdgpu_ioc32.o
amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o amdgpu-$(CONFIG_VGA_SWITCHEROO) += amdgpu_atpx_handler.o
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#ifdef CONFIG_DRM_AMDGPU_CIK #ifdef CONFIG_DRM_AMDGPU_CIK
#include "cik.h" #include "cik.h"
#endif #endif
#include "vi.h"
#include "bif/bif_4_1_d.h" #include "bif/bif_4_1_d.h"
static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev); static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev);
...@@ -1154,9 +1155,21 @@ int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev, ...@@ -1154,9 +1155,21 @@ int amdgpu_ip_block_version_cmp(struct amdgpu_device *adev,
static int amdgpu_early_init(struct amdgpu_device *adev) static int amdgpu_early_init(struct amdgpu_device *adev)
{ {
int i, r = -EINVAL; int i, r;
switch (adev->asic_type) { switch (adev->asic_type) {
case CHIP_TOPAZ:
case CHIP_TONGA:
case CHIP_CARRIZO:
if (adev->asic_type == CHIP_CARRIZO)
adev->family = AMDGPU_FAMILY_CZ;
else
adev->family = AMDGPU_FAMILY_VI;
r = vi_set_ip_blocks(adev);
if (r)
return r;
break;
#ifdef CONFIG_DRM_AMDGPU_CIK #ifdef CONFIG_DRM_AMDGPU_CIK
case CHIP_BONAIRE: case CHIP_BONAIRE:
case CHIP_HAWAII: case CHIP_HAWAII:
......
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
static const unsigned int vi_SECT_CONTEXT_def_1[] =
{
0x00000000, // DB_RENDER_CONTROL
0x00000000, // DB_COUNT_CONTROL
0x00000000, // DB_DEPTH_VIEW
0x00000000, // DB_RENDER_OVERRIDE
0x00000000, // DB_RENDER_OVERRIDE2
0x00000000, // DB_HTILE_DATA_BASE
0, // HOLE
0, // HOLE
0x00000000, // DB_DEPTH_BOUNDS_MIN
0x00000000, // DB_DEPTH_BOUNDS_MAX
0x00000000, // DB_STENCIL_CLEAR
0x00000000, // DB_DEPTH_CLEAR
0x00000000, // PA_SC_SCREEN_SCISSOR_TL
0x40004000, // PA_SC_SCREEN_SCISSOR_BR
0, // HOLE
0x00000000, // DB_DEPTH_INFO
0x00000000, // DB_Z_INFO
0x00000000, // DB_STENCIL_INFO
0x00000000, // DB_Z_READ_BASE
0x00000000, // DB_STENCIL_READ_BASE
0x00000000, // DB_Z_WRITE_BASE
0x00000000, // DB_STENCIL_WRITE_BASE
0x00000000, // DB_DEPTH_SIZE
0x00000000, // DB_DEPTH_SLICE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // TA_BC_BASE_ADDR
0x00000000, // TA_BC_BASE_ADDR_HI
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // COHER_DEST_BASE_HI_0
0x00000000, // COHER_DEST_BASE_HI_1
0x00000000, // COHER_DEST_BASE_HI_2
0x00000000, // COHER_DEST_BASE_HI_3
0x00000000, // COHER_DEST_BASE_2
0x00000000, // COHER_DEST_BASE_3
0x00000000, // PA_SC_WINDOW_OFFSET
0x80000000, // PA_SC_WINDOW_SCISSOR_TL
0x40004000, // PA_SC_WINDOW_SCISSOR_BR
0x0000ffff, // PA_SC_CLIPRECT_RULE
0x00000000, // PA_SC_CLIPRECT_0_TL
0x40004000, // PA_SC_CLIPRECT_0_BR
0x00000000, // PA_SC_CLIPRECT_1_TL
0x40004000, // PA_SC_CLIPRECT_1_BR
0x00000000, // PA_SC_CLIPRECT_2_TL
0x40004000, // PA_SC_CLIPRECT_2_BR
0x00000000, // PA_SC_CLIPRECT_3_TL
0x40004000, // PA_SC_CLIPRECT_3_BR
0xaa99aaaa, // PA_SC_EDGERULE
0x00000000, // PA_SU_HARDWARE_SCREEN_OFFSET
0xffffffff, // CB_TARGET_MASK
0xffffffff, // CB_SHADER_MASK
0x80000000, // PA_SC_GENERIC_SCISSOR_TL
0x40004000, // PA_SC_GENERIC_SCISSOR_BR
0x00000000, // COHER_DEST_BASE_0
0x00000000, // COHER_DEST_BASE_1
0x80000000, // PA_SC_VPORT_SCISSOR_0_TL
0x40004000, // PA_SC_VPORT_SCISSOR_0_BR
0x80000000, // PA_SC_VPORT_SCISSOR_1_TL
0x40004000, // PA_SC_VPORT_SCISSOR_1_BR
0x80000000, // PA_SC_VPORT_SCISSOR_2_TL
0x40004000, // PA_SC_VPORT_SCISSOR_2_BR
0x80000000, // PA_SC_VPORT_SCISSOR_3_TL
0x40004000, // PA_SC_VPORT_SCISSOR_3_BR
0x80000000, // PA_SC_VPORT_SCISSOR_4_TL
0x40004000, // PA_SC_VPORT_SCISSOR_4_BR
0x80000000, // PA_SC_VPORT_SCISSOR_5_TL
0x40004000, // PA_SC_VPORT_SCISSOR_5_BR
0x80000000, // PA_SC_VPORT_SCISSOR_6_TL
0x40004000, // PA_SC_VPORT_SCISSOR_6_BR
0x80000000, // PA_SC_VPORT_SCISSOR_7_TL
0x40004000, // PA_SC_VPORT_SCISSOR_7_BR
0x80000000, // PA_SC_VPORT_SCISSOR_8_TL
0x40004000, // PA_SC_VPORT_SCISSOR_8_BR
0x80000000, // PA_SC_VPORT_SCISSOR_9_TL
0x40004000, // PA_SC_VPORT_SCISSOR_9_BR
0x80000000, // PA_SC_VPORT_SCISSOR_10_TL
0x40004000, // PA_SC_VPORT_SCISSOR_10_BR
0x80000000, // PA_SC_VPORT_SCISSOR_11_TL
0x40004000, // PA_SC_VPORT_SCISSOR_11_BR
0x80000000, // PA_SC_VPORT_SCISSOR_12_TL
0x40004000, // PA_SC_VPORT_SCISSOR_12_BR
0x80000000, // PA_SC_VPORT_SCISSOR_13_TL
0x40004000, // PA_SC_VPORT_SCISSOR_13_BR
0x80000000, // PA_SC_VPORT_SCISSOR_14_TL
0x40004000, // PA_SC_VPORT_SCISSOR_14_BR
0x80000000, // PA_SC_VPORT_SCISSOR_15_TL
0x40004000, // PA_SC_VPORT_SCISSOR_15_BR
0x00000000, // PA_SC_VPORT_ZMIN_0
0x3f800000, // PA_SC_VPORT_ZMAX_0
0x00000000, // PA_SC_VPORT_ZMIN_1
0x3f800000, // PA_SC_VPORT_ZMAX_1
0x00000000, // PA_SC_VPORT_ZMIN_2
0x3f800000, // PA_SC_VPORT_ZMAX_2
0x00000000, // PA_SC_VPORT_ZMIN_3
0x3f800000, // PA_SC_VPORT_ZMAX_3
0x00000000, // PA_SC_VPORT_ZMIN_4
0x3f800000, // PA_SC_VPORT_ZMAX_4
0x00000000, // PA_SC_VPORT_ZMIN_5
0x3f800000, // PA_SC_VPORT_ZMAX_5
0x00000000, // PA_SC_VPORT_ZMIN_6
0x3f800000, // PA_SC_VPORT_ZMAX_6
0x00000000, // PA_SC_VPORT_ZMIN_7
0x3f800000, // PA_SC_VPORT_ZMAX_7
0x00000000, // PA_SC_VPORT_ZMIN_8
0x3f800000, // PA_SC_VPORT_ZMAX_8
0x00000000, // PA_SC_VPORT_ZMIN_9
0x3f800000, // PA_SC_VPORT_ZMAX_9
0x00000000, // PA_SC_VPORT_ZMIN_10
0x3f800000, // PA_SC_VPORT_ZMAX_10
0x00000000, // PA_SC_VPORT_ZMIN_11
0x3f800000, // PA_SC_VPORT_ZMAX_11
0x00000000, // PA_SC_VPORT_ZMIN_12
0x3f800000, // PA_SC_VPORT_ZMAX_12
0x00000000, // PA_SC_VPORT_ZMIN_13
0x3f800000, // PA_SC_VPORT_ZMAX_13
0x00000000, // PA_SC_VPORT_ZMIN_14
0x3f800000, // PA_SC_VPORT_ZMAX_14
0x00000000, // PA_SC_VPORT_ZMIN_15
0x3f800000, // PA_SC_VPORT_ZMAX_15
};
static const unsigned int vi_SECT_CONTEXT_def_2[] =
{
0x00000000, // PA_SC_SCREEN_EXTENT_CONTROL
0, // HOLE
0x00000000, // CP_PERFMON_CNTX_CNTL
0x00000000, // CP_RINGID
0x00000000, // CP_VMID
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0xffffffff, // VGT_MAX_VTX_INDX
0x00000000, // VGT_MIN_VTX_INDX
0x00000000, // VGT_INDX_OFFSET
0x00000000, // VGT_MULTI_PRIM_IB_RESET_INDX
0, // HOLE
0x00000000, // CB_BLEND_RED
0x00000000, // CB_BLEND_GREEN
0x00000000, // CB_BLEND_BLUE
0x00000000, // CB_BLEND_ALPHA
0x00000000, // CB_DCC_CONTROL
0, // HOLE
0x00000000, // DB_STENCIL_CONTROL
0x00000000, // DB_STENCILREFMASK
0x00000000, // DB_STENCILREFMASK_BF
0, // HOLE
0x00000000, // PA_CL_VPORT_XSCALE
0x00000000, // PA_CL_VPORT_XOFFSET
0x00000000, // PA_CL_VPORT_YSCALE
0x00000000, // PA_CL_VPORT_YOFFSET
0x00000000, // PA_CL_VPORT_ZSCALE
0x00000000, // PA_CL_VPORT_ZOFFSET
0x00000000, // PA_CL_VPORT_XSCALE_1
0x00000000, // PA_CL_VPORT_XOFFSET_1
0x00000000, // PA_CL_VPORT_YSCALE_1
0x00000000, // PA_CL_VPORT_YOFFSET_1
0x00000000, // PA_CL_VPORT_ZSCALE_1
0x00000000, // PA_CL_VPORT_ZOFFSET_1
0x00000000, // PA_CL_VPORT_XSCALE_2
0x00000000, // PA_CL_VPORT_XOFFSET_2
0x00000000, // PA_CL_VPORT_YSCALE_2
0x00000000, // PA_CL_VPORT_YOFFSET_2
0x00000000, // PA_CL_VPORT_ZSCALE_2
0x00000000, // PA_CL_VPORT_ZOFFSET_2
0x00000000, // PA_CL_VPORT_XSCALE_3
0x00000000, // PA_CL_VPORT_XOFFSET_3
0x00000000, // PA_CL_VPORT_YSCALE_3
0x00000000, // PA_CL_VPORT_YOFFSET_3
0x00000000, // PA_CL_VPORT_ZSCALE_3
0x00000000, // PA_CL_VPORT_ZOFFSET_3
0x00000000, // PA_CL_VPORT_XSCALE_4
0x00000000, // PA_CL_VPORT_XOFFSET_4
0x00000000, // PA_CL_VPORT_YSCALE_4
0x00000000, // PA_CL_VPORT_YOFFSET_4
0x00000000, // PA_CL_VPORT_ZSCALE_4
0x00000000, // PA_CL_VPORT_ZOFFSET_4
0x00000000, // PA_CL_VPORT_XSCALE_5
0x00000000, // PA_CL_VPORT_XOFFSET_5
0x00000000, // PA_CL_VPORT_YSCALE_5
0x00000000, // PA_CL_VPORT_YOFFSET_5
0x00000000, // PA_CL_VPORT_ZSCALE_5
0x00000000, // PA_CL_VPORT_ZOFFSET_5
0x00000000, // PA_CL_VPORT_XSCALE_6
0x00000000, // PA_CL_VPORT_XOFFSET_6
0x00000000, // PA_CL_VPORT_YSCALE_6
0x00000000, // PA_CL_VPORT_YOFFSET_6
0x00000000, // PA_CL_VPORT_ZSCALE_6
0x00000000, // PA_CL_VPORT_ZOFFSET_6
0x00000000, // PA_CL_VPORT_XSCALE_7
0x00000000, // PA_CL_VPORT_XOFFSET_7
0x00000000, // PA_CL_VPORT_YSCALE_7
0x00000000, // PA_CL_VPORT_YOFFSET_7
0x00000000, // PA_CL_VPORT_ZSCALE_7
0x00000000, // PA_CL_VPORT_ZOFFSET_7
0x00000000, // PA_CL_VPORT_XSCALE_8
0x00000000, // PA_CL_VPORT_XOFFSET_8
0x00000000, // PA_CL_VPORT_YSCALE_8
0x00000000, // PA_CL_VPORT_YOFFSET_8
0x00000000, // PA_CL_VPORT_ZSCALE_8
0x00000000, // PA_CL_VPORT_ZOFFSET_8
0x00000000, // PA_CL_VPORT_XSCALE_9
0x00000000, // PA_CL_VPORT_XOFFSET_9
0x00000000, // PA_CL_VPORT_YSCALE_9
0x00000000, // PA_CL_VPORT_YOFFSET_9
0x00000000, // PA_CL_VPORT_ZSCALE_9
0x00000000, // PA_CL_VPORT_ZOFFSET_9
0x00000000, // PA_CL_VPORT_XSCALE_10
0x00000000, // PA_CL_VPORT_XOFFSET_10
0x00000000, // PA_CL_VPORT_YSCALE_10
0x00000000, // PA_CL_VPORT_YOFFSET_10
0x00000000, // PA_CL_VPORT_ZSCALE_10
0x00000000, // PA_CL_VPORT_ZOFFSET_10
0x00000000, // PA_CL_VPORT_XSCALE_11
0x00000000, // PA_CL_VPORT_XOFFSET_11
0x00000000, // PA_CL_VPORT_YSCALE_11
0x00000000, // PA_CL_VPORT_YOFFSET_11
0x00000000, // PA_CL_VPORT_ZSCALE_11
0x00000000, // PA_CL_VPORT_ZOFFSET_11
0x00000000, // PA_CL_VPORT_XSCALE_12
0x00000000, // PA_CL_VPORT_XOFFSET_12
0x00000000, // PA_CL_VPORT_YSCALE_12
0x00000000, // PA_CL_VPORT_YOFFSET_12
0x00000000, // PA_CL_VPORT_ZSCALE_12
0x00000000, // PA_CL_VPORT_ZOFFSET_12
0x00000000, // PA_CL_VPORT_XSCALE_13
0x00000000, // PA_CL_VPORT_XOFFSET_13
0x00000000, // PA_CL_VPORT_YSCALE_13
0x00000000, // PA_CL_VPORT_YOFFSET_13
0x00000000, // PA_CL_VPORT_ZSCALE_13
0x00000000, // PA_CL_VPORT_ZOFFSET_13
0x00000000, // PA_CL_VPORT_XSCALE_14
0x00000000, // PA_CL_VPORT_XOFFSET_14
0x00000000, // PA_CL_VPORT_YSCALE_14
0x00000000, // PA_CL_VPORT_YOFFSET_14
0x00000000, // PA_CL_VPORT_ZSCALE_14
0x00000000, // PA_CL_VPORT_ZOFFSET_14
0x00000000, // PA_CL_VPORT_XSCALE_15
0x00000000, // PA_CL_VPORT_XOFFSET_15
0x00000000, // PA_CL_VPORT_YSCALE_15
0x00000000, // PA_CL_VPORT_YOFFSET_15
0x00000000, // PA_CL_VPORT_ZSCALE_15
0x00000000, // PA_CL_VPORT_ZOFFSET_15
0x00000000, // PA_CL_UCP_0_X
0x00000000, // PA_CL_UCP_0_Y
0x00000000, // PA_CL_UCP_0_Z
0x00000000, // PA_CL_UCP_0_W
0x00000000, // PA_CL_UCP_1_X
0x00000000, // PA_CL_UCP_1_Y
0x00000000, // PA_CL_UCP_1_Z
0x00000000, // PA_CL_UCP_1_W
0x00000000, // PA_CL_UCP_2_X
0x00000000, // PA_CL_UCP_2_Y
0x00000000, // PA_CL_UCP_2_Z
0x00000000, // PA_CL_UCP_2_W
0x00000000, // PA_CL_UCP_3_X
0x00000000, // PA_CL_UCP_3_Y
0x00000000, // PA_CL_UCP_3_Z
0x00000000, // PA_CL_UCP_3_W
0x00000000, // PA_CL_UCP_4_X
0x00000000, // PA_CL_UCP_4_Y
0x00000000, // PA_CL_UCP_4_Z
0x00000000, // PA_CL_UCP_4_W
0x00000000, // PA_CL_UCP_5_X
0x00000000, // PA_CL_UCP_5_Y
0x00000000, // PA_CL_UCP_5_Z
0x00000000, // PA_CL_UCP_5_W
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // SPI_PS_INPUT_CNTL_0
0x00000000, // SPI_PS_INPUT_CNTL_1
0x00000000, // SPI_PS_INPUT_CNTL_2
0x00000000, // SPI_PS_INPUT_CNTL_3
0x00000000, // SPI_PS_INPUT_CNTL_4
0x00000000, // SPI_PS_INPUT_CNTL_5
0x00000000, // SPI_PS_INPUT_CNTL_6
0x00000000, // SPI_PS_INPUT_CNTL_7
0x00000000, // SPI_PS_INPUT_CNTL_8
0x00000000, // SPI_PS_INPUT_CNTL_9
0x00000000, // SPI_PS_INPUT_CNTL_10
0x00000000, // SPI_PS_INPUT_CNTL_11
0x00000000, // SPI_PS_INPUT_CNTL_12
0x00000000, // SPI_PS_INPUT_CNTL_13
0x00000000, // SPI_PS_INPUT_CNTL_14
0x00000000, // SPI_PS_INPUT_CNTL_15
0x00000000, // SPI_PS_INPUT_CNTL_16
0x00000000, // SPI_PS_INPUT_CNTL_17
0x00000000, // SPI_PS_INPUT_CNTL_18
0x00000000, // SPI_PS_INPUT_CNTL_19
0x00000000, // SPI_PS_INPUT_CNTL_20
0x00000000, // SPI_PS_INPUT_CNTL_21
0x00000000, // SPI_PS_INPUT_CNTL_22
0x00000000, // SPI_PS_INPUT_CNTL_23
0x00000000, // SPI_PS_INPUT_CNTL_24
0x00000000, // SPI_PS_INPUT_CNTL_25
0x00000000, // SPI_PS_INPUT_CNTL_26
0x00000000, // SPI_PS_INPUT_CNTL_27
0x00000000, // SPI_PS_INPUT_CNTL_28
0x00000000, // SPI_PS_INPUT_CNTL_29
0x00000000, // SPI_PS_INPUT_CNTL_30
0x00000000, // SPI_PS_INPUT_CNTL_31
0x00000000, // SPI_VS_OUT_CONFIG
0, // HOLE
0x00000000, // SPI_PS_INPUT_ENA
0x00000000, // SPI_PS_INPUT_ADDR
0x00000000, // SPI_INTERP_CONTROL_0
0x00000002, // SPI_PS_IN_CONTROL
0, // HOLE
0x00000000, // SPI_BARYC_CNTL
0, // HOLE
0x00000000, // SPI_TMPRING_SIZE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // SPI_SHADER_POS_FORMAT
0x00000000, // SPI_SHADER_Z_FORMAT
0x00000000, // SPI_SHADER_COL_FORMAT
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // CB_BLEND0_CONTROL
0x00000000, // CB_BLEND1_CONTROL
0x00000000, // CB_BLEND2_CONTROL
0x00000000, // CB_BLEND3_CONTROL
0x00000000, // CB_BLEND4_CONTROL
0x00000000, // CB_BLEND5_CONTROL
0x00000000, // CB_BLEND6_CONTROL
0x00000000, // CB_BLEND7_CONTROL
};
static const unsigned int vi_SECT_CONTEXT_def_3[] =
{
0x00000000, // PA_CL_POINT_X_RAD
0x00000000, // PA_CL_POINT_Y_RAD
0x00000000, // PA_CL_POINT_SIZE
0x00000000, // PA_CL_POINT_CULL_RAD
0x00000000, // VGT_DMA_BASE_HI
0x00000000, // VGT_DMA_BASE
};
static const unsigned int vi_SECT_CONTEXT_def_4[] =
{
0x00000000, // DB_DEPTH_CONTROL
0x00000000, // DB_EQAA
0x00000000, // CB_COLOR_CONTROL
0x00000000, // DB_SHADER_CONTROL
0x00090000, // PA_CL_CLIP_CNTL
0x00000004, // PA_SU_SC_MODE_CNTL
0x00000000, // PA_CL_VTE_CNTL
0x00000000, // PA_CL_VS_OUT_CNTL
0x00000000, // PA_CL_NANINF_CNTL
0x00000000, // PA_SU_LINE_STIPPLE_CNTL
0x00000000, // PA_SU_LINE_STIPPLE_SCALE
0x00000000, // PA_SU_PRIM_FILTER_CNTL
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // PA_SU_POINT_SIZE
0x00000000, // PA_SU_POINT_MINMAX
0x00000000, // PA_SU_LINE_CNTL
0x00000000, // PA_SC_LINE_STIPPLE
0x00000000, // VGT_OUTPUT_PATH_CNTL
0x00000000, // VGT_HOS_CNTL
0x00000000, // VGT_HOS_MAX_TESS_LEVEL
0x00000000, // VGT_HOS_MIN_TESS_LEVEL
0x00000000, // VGT_HOS_REUSE_DEPTH
0x00000000, // VGT_GROUP_PRIM_TYPE
0x00000000, // VGT_GROUP_FIRST_DECR
0x00000000, // VGT_GROUP_DECR
0x00000000, // VGT_GROUP_VECT_0_CNTL
0x00000000, // VGT_GROUP_VECT_1_CNTL
0x00000000, // VGT_GROUP_VECT_0_FMT_CNTL
0x00000000, // VGT_GROUP_VECT_1_FMT_CNTL
0x00000000, // VGT_GS_MODE
0x00000000, // VGT_GS_ONCHIP_CNTL
0x00000000, // PA_SC_MODE_CNTL_0
0x00000000, // PA_SC_MODE_CNTL_1
0x00000000, // VGT_ENHANCE
0x00000100, // VGT_GS_PER_ES
0x00000080, // VGT_ES_PER_GS
0x00000002, // VGT_GS_PER_VS
0x00000000, // VGT_GSVS_RING_OFFSET_1
0x00000000, // VGT_GSVS_RING_OFFSET_2
0x00000000, // VGT_GSVS_RING_OFFSET_3
0x00000000, // VGT_GS_OUT_PRIM_TYPE
0x00000000, // IA_ENHANCE
};
static const unsigned int vi_SECT_CONTEXT_def_5[] =
{
0x00000000, // WD_ENHANCE
0x00000000, // VGT_PRIMITIVEID_EN
};
static const unsigned int vi_SECT_CONTEXT_def_6[] =
{
0x00000000, // VGT_PRIMITIVEID_RESET
};
static const unsigned int vi_SECT_CONTEXT_def_7[] =
{
0x00000000, // VGT_MULTI_PRIM_IB_RESET_EN
0, // HOLE
0, // HOLE
0x00000000, // VGT_INSTANCE_STEP_RATE_0
0x00000000, // VGT_INSTANCE_STEP_RATE_1
0x000000ff, // IA_MULTI_VGT_PARAM
0x00000000, // VGT_ESGS_RING_ITEMSIZE
0x00000000, // VGT_GSVS_RING_ITEMSIZE
0x00000000, // VGT_REUSE_OFF
0x00000000, // VGT_VTX_CNT_EN
0x00000000, // DB_HTILE_SURFACE
0x00000000, // DB_SRESULTS_COMPARE_STATE0
0x00000000, // DB_SRESULTS_COMPARE_STATE1
0x00000000, // DB_PRELOAD_CONTROL
0, // HOLE
0x00000000, // VGT_STRMOUT_BUFFER_SIZE_0
0x00000000, // VGT_STRMOUT_VTX_STRIDE_0
0, // HOLE
0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_0
0x00000000, // VGT_STRMOUT_BUFFER_SIZE_1
0x00000000, // VGT_STRMOUT_VTX_STRIDE_1
0, // HOLE
0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_1
0x00000000, // VGT_STRMOUT_BUFFER_SIZE_2
0x00000000, // VGT_STRMOUT_VTX_STRIDE_2
0, // HOLE
0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_2
0x00000000, // VGT_STRMOUT_BUFFER_SIZE_3
0x00000000, // VGT_STRMOUT_VTX_STRIDE_3
0, // HOLE
0x00000000, // VGT_STRMOUT_BUFFER_OFFSET_3
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_OFFSET
0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_BUFFER_FILLED_SIZE
0x00000000, // VGT_STRMOUT_DRAW_OPAQUE_VERTEX_STRIDE
0, // HOLE
0x00000000, // VGT_GS_MAX_VERT_OUT
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // VGT_TESS_DISTRIBUTION
0x00000000, // VGT_SHADER_STAGES_EN
0x00000000, // VGT_LS_HS_CONFIG
0x00000000, // VGT_GS_VERT_ITEMSIZE
0x00000000, // VGT_GS_VERT_ITEMSIZE_1
0x00000000, // VGT_GS_VERT_ITEMSIZE_2
0x00000000, // VGT_GS_VERT_ITEMSIZE_3
0x00000000, // VGT_TF_PARAM
0x00000000, // DB_ALPHA_TO_MASK
0x00000000, // VGT_DISPATCH_DRAW_INDEX
0x00000000, // PA_SU_POLY_OFFSET_DB_FMT_CNTL
0x00000000, // PA_SU_POLY_OFFSET_CLAMP
0x00000000, // PA_SU_POLY_OFFSET_FRONT_SCALE
0x00000000, // PA_SU_POLY_OFFSET_FRONT_OFFSET
0x00000000, // PA_SU_POLY_OFFSET_BACK_SCALE
0x00000000, // PA_SU_POLY_OFFSET_BACK_OFFSET
0x00000000, // VGT_GS_INSTANCE_CNT
0x00000000, // VGT_STRMOUT_CONFIG
0x00000000, // VGT_STRMOUT_BUFFER_CONFIG
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x00000000, // PA_SC_CENTROID_PRIORITY_0
0x00000000, // PA_SC_CENTROID_PRIORITY_1
0x00001000, // PA_SC_LINE_CNTL
0x00000000, // PA_SC_AA_CONFIG
0x00000005, // PA_SU_VTX_CNTL
0x3f800000, // PA_CL_GB_VERT_CLIP_ADJ
0x3f800000, // PA_CL_GB_VERT_DISC_ADJ
0x3f800000, // PA_CL_GB_HORZ_CLIP_ADJ
0x3f800000, // PA_CL_GB_HORZ_DISC_ADJ
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_0
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_1
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_2
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y0_3
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_0
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_1
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_2
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y0_3
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_0
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_1
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_2
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X0Y1_3
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_0
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_1
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_2
0x00000000, // PA_SC_AA_SAMPLE_LOCS_PIXEL_X1Y1_3
0xffffffff, // PA_SC_AA_MASK_X0Y0_X1Y0
0xffffffff, // PA_SC_AA_MASK_X0Y1_X1Y1
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0, // HOLE
0x0000001e, // VGT_VERTEX_REUSE_BLOCK_CNTL
0x00000020, // VGT_OUT_DEALLOC_CNTL
0x00000000, // CB_COLOR0_BASE
0x00000000, // CB_COLOR0_PITCH
0x00000000, // CB_COLOR0_SLICE
0x00000000, // CB_COLOR0_VIEW
0x00000000, // CB_COLOR0_INFO
0x00000000, // CB_COLOR0_ATTRIB
0x00000000, // CB_COLOR0_DCC_CONTROL
0x00000000, // CB_COLOR0_CMASK
0x00000000, // CB_COLOR0_CMASK_SLICE
0x00000000, // CB_COLOR0_FMASK
0x00000000, // CB_COLOR0_FMASK_SLICE
0x00000000, // CB_COLOR0_CLEAR_WORD0
0x00000000, // CB_COLOR0_CLEAR_WORD1
0x00000000, // CB_COLOR0_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR1_BASE
0x00000000, // CB_COLOR1_PITCH
0x00000000, // CB_COLOR1_SLICE
0x00000000, // CB_COLOR1_VIEW
0x00000000, // CB_COLOR1_INFO
0x00000000, // CB_COLOR1_ATTRIB
0x00000000, // CB_COLOR1_DCC_CONTROL
0x00000000, // CB_COLOR1_CMASK
0x00000000, // CB_COLOR1_CMASK_SLICE
0x00000000, // CB_COLOR1_FMASK
0x00000000, // CB_COLOR1_FMASK_SLICE
0x00000000, // CB_COLOR1_CLEAR_WORD0
0x00000000, // CB_COLOR1_CLEAR_WORD1
0x00000000, // CB_COLOR1_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR2_BASE
0x00000000, // CB_COLOR2_PITCH
0x00000000, // CB_COLOR2_SLICE
0x00000000, // CB_COLOR2_VIEW
0x00000000, // CB_COLOR2_INFO
0x00000000, // CB_COLOR2_ATTRIB
0x00000000, // CB_COLOR2_DCC_CONTROL
0x00000000, // CB_COLOR2_CMASK
0x00000000, // CB_COLOR2_CMASK_SLICE
0x00000000, // CB_COLOR2_FMASK
0x00000000, // CB_COLOR2_FMASK_SLICE
0x00000000, // CB_COLOR2_CLEAR_WORD0
0x00000000, // CB_COLOR2_CLEAR_WORD1
0x00000000, // CB_COLOR2_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR3_BASE
0x00000000, // CB_COLOR3_PITCH
0x00000000, // CB_COLOR3_SLICE
0x00000000, // CB_COLOR3_VIEW
0x00000000, // CB_COLOR3_INFO
0x00000000, // CB_COLOR3_ATTRIB
0x00000000, // CB_COLOR3_DCC_CONTROL
0x00000000, // CB_COLOR3_CMASK
0x00000000, // CB_COLOR3_CMASK_SLICE
0x00000000, // CB_COLOR3_FMASK
0x00000000, // CB_COLOR3_FMASK_SLICE
0x00000000, // CB_COLOR3_CLEAR_WORD0
0x00000000, // CB_COLOR3_CLEAR_WORD1
0x00000000, // CB_COLOR3_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR4_BASE
0x00000000, // CB_COLOR4_PITCH
0x00000000, // CB_COLOR4_SLICE
0x00000000, // CB_COLOR4_VIEW
0x00000000, // CB_COLOR4_INFO
0x00000000, // CB_COLOR4_ATTRIB
0x00000000, // CB_COLOR4_DCC_CONTROL
0x00000000, // CB_COLOR4_CMASK
0x00000000, // CB_COLOR4_CMASK_SLICE
0x00000000, // CB_COLOR4_FMASK
0x00000000, // CB_COLOR4_FMASK_SLICE
0x00000000, // CB_COLOR4_CLEAR_WORD0
0x00000000, // CB_COLOR4_CLEAR_WORD1
0x00000000, // CB_COLOR4_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR5_BASE
0x00000000, // CB_COLOR5_PITCH
0x00000000, // CB_COLOR5_SLICE
0x00000000, // CB_COLOR5_VIEW
0x00000000, // CB_COLOR5_INFO
0x00000000, // CB_COLOR5_ATTRIB
0x00000000, // CB_COLOR5_DCC_CONTROL
0x00000000, // CB_COLOR5_CMASK
0x00000000, // CB_COLOR5_CMASK_SLICE
0x00000000, // CB_COLOR5_FMASK
0x00000000, // CB_COLOR5_FMASK_SLICE
0x00000000, // CB_COLOR5_CLEAR_WORD0
0x00000000, // CB_COLOR5_CLEAR_WORD1
0x00000000, // CB_COLOR5_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR6_BASE
0x00000000, // CB_COLOR6_PITCH
0x00000000, // CB_COLOR6_SLICE
0x00000000, // CB_COLOR6_VIEW
0x00000000, // CB_COLOR6_INFO
0x00000000, // CB_COLOR6_ATTRIB
0x00000000, // CB_COLOR6_DCC_CONTROL
0x00000000, // CB_COLOR6_CMASK
0x00000000, // CB_COLOR6_CMASK_SLICE
0x00000000, // CB_COLOR6_FMASK
0x00000000, // CB_COLOR6_FMASK_SLICE
0x00000000, // CB_COLOR6_CLEAR_WORD0
0x00000000, // CB_COLOR6_CLEAR_WORD1
0x00000000, // CB_COLOR6_DCC_BASE
0, // HOLE
0x00000000, // CB_COLOR7_BASE
0x00000000, // CB_COLOR7_PITCH
0x00000000, // CB_COLOR7_SLICE
0x00000000, // CB_COLOR7_VIEW
0x00000000, // CB_COLOR7_INFO
0x00000000, // CB_COLOR7_ATTRIB
0x00000000, // CB_COLOR7_DCC_CONTROL
0x00000000, // CB_COLOR7_CMASK
0x00000000, // CB_COLOR7_CMASK_SLICE
0x00000000, // CB_COLOR7_FMASK
0x00000000, // CB_COLOR7_FMASK_SLICE
0x00000000, // CB_COLOR7_CLEAR_WORD0
0x00000000, // CB_COLOR7_CLEAR_WORD1
};
static const struct cs_extent_def vi_SECT_CONTEXT_defs[] =
{
{vi_SECT_CONTEXT_def_1, 0x0000a000, 212 },
{vi_SECT_CONTEXT_def_2, 0x0000a0d6, 274 },
{vi_SECT_CONTEXT_def_3, 0x0000a1f5, 6 },
{vi_SECT_CONTEXT_def_4, 0x0000a200, 157 },
{vi_SECT_CONTEXT_def_5, 0x0000a2a0, 2 },
{vi_SECT_CONTEXT_def_6, 0x0000a2a3, 1 },
{vi_SECT_CONTEXT_def_7, 0x0000a2a5, 233 },
{ 0, 0, 0 }
};
static const struct cs_section_def vi_cs_data[] = {
{ vi_SECT_CONTEXT_defs, SECT_CONTEXT },
{ 0, SECT_NONE }
};
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include <linux/seq_file.h>
#include "drmP.h"
#include "amdgpu.h"
#include "amdgpu_pm.h"
#include "amdgpu_atombios.h"
#include "vid.h"
#include "vi_dpm.h"
#include "amdgpu_dpm.h"
#include "cz_dpm.h"
#include "cz_ppsmc.h"
#include "atom.h"
#include "smu/smu_8_0_d.h"
#include "smu/smu_8_0_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_sh_mask.h"
#include "gmc/gmc_8_1_d.h"
#include "bif/bif_5_1_d.h"
#include "gfx_v8_0.h"
static struct cz_ps *cz_get_ps(struct amdgpu_ps *rps)
{
struct cz_ps *ps = rps->ps_priv;
return ps;
}
static struct cz_power_info *cz_get_pi(struct amdgpu_device *adev)
{
struct cz_power_info *pi = adev->pm.dpm.priv;
return pi;
}
static uint16_t cz_convert_8bit_index_to_voltage(struct amdgpu_device *adev,
uint16_t voltage)
{
uint16_t tmp = 6200 - voltage * 25;
return tmp;
}
static void cz_construct_max_power_limits_table(struct amdgpu_device *adev,
struct amdgpu_clock_and_voltage_limits *table)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_voltage_dependency_table *dep_table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
if (dep_table->count > 0) {
table->sclk = dep_table->entries[dep_table->count - 1].clk;
table->vddc = cz_convert_8bit_index_to_voltage(adev,
dep_table->entries[dep_table->count - 1].v);
}
table->mclk = pi->sys_info.nbp_memory_clock[0];
}
union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_8 info_8;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_9 info_9;
};
static int cz_parse_sys_info_table(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_mode_info *mode_info = &adev->mode_info;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
union igp_info *igp_info;
u8 frev, crev;
u16 data_offset;
int i = 0;
if (amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset)) {
igp_info = (union igp_info *)(mode_info->atom_context->bios +
data_offset);
if (crev != 9) {
DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
return -EINVAL;
}
pi->sys_info.bootup_sclk =
le32_to_cpu(igp_info->info_9.ulBootUpEngineClock);
pi->sys_info.bootup_uma_clk =
le32_to_cpu(igp_info->info_9.ulBootUpUMAClock);
pi->sys_info.dentist_vco_freq =
le32_to_cpu(igp_info->info_9.ulDentistVCOFreq);
pi->sys_info.bootup_nb_voltage_index =
le16_to_cpu(igp_info->info_9.usBootUpNBVoltage);
if (igp_info->info_9.ucHtcTmpLmt == 0)
pi->sys_info.htc_tmp_lmt = 203;
else
pi->sys_info.htc_tmp_lmt = igp_info->info_9.ucHtcTmpLmt;
if (igp_info->info_9.ucHtcHystLmt == 0)
pi->sys_info.htc_hyst_lmt = 5;
else
pi->sys_info.htc_hyst_lmt = igp_info->info_9.ucHtcHystLmt;
if (pi->sys_info.htc_tmp_lmt <= pi->sys_info.htc_hyst_lmt) {
DRM_ERROR("The htcTmpLmt should be larger than htcHystLmt.\n");
return -EINVAL;
}
if (le32_to_cpu(igp_info->info_9.ulSystemConfig) & (1 << 3) &&
pi->enable_nb_ps_policy)
pi->sys_info.nb_dpm_enable = true;
else
pi->sys_info.nb_dpm_enable = false;
for (i = 0; i < CZ_NUM_NBPSTATES; i++) {
if (i < CZ_NUM_NBPMEMORY_CLOCK)
pi->sys_info.nbp_memory_clock[i] =
le32_to_cpu(igp_info->info_9.ulNbpStateMemclkFreq[i]);
pi->sys_info.nbp_n_clock[i] =
le32_to_cpu(igp_info->info_9.ulNbpStateNClkFreq[i]);
}
for (i = 0; i < CZ_MAX_DISPLAY_CLOCK_LEVEL; i++)
pi->sys_info.display_clock[i] =
le32_to_cpu(igp_info->info_9.sDispClkVoltageMapping[i].ulMaximumSupportedCLK);
for (i = 0; i < CZ_NUM_NBPSTATES; i++)
pi->sys_info.nbp_voltage_index[i] =
le32_to_cpu(igp_info->info_9.usNBPStateVoltage[i]);
if (le32_to_cpu(igp_info->info_9.ulGPUCapInfo) &
SYS_INFO_GPUCAPS__ENABEL_DFS_BYPASS)
pi->caps_enable_dfs_bypass = true;
pi->sys_info.uma_channel_number =
igp_info->info_9.ucUMAChannelNumber;
cz_construct_max_power_limits_table(adev,
&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac);
}
return 0;
}
static void cz_patch_voltage_values(struct amdgpu_device *adev)
{
int i;
struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table =
&adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
struct amdgpu_vce_clock_voltage_dependency_table *vce_table =
&adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
struct amdgpu_clock_voltage_dependency_table *acp_table =
&adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
if (uvd_table->count) {
for (i = 0; i < uvd_table->count; i++)
uvd_table->entries[i].v =
cz_convert_8bit_index_to_voltage(adev,
uvd_table->entries[i].v);
}
if (vce_table->count) {
for (i = 0; i < vce_table->count; i++)
vce_table->entries[i].v =
cz_convert_8bit_index_to_voltage(adev,
vce_table->entries[i].v);
}
if (acp_table->count) {
for (i = 0; i < acp_table->count; i++)
acp_table->entries[i].v =
cz_convert_8bit_index_to_voltage(adev,
acp_table->entries[i].v);
}
}
static void cz_construct_boot_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
pi->boot_pl.sclk = pi->sys_info.bootup_sclk;
pi->boot_pl.vddc_index = pi->sys_info.bootup_nb_voltage_index;
pi->boot_pl.ds_divider_index = 0;
pi->boot_pl.ss_divider_index = 0;
pi->boot_pl.allow_gnb_slow = 1;
pi->boot_pl.force_nbp_state = 0;
pi->boot_pl.display_wm = 0;
pi->boot_pl.vce_wm = 0;
}
static void cz_patch_boot_state(struct amdgpu_device *adev,
struct cz_ps *ps)
{
struct cz_power_info *pi = cz_get_pi(adev);
ps->num_levels = 1;
ps->levels[0] = pi->boot_pl;
}
union pplib_clock_info {
struct _ATOM_PPLIB_EVERGREEN_CLOCK_INFO evergreen;
struct _ATOM_PPLIB_SUMO_CLOCK_INFO sumo;
struct _ATOM_PPLIB_CZ_CLOCK_INFO carrizo;
};
static void cz_parse_pplib_clock_info(struct amdgpu_device *adev,
struct amdgpu_ps *rps, int index,
union pplib_clock_info *clock_info)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct cz_ps *ps = cz_get_ps(rps);
struct cz_pl *pl = &ps->levels[index];
struct amdgpu_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
pl->sclk = table->entries[clock_info->carrizo.index].clk;
pl->vddc_index = table->entries[clock_info->carrizo.index].v;
ps->num_levels = index + 1;
if (pi->caps_sclk_ds) {
pl->ds_divider_index = 5;
pl->ss_divider_index = 5;
}
}
static void cz_parse_pplib_non_clock_info(struct amdgpu_device *adev,
struct amdgpu_ps *rps,
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info,
u8 table_rev)
{
struct cz_ps *ps = cz_get_ps(rps);
rps->caps = le32_to_cpu(non_clock_info->ulCapsAndSettings);
rps->class = le16_to_cpu(non_clock_info->usClassification);
rps->class2 = le16_to_cpu(non_clock_info->usClassification2);
if (ATOM_PPLIB_NONCLOCKINFO_VER1 < table_rev) {
rps->vclk = le32_to_cpu(non_clock_info->ulVCLK);
rps->dclk = le32_to_cpu(non_clock_info->ulDCLK);
} else {
rps->vclk = 0;
rps->dclk = 0;
}
if (rps->class & ATOM_PPLIB_CLASSIFICATION_BOOT) {
adev->pm.dpm.boot_ps = rps;
cz_patch_boot_state(adev, ps);
}
if (rps->class & ATOM_PPLIB_CLASSIFICATION_UVDSTATE)
adev->pm.dpm.uvd_ps = rps;
}
union power_info {
struct _ATOM_PPLIB_POWERPLAYTABLE pplib;
struct _ATOM_PPLIB_POWERPLAYTABLE2 pplib2;
struct _ATOM_PPLIB_POWERPLAYTABLE3 pplib3;
struct _ATOM_PPLIB_POWERPLAYTABLE4 pplib4;
struct _ATOM_PPLIB_POWERPLAYTABLE5 pplib5;
};
union pplib_power_state {
struct _ATOM_PPLIB_STATE v1;
struct _ATOM_PPLIB_STATE_V2 v2;
};
static int cz_parse_power_table(struct amdgpu_device *adev)
{
struct amdgpu_mode_info *mode_info = &adev->mode_info;
struct _ATOM_PPLIB_NONCLOCK_INFO *non_clock_info;
union pplib_power_state *power_state;
int i, j, k, non_clock_array_index, clock_array_index;
union pplib_clock_info *clock_info;
struct _StateArray *state_array;
struct _ClockInfoArray *clock_info_array;
struct _NonClockInfoArray *non_clock_info_array;
union power_info *power_info;
int index = GetIndexIntoMasterTable(DATA, PowerPlayInfo);
u16 data_offset;
u8 frev, crev;
u8 *power_state_offset;
struct cz_ps *ps;
if (!amdgpu_atom_parse_data_header(mode_info->atom_context, index, NULL,
&frev, &crev, &data_offset))
return -EINVAL;
power_info = (union power_info *)(mode_info->atom_context->bios + data_offset);
state_array = (struct _StateArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usStateArrayOffset));
clock_info_array = (struct _ClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usClockInfoArrayOffset));
non_clock_info_array = (struct _NonClockInfoArray *)
(mode_info->atom_context->bios + data_offset +
le16_to_cpu(power_info->pplib.usNonClockInfoArrayOffset));
adev->pm.dpm.ps = kzalloc(sizeof(struct amdgpu_ps) *
state_array->ucNumEntries, GFP_KERNEL);
if (!adev->pm.dpm.ps)
return -ENOMEM;
power_state_offset = (u8 *)state_array->states;
adev->pm.dpm.platform_caps =
le32_to_cpu(power_info->pplib.ulPlatformCaps);
adev->pm.dpm.backbias_response_time =
le16_to_cpu(power_info->pplib.usBackbiasTime);
adev->pm.dpm.voltage_response_time =
le16_to_cpu(power_info->pplib.usVoltageTime);
for (i = 0; i < state_array->ucNumEntries; i++) {
power_state = (union pplib_power_state *)power_state_offset;
non_clock_array_index = power_state->v2.nonClockInfoIndex;
non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
&non_clock_info_array->nonClockInfo[non_clock_array_index];
ps = kzalloc(sizeof(struct cz_ps), GFP_KERNEL);
if (ps == NULL) {
kfree(adev->pm.dpm.ps);
return -ENOMEM;
}
adev->pm.dpm.ps[i].ps_priv = ps;
k = 0;
for (j = 0; j < power_state->v2.ucNumDPMLevels; j++) {
clock_array_index = power_state->v2.clockInfoIndex[j];
if (clock_array_index >= clock_info_array->ucNumEntries)
continue;
if (k >= CZ_MAX_HARDWARE_POWERLEVELS)
break;
clock_info = (union pplib_clock_info *)
&clock_info_array->clockInfo[clock_array_index *
clock_info_array->ucEntrySize];
cz_parse_pplib_clock_info(adev, &adev->pm.dpm.ps[i],
k, clock_info);
k++;
}
cz_parse_pplib_non_clock_info(adev, &adev->pm.dpm.ps[i],
non_clock_info,
non_clock_info_array->ucEntrySize);
power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
}
adev->pm.dpm.num_ps = state_array->ucNumEntries;
return 0;
}
static int cz_process_firmware_header(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
u32 tmp;
int ret;
ret = cz_read_smc_sram_dword(adev, SMU8_FIRMWARE_HEADER_LOCATION +
offsetof(struct SMU8_Firmware_Header,
DpmTable),
&tmp, pi->sram_end);
if (ret == 0)
pi->dpm_table_start = tmp;
return ret;
}
static int cz_dpm_init(struct amdgpu_device *adev)
{
struct cz_power_info *pi;
int ret, i;
pi = kzalloc(sizeof(struct cz_power_info), GFP_KERNEL);
if (NULL == pi)
return -ENOMEM;
adev->pm.dpm.priv = pi;
ret = amdgpu_get_platform_caps(adev);
if (ret)
return ret;
ret = amdgpu_parse_extended_power_table(adev);
if (ret)
return ret;
pi->sram_end = SMC_RAM_END;
/* set up DPM defaults */
for (i = 0; i < CZ_MAX_HARDWARE_POWERLEVELS; i++)
pi->active_target[i] = CZ_AT_DFLT;
pi->mgcg_cgtt_local0 = 0x0;
pi->mgcg_cgtt_local1 = 0x0;
pi->clock_slow_down_step = 25000;
pi->skip_clock_slow_down = 1;
pi->enable_nb_ps_policy = 1;
pi->caps_power_containment = true;
pi->caps_cac = true;
pi->didt_enabled = false;
if (pi->didt_enabled) {
pi->caps_sq_ramping = true;
pi->caps_db_ramping = true;
pi->caps_td_ramping = true;
pi->caps_tcp_ramping = true;
}
pi->caps_sclk_ds = true;
pi->voting_clients = 0x00c00033;
pi->auto_thermal_throttling_enabled = true;
pi->bapm_enabled = false;
pi->disable_nb_ps3_in_battery = false;
pi->voltage_drop_threshold = 0;
pi->caps_sclk_throttle_low_notification = false;
pi->gfx_pg_threshold = 500;
pi->caps_fps = true;
/* uvd */
pi->caps_uvd_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_UVD) ? true : false;
pi->caps_uvd_dpm = true;
/* vce */
pi->caps_vce_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_VCE) ? true : false;
pi->caps_vce_dpm = true;
/* acp */
pi->caps_acp_pg = (adev->pg_flags & AMDGPU_PG_SUPPORT_ACP) ? true : false;
pi->caps_acp_dpm = true;
pi->caps_stable_power_state = false;
pi->nb_dpm_enabled_by_driver = true;
pi->nb_dpm_enabled = false;
pi->caps_voltage_island = false;
/* flags which indicate need to upload pptable */
pi->need_pptable_upload = true;
ret = cz_parse_sys_info_table(adev);
if (ret)
return ret;
cz_patch_voltage_values(adev);
cz_construct_boot_state(adev);
ret = cz_parse_power_table(adev);
if (ret)
return ret;
ret = cz_process_firmware_header(adev);
if (ret)
return ret;
pi->dpm_enabled = true;
return 0;
}
static void cz_dpm_fini(struct amdgpu_device *adev)
{
int i;
for (i = 0; i < adev->pm.dpm.num_ps; i++)
kfree(adev->pm.dpm.ps[i].ps_priv);
kfree(adev->pm.dpm.ps);
kfree(adev->pm.dpm.priv);
amdgpu_free_extended_power_table(adev);
}
static void
cz_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev,
struct seq_file *m)
{
struct amdgpu_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
u32 current_index =
(RREG32_SMC(ixTARGET_AND_CURRENT_PROFILE_INDEX) &
TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX_MASK) >>
TARGET_AND_CURRENT_PROFILE_INDEX__CURR_SCLK_INDEX__SHIFT;
u32 sclk, tmp;
u16 vddc;
if (current_index >= NUM_SCLK_LEVELS) {
seq_printf(m, "invalid dpm profile %d\n", current_index);
} else {
sclk = table->entries[current_index].clk;
tmp = (RREG32_SMC(ixSMU_VOLTAGE_STATUS) &
SMU_VOLTAGE_STATUS__SMU_VOLTAGE_CURRENT_LEVEL_MASK) >>
SMU_VOLTAGE_STATUS__SMU_VOLTAGE_CURRENT_LEVEL__SHIFT;
vddc = cz_convert_8bit_index_to_voltage(adev, (u16)tmp);
seq_printf(m, "power level %d sclk: %u vddc: %u\n",
current_index, sclk, vddc);
}
}
static void cz_dpm_print_power_state(struct amdgpu_device *adev,
struct amdgpu_ps *rps)
{
int i;
struct cz_ps *ps = cz_get_ps(rps);
amdgpu_dpm_print_class_info(rps->class, rps->class2);
amdgpu_dpm_print_cap_info(rps->caps);
DRM_INFO("\tuvd vclk: %d dclk: %d\n", rps->vclk, rps->dclk);
for (i = 0; i < ps->num_levels; i++) {
struct cz_pl *pl = &ps->levels[i];
DRM_INFO("\t\tpower level %d sclk: %u vddc: %u\n",
i, pl->sclk,
cz_convert_8bit_index_to_voltage(adev, pl->vddc_index));
}
amdgpu_dpm_print_ps_status(adev, rps);
}
static void cz_dpm_set_funcs(struct amdgpu_device *adev);
static int cz_dpm_early_init(struct amdgpu_device *adev)
{
cz_dpm_set_funcs(adev);
return 0;
}
static int cz_dpm_sw_init(struct amdgpu_device *adev)
{
int ret = 0;
/* fix me to add thermal support TODO */
/* default to balanced state */
adev->pm.dpm.state = POWER_STATE_TYPE_BALANCED;
adev->pm.dpm.user_state = POWER_STATE_TYPE_BALANCED;
adev->pm.dpm.forced_level = AMDGPU_DPM_FORCED_LEVEL_AUTO;
adev->pm.default_sclk = adev->clock.default_sclk;
adev->pm.default_mclk = adev->clock.default_mclk;
adev->pm.current_sclk = adev->clock.default_sclk;
adev->pm.current_mclk = adev->clock.default_mclk;
adev->pm.int_thermal_type = THERMAL_TYPE_NONE;
if (amdgpu_dpm == 0)
return 0;
mutex_lock(&adev->pm.mutex);
ret = cz_dpm_init(adev);
if (ret)
goto dpm_init_failed;
adev->pm.dpm.current_ps = adev->pm.dpm.requested_ps = adev->pm.dpm.boot_ps;
if (amdgpu_dpm == 1)
amdgpu_pm_print_power_states(adev);
ret = amdgpu_pm_sysfs_init(adev);
if (ret)
goto dpm_init_failed;
mutex_unlock(&adev->pm.mutex);
DRM_INFO("amdgpu: dpm initialized\n");
return 0;
dpm_init_failed:
cz_dpm_fini(adev);
mutex_unlock(&adev->pm.mutex);
DRM_ERROR("amdgpu: dpm initialization failed\n");
return ret;
}
static int cz_dpm_sw_fini(struct amdgpu_device *adev)
{
mutex_lock(&adev->pm.mutex);
amdgpu_pm_sysfs_fini(adev);
cz_dpm_fini(adev);
mutex_unlock(&adev->pm.mutex);
return 0;
}
static void cz_reset_ap_mask(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
pi->active_process_mask = 0;
}
static int cz_dpm_download_pptable_from_smu(struct amdgpu_device *adev,
void **table)
{
int ret = 0;
ret = cz_smu_download_pptable(adev, table);
return ret;
}
static int cz_dpm_upload_pptable_to_smu(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct SMU8_Fusion_ClkTable *clock_table;
struct atom_clock_dividers dividers;
void *table = NULL;
uint8_t i = 0;
int ret = 0;
struct amdgpu_clock_voltage_dependency_table *vddc_table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
struct amdgpu_clock_voltage_dependency_table *vddgfx_table =
&adev->pm.dpm.dyn_state.vddgfx_dependency_on_sclk;
struct amdgpu_uvd_clock_voltage_dependency_table *uvd_table =
&adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
struct amdgpu_vce_clock_voltage_dependency_table *vce_table =
&adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
struct amdgpu_clock_voltage_dependency_table *acp_table =
&adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
if (!pi->need_pptable_upload)
return 0;
ret = cz_dpm_download_pptable_from_smu(adev, &table);
if (ret) {
DRM_ERROR("amdgpu: Failed to get power play table from SMU!\n");
return -EINVAL;
}
clock_table = (struct SMU8_Fusion_ClkTable *)table;
/* patch clock table */
if (vddc_table->count > CZ_MAX_HARDWARE_POWERLEVELS ||
vddgfx_table->count > CZ_MAX_HARDWARE_POWERLEVELS ||
uvd_table->count > CZ_MAX_HARDWARE_POWERLEVELS ||
vce_table->count > CZ_MAX_HARDWARE_POWERLEVELS ||
acp_table->count > CZ_MAX_HARDWARE_POWERLEVELS) {
DRM_ERROR("amdgpu: Invalid Clock Voltage Dependency Table!\n");
return -EINVAL;
}
for (i = 0; i < CZ_MAX_HARDWARE_POWERLEVELS; i++) {
/* vddc sclk */
clock_table->SclkBreakdownTable.ClkLevel[i].GnbVid =
(i < vddc_table->count) ? (uint8_t)vddc_table->entries[i].v : 0;
clock_table->SclkBreakdownTable.ClkLevel[i].Frequency =
(i < vddc_table->count) ? vddc_table->entries[i].clk : 0;
ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock_table->SclkBreakdownTable.ClkLevel[i].Frequency,
false, &dividers);
if (ret)
return ret;
clock_table->SclkBreakdownTable.ClkLevel[i].DfsDid =
(uint8_t)dividers.post_divider;
/* vddgfx sclk */
clock_table->SclkBreakdownTable.ClkLevel[i].GfxVid =
(i < vddgfx_table->count) ? (uint8_t)vddgfx_table->entries[i].v : 0;
/* acp breakdown */
clock_table->AclkBreakdownTable.ClkLevel[i].GfxVid =
(i < acp_table->count) ? (uint8_t)acp_table->entries[i].v : 0;
clock_table->AclkBreakdownTable.ClkLevel[i].Frequency =
(i < acp_table->count) ? acp_table->entries[i].clk : 0;
ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock_table->SclkBreakdownTable.ClkLevel[i].Frequency,
false, &dividers);
if (ret)
return ret;
clock_table->AclkBreakdownTable.ClkLevel[i].DfsDid =
(uint8_t)dividers.post_divider;
/* uvd breakdown */
clock_table->VclkBreakdownTable.ClkLevel[i].GfxVid =
(i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0;
clock_table->VclkBreakdownTable.ClkLevel[i].Frequency =
(i < uvd_table->count) ? uvd_table->entries[i].vclk : 0;
ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock_table->VclkBreakdownTable.ClkLevel[i].Frequency,
false, &dividers);
if (ret)
return ret;
clock_table->VclkBreakdownTable.ClkLevel[i].DfsDid =
(uint8_t)dividers.post_divider;
clock_table->DclkBreakdownTable.ClkLevel[i].GfxVid =
(i < uvd_table->count) ? (uint8_t)uvd_table->entries[i].v : 0;
clock_table->DclkBreakdownTable.ClkLevel[i].Frequency =
(i < uvd_table->count) ? uvd_table->entries[i].dclk : 0;
ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock_table->DclkBreakdownTable.ClkLevel[i].Frequency,
false, &dividers);
if (ret)
return ret;
clock_table->DclkBreakdownTable.ClkLevel[i].DfsDid =
(uint8_t)dividers.post_divider;
/* vce breakdown */
clock_table->EclkBreakdownTable.ClkLevel[i].GfxVid =
(i < vce_table->count) ? (uint8_t)vce_table->entries[i].v : 0;
clock_table->EclkBreakdownTable.ClkLevel[i].Frequency =
(i < vce_table->count) ? vce_table->entries[i].ecclk : 0;
ret = amdgpu_atombios_get_clock_dividers(adev, COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock_table->EclkBreakdownTable.ClkLevel[i].Frequency,
false, &dividers);
if (ret)
return ret;
clock_table->EclkBreakdownTable.ClkLevel[i].DfsDid =
(uint8_t)dividers.post_divider;
}
/* its time to upload to SMU */
ret = cz_smu_upload_pptable(adev);
if (ret) {
DRM_ERROR("amdgpu: Failed to put power play table to SMU!\n");
return ret;
}
return 0;
}
static void cz_init_sclk_limit(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
uint32_t clock = 0, level;
if (!table || !table->count) {
DRM_ERROR("Invalid Voltage Dependency table.\n");
return;
}
pi->sclk_dpm.soft_min_clk = 0;
pi->sclk_dpm.hard_min_clk = 0;
cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxSclkLevel);
level = cz_get_argument(adev);
if (level < table->count)
clock = table->entries[level].clk;
else {
DRM_ERROR("Invalid SLCK Voltage Dependency table entry.\n");
clock = table->entries[table->count - 1].clk;
}
pi->sclk_dpm.soft_max_clk = clock;
pi->sclk_dpm.hard_max_clk = clock;
}
static void cz_init_uvd_limit(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_uvd_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.uvd_clock_voltage_dependency_table;
uint32_t clock = 0, level;
if (!table || !table->count) {
DRM_ERROR("Invalid Voltage Dependency table.\n");
return;
}
pi->uvd_dpm.soft_min_clk = 0;
pi->uvd_dpm.hard_min_clk = 0;
cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxUvdLevel);
level = cz_get_argument(adev);
if (level < table->count)
clock = table->entries[level].vclk;
else {
DRM_ERROR("Invalid UVD Voltage Dependency table entry.\n");
clock = table->entries[table->count - 1].vclk;
}
pi->uvd_dpm.soft_max_clk = clock;
pi->uvd_dpm.hard_max_clk = clock;
}
static void cz_init_vce_limit(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_vce_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.vce_clock_voltage_dependency_table;
uint32_t clock = 0, level;
if (!table || !table->count) {
DRM_ERROR("Invalid Voltage Dependency table.\n");
return;
}
pi->vce_dpm.soft_min_clk = 0;
pi->vce_dpm.hard_min_clk = 0;
cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxEclkLevel);
level = cz_get_argument(adev);
if (level < table->count)
clock = table->entries[level].evclk;
else {
/* future BIOS would fix this error */
DRM_ERROR("Invalid VCE Voltage Dependency table entry.\n");
clock = table->entries[table->count - 1].evclk;
}
pi->vce_dpm.soft_max_clk = clock;
pi->vce_dpm.hard_max_clk = clock;
}
static void cz_init_acp_limit(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.acp_clock_voltage_dependency_table;
uint32_t clock = 0, level;
if (!table || !table->count) {
DRM_ERROR("Invalid Voltage Dependency table.\n");
return;
}
pi->acp_dpm.soft_min_clk = 0;
pi->acp_dpm.hard_min_clk = 0;
cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxAclkLevel);
level = cz_get_argument(adev);
if (level < table->count)
clock = table->entries[level].clk;
else {
DRM_ERROR("Invalid ACP Voltage Dependency table entry.\n");
clock = table->entries[table->count - 1].clk;
}
pi->acp_dpm.soft_max_clk = clock;
pi->acp_dpm.hard_max_clk = clock;
}
static void cz_init_pg_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
pi->uvd_power_gated = false;
pi->vce_power_gated = false;
pi->acp_power_gated = false;
}
static void cz_init_sclk_threshold(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
pi->low_sclk_interrupt_threshold = 0;
}
static void cz_dpm_setup_asic(struct amdgpu_device *adev)
{
cz_reset_ap_mask(adev);
cz_dpm_upload_pptable_to_smu(adev);
cz_init_sclk_limit(adev);
cz_init_uvd_limit(adev);
cz_init_vce_limit(adev);
cz_init_acp_limit(adev);
cz_init_pg_state(adev);
cz_init_sclk_threshold(adev);
}
static bool cz_check_smu_feature(struct amdgpu_device *adev,
uint32_t feature)
{
uint32_t smu_feature = 0;
int ret;
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_GetFeatureStatus, 0);
if (ret) {
DRM_ERROR("Failed to get SMU features from SMC.\n");
return false;
} else {
smu_feature = cz_get_argument(adev);
if (feature & smu_feature)
return true;
}
return false;
}
static bool cz_check_for_dpm_enabled(struct amdgpu_device *adev)
{
if (cz_check_smu_feature(adev,
SMU_EnabledFeatureScoreboard_SclkDpmOn))
return true;
return false;
}
static void cz_program_voting_clients(struct amdgpu_device *adev)
{
WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, PPCZ_VOTINGRIGHTSCLIENTS_DFLT0);
}
static void cz_clear_voting_clients(struct amdgpu_device *adev)
{
WREG32_SMC(ixCG_FREQ_TRAN_VOTING_0, 0);
}
static int cz_start_dpm(struct amdgpu_device *adev)
{
int ret = 0;
if (amdgpu_dpm) {
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_EnableAllSmuFeatures, SCLK_DPM_MASK);
if (ret) {
DRM_ERROR("SMU feature: SCLK_DPM enable failed\n");
return -EINVAL;
}
}
return 0;
}
static int cz_stop_dpm(struct amdgpu_device *adev)
{
int ret = 0;
if (amdgpu_dpm && adev->pm.dpm_enabled) {
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_DisableAllSmuFeatures, SCLK_DPM_MASK);
if (ret) {
DRM_ERROR("SMU feature: SCLK_DPM disable failed\n");
return -EINVAL;
}
}
return 0;
}
static uint32_t cz_get_sclk_level(struct amdgpu_device *adev,
uint32_t clock, uint16_t msg)
{
int i = 0;
struct amdgpu_clock_voltage_dependency_table *table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
switch (msg) {
case PPSMC_MSG_SetSclkSoftMin:
case PPSMC_MSG_SetSclkHardMin:
for (i = 0; i < table->count; i++)
if (clock <= table->entries[i].clk)
break;
if (i == table->count)
i = table->count - 1;
break;
case PPSMC_MSG_SetSclkSoftMax:
case PPSMC_MSG_SetSclkHardMax:
for (i = table->count - 1; i >= 0; i--)
if (clock >= table->entries[i].clk)
break;
if (i < 0)
i = 0;
break;
default:
break;
}
return i;
}
static int cz_program_bootup_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
uint32_t soft_min_clk = 0;
uint32_t soft_max_clk = 0;
int ret = 0;
pi->sclk_dpm.soft_min_clk = pi->sys_info.bootup_sclk;
pi->sclk_dpm.soft_max_clk = pi->sys_info.bootup_sclk;
soft_min_clk = cz_get_sclk_level(adev,
pi->sclk_dpm.soft_min_clk,
PPSMC_MSG_SetSclkSoftMin);
soft_max_clk = cz_get_sclk_level(adev,
pi->sclk_dpm.soft_max_clk,
PPSMC_MSG_SetSclkSoftMax);
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMin, soft_min_clk);
if (ret)
return -EINVAL;
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMax, soft_max_clk);
if (ret)
return -EINVAL;
return 0;
}
/* TODO */
static int cz_disable_cgpg(struct amdgpu_device *adev)
{
return 0;
}
/* TODO */
static int cz_enable_cgpg(struct amdgpu_device *adev)
{
return 0;
}
/* TODO */
static int cz_program_pt_config_registers(struct amdgpu_device *adev)
{
return 0;
}
static void cz_do_enable_didt(struct amdgpu_device *adev, bool enable)
{
struct cz_power_info *pi = cz_get_pi(adev);
uint32_t reg = 0;
if (pi->caps_sq_ramping) {
reg = RREG32_DIDT(ixDIDT_SQ_CTRL0);
if (enable)
reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 1);
else
reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 0);
WREG32_DIDT(ixDIDT_SQ_CTRL0, reg);
}
if (pi->caps_db_ramping) {
reg = RREG32_DIDT(ixDIDT_DB_CTRL0);
if (enable)
reg = REG_SET_FIELD(reg, DIDT_DB_CTRL0, DIDT_CTRL_EN, 1);
else
reg = REG_SET_FIELD(reg, DIDT_DB_CTRL0, DIDT_CTRL_EN, 0);
WREG32_DIDT(ixDIDT_DB_CTRL0, reg);
}
if (pi->caps_td_ramping) {
reg = RREG32_DIDT(ixDIDT_TD_CTRL0);
if (enable)
reg = REG_SET_FIELD(reg, DIDT_TD_CTRL0, DIDT_CTRL_EN, 1);
else
reg = REG_SET_FIELD(reg, DIDT_TD_CTRL0, DIDT_CTRL_EN, 0);
WREG32_DIDT(ixDIDT_TD_CTRL0, reg);
}
if (pi->caps_tcp_ramping) {
reg = RREG32_DIDT(ixDIDT_TCP_CTRL0);
if (enable)
reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 1);
else
reg = REG_SET_FIELD(reg, DIDT_SQ_CTRL0, DIDT_CTRL_EN, 0);
WREG32_DIDT(ixDIDT_TCP_CTRL0, reg);
}
}
static int cz_enable_didt(struct amdgpu_device *adev, bool enable)
{
struct cz_power_info *pi = cz_get_pi(adev);
int ret;
if (pi->caps_sq_ramping || pi->caps_db_ramping ||
pi->caps_td_ramping || pi->caps_tcp_ramping) {
if (adev->gfx.gfx_current_status != AMDGPU_GFX_SAFE_MODE) {
ret = cz_disable_cgpg(adev);
if (ret) {
DRM_ERROR("Pre Di/Dt disable cg/pg failed\n");
return -EINVAL;
}
adev->gfx.gfx_current_status = AMDGPU_GFX_SAFE_MODE;
}
ret = cz_program_pt_config_registers(adev);
if (ret) {
DRM_ERROR("Di/Dt config failed\n");
return -EINVAL;
}
cz_do_enable_didt(adev, enable);
if (adev->gfx.gfx_current_status == AMDGPU_GFX_SAFE_MODE) {
ret = cz_enable_cgpg(adev);
if (ret) {
DRM_ERROR("Post Di/Dt enable cg/pg failed\n");
return -EINVAL;
}
adev->gfx.gfx_current_status = AMDGPU_GFX_NORMAL_MODE;
}
}
return 0;
}
/* TODO */
static void cz_reset_acp_boot_level(struct amdgpu_device *adev)
{
}
static void cz_update_current_ps(struct amdgpu_device *adev,
struct amdgpu_ps *rps)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct cz_ps *ps = cz_get_ps(rps);
pi->current_ps = *ps;
pi->current_rps = *rps;
pi->current_rps.ps_priv = ps;
}
static void cz_update_requested_ps(struct amdgpu_device *adev,
struct amdgpu_ps *rps)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct cz_ps *ps = cz_get_ps(rps);
pi->requested_ps = *ps;
pi->requested_rps = *rps;
pi->requested_rps.ps_priv = ps;
}
/* PP arbiter support needed TODO */
static void cz_apply_state_adjust_rules(struct amdgpu_device *adev,
struct amdgpu_ps *new_rps,
struct amdgpu_ps *old_rps)
{
struct cz_ps *ps = cz_get_ps(new_rps);
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_and_voltage_limits *limits =
&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
/* 10kHz memory clock */
uint32_t mclk = 0;
ps->force_high = false;
ps->need_dfs_bypass = true;
pi->video_start = new_rps->dclk || new_rps->vclk ||
new_rps->evclk || new_rps->ecclk;
if ((new_rps->class & ATOM_PPLIB_CLASSIFICATION_UI_MASK) ==
ATOM_PPLIB_CLASSIFICATION_UI_BATTERY)
pi->battery_state = true;
else
pi->battery_state = false;
if (pi->caps_stable_power_state)
mclk = limits->mclk;
if (mclk > pi->sys_info.nbp_memory_clock[CZ_NUM_NBPMEMORY_CLOCK - 1])
ps->force_high = true;
}
static int cz_dpm_enable(struct amdgpu_device *adev)
{
int ret = 0;
/* renable will hang up SMU, so check first */
if (cz_check_for_dpm_enabled(adev))
return -EINVAL;
cz_program_voting_clients(adev);
ret = cz_start_dpm(adev);
if (ret) {
DRM_ERROR("Carrizo DPM enable failed\n");
return -EINVAL;
}
ret = cz_program_bootup_state(adev);
if (ret) {
DRM_ERROR("Carrizo bootup state program failed\n");
return -EINVAL;
}
ret = cz_enable_didt(adev, true);
if (ret) {
DRM_ERROR("Carrizo enable di/dt failed\n");
return -EINVAL;
}
cz_reset_acp_boot_level(adev);
cz_update_current_ps(adev, adev->pm.dpm.boot_ps);
return 0;
}
static int cz_dpm_hw_init(struct amdgpu_device *adev)
{
int ret;
if (!amdgpu_dpm)
return 0;
mutex_lock(&adev->pm.mutex);
/* init smc in dpm hw init */
ret = cz_smu_init(adev);
if (ret) {
DRM_ERROR("amdgpu: smc initialization failed\n");
mutex_unlock(&adev->pm.mutex);
return ret;
}
/* do the actual fw loading */
ret = cz_smu_start(adev);
if (ret) {
DRM_ERROR("amdgpu: smc start failed\n");
mutex_unlock(&adev->pm.mutex);
return ret;
}
/* cz dpm setup asic */
cz_dpm_setup_asic(adev);
/* cz dpm enable */
ret = cz_dpm_enable(adev);
if (ret)
adev->pm.dpm_enabled = false;
else
adev->pm.dpm_enabled = true;
mutex_unlock(&adev->pm.mutex);
return 0;
}
static int cz_dpm_disable(struct amdgpu_device *adev)
{
int ret = 0;
if (!cz_check_for_dpm_enabled(adev))
return -EINVAL;
ret = cz_enable_didt(adev, false);
if (ret) {
DRM_ERROR("Carrizo disable di/dt failed\n");
return -EINVAL;
}
cz_clear_voting_clients(adev);
cz_stop_dpm(adev);
cz_update_current_ps(adev, adev->pm.dpm.boot_ps);
return 0;
}
static int cz_dpm_hw_fini(struct amdgpu_device *adev)
{
int ret = 0;
mutex_lock(&adev->pm.mutex);
cz_smu_fini(adev);
if (adev->pm.dpm_enabled) {
ret = cz_dpm_disable(adev);
if (ret)
return -EINVAL;
adev->pm.dpm.current_ps =
adev->pm.dpm.requested_ps =
adev->pm.dpm.boot_ps;
}
adev->pm.dpm_enabled = false;
mutex_unlock(&adev->pm.mutex);
return 0;
}
static int cz_dpm_suspend(struct amdgpu_device *adev)
{
int ret = 0;
if (adev->pm.dpm_enabled) {
mutex_lock(&adev->pm.mutex);
ret = cz_dpm_disable(adev);
if (ret)
return -EINVAL;
adev->pm.dpm.current_ps =
adev->pm.dpm.requested_ps =
adev->pm.dpm.boot_ps;
mutex_unlock(&adev->pm.mutex);
}
return 0;
}
static int cz_dpm_resume(struct amdgpu_device *adev)
{
int ret = 0;
mutex_lock(&adev->pm.mutex);
ret = cz_smu_init(adev);
if (ret) {
DRM_ERROR("amdgpu: smc resume failed\n");
mutex_unlock(&adev->pm.mutex);
return ret;
}
/* do the actual fw loading */
ret = cz_smu_start(adev);
if (ret) {
DRM_ERROR("amdgpu: smc start failed\n");
mutex_unlock(&adev->pm.mutex);
return ret;
}
/* cz dpm setup asic */
cz_dpm_setup_asic(adev);
/* cz dpm enable */
ret = cz_dpm_enable(adev);
if (ret)
adev->pm.dpm_enabled = false;
else
adev->pm.dpm_enabled = true;
mutex_unlock(&adev->pm.mutex);
/* upon resume, re-compute the clocks */
if (adev->pm.dpm_enabled)
amdgpu_pm_compute_clocks(adev);
return 0;
}
static int cz_dpm_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
return 0;
}
static int cz_dpm_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
/* borrowed from KV, need future unify */
static int cz_dpm_get_temperature(struct amdgpu_device *adev)
{
int actual_temp = 0;
uint32_t temp = RREG32_SMC(0xC0300E0C);
if (temp)
actual_temp = 1000 * ((temp / 8) - 49);
return actual_temp;
}
static int cz_dpm_pre_set_power_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_ps requested_ps = *adev->pm.dpm.requested_ps;
struct amdgpu_ps *new_ps = &requested_ps;
cz_update_requested_ps(adev, new_ps);
cz_apply_state_adjust_rules(adev, &pi->requested_rps,
&pi->current_rps);
return 0;
}
static int cz_dpm_update_sclk_limit(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_and_voltage_limits *limits =
&adev->pm.dpm.dyn_state.max_clock_voltage_on_ac;
uint32_t clock, stable_ps_clock = 0;
clock = pi->sclk_dpm.soft_min_clk;
if (pi->caps_stable_power_state) {
stable_ps_clock = limits->sclk * 75 / 100;
if (clock < stable_ps_clock)
clock = stable_ps_clock;
}
if (clock != pi->sclk_dpm.soft_min_clk) {
pi->sclk_dpm.soft_min_clk = clock;
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMin,
cz_get_sclk_level(adev, clock,
PPSMC_MSG_SetSclkSoftMin));
}
if (pi->caps_stable_power_state &&
pi->sclk_dpm.soft_max_clk != clock) {
pi->sclk_dpm.soft_max_clk = clock;
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMax,
cz_get_sclk_level(adev, clock,
PPSMC_MSG_SetSclkSoftMax));
} else {
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMax,
cz_get_sclk_level(adev,
pi->sclk_dpm.soft_max_clk,
PPSMC_MSG_SetSclkSoftMax));
}
return 0;
}
static int cz_dpm_set_deep_sleep_sclk_threshold(struct amdgpu_device *adev)
{
int ret = 0;
struct cz_power_info *pi = cz_get_pi(adev);
if (pi->caps_sclk_ds) {
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetMinDeepSleepSclk,
CZ_MIN_DEEP_SLEEP_SCLK);
}
return ret;
}
/* ?? without dal support, is this still needed in setpowerstate list*/
static int cz_dpm_set_watermark_threshold(struct amdgpu_device *adev)
{
int ret = 0;
struct cz_power_info *pi = cz_get_pi(adev);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetWatermarkFrequency,
pi->sclk_dpm.soft_max_clk);
return ret;
}
static int cz_dpm_enable_nbdpm(struct amdgpu_device *adev)
{
int ret = 0;
struct cz_power_info *pi = cz_get_pi(adev);
/* also depend on dal NBPStateDisableRequired */
if (pi->nb_dpm_enabled_by_driver && !pi->nb_dpm_enabled) {
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_EnableAllSmuFeatures,
NB_DPM_MASK);
if (ret) {
DRM_ERROR("amdgpu: nb dpm enable failed\n");
return ret;
}
pi->nb_dpm_enabled = true;
}
return ret;
}
static void cz_dpm_nbdpm_lm_pstate_enable(struct amdgpu_device *adev,
bool enable)
{
if (enable)
cz_send_msg_to_smc(adev, PPSMC_MSG_EnableLowMemoryPstate);
else
cz_send_msg_to_smc(adev, PPSMC_MSG_DisableLowMemoryPstate);
}
static int cz_dpm_update_low_memory_pstate(struct amdgpu_device *adev)
{
int ret = 0;
struct cz_power_info *pi = cz_get_pi(adev);
struct cz_ps *ps = &pi->requested_ps;
if (pi->sys_info.nb_dpm_enable) {
if (ps->force_high)
cz_dpm_nbdpm_lm_pstate_enable(adev, true);
else
cz_dpm_nbdpm_lm_pstate_enable(adev, false);
}
return ret;
}
/* with dpm enabled */
static int cz_dpm_set_power_state(struct amdgpu_device *adev)
{
int ret = 0;
cz_dpm_update_sclk_limit(adev);
cz_dpm_set_deep_sleep_sclk_threshold(adev);
cz_dpm_set_watermark_threshold(adev);
cz_dpm_enable_nbdpm(adev);
cz_dpm_update_low_memory_pstate(adev);
return ret;
}
static void cz_dpm_post_set_power_state(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_ps *ps = &pi->requested_rps;
cz_update_current_ps(adev, ps);
}
static int cz_dpm_force_highest(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
int ret = 0;
if (pi->sclk_dpm.soft_min_clk != pi->sclk_dpm.soft_max_clk) {
pi->sclk_dpm.soft_min_clk =
pi->sclk_dpm.soft_max_clk;
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMin,
cz_get_sclk_level(adev,
pi->sclk_dpm.soft_min_clk,
PPSMC_MSG_SetSclkSoftMin));
if (ret)
return ret;
}
return ret;
}
static int cz_dpm_force_lowest(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
int ret = 0;
if (pi->sclk_dpm.soft_max_clk != pi->sclk_dpm.soft_min_clk) {
pi->sclk_dpm.soft_max_clk = pi->sclk_dpm.soft_min_clk;
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMax,
cz_get_sclk_level(adev,
pi->sclk_dpm.soft_max_clk,
PPSMC_MSG_SetSclkSoftMax));
if (ret)
return ret;
}
return ret;
}
static uint32_t cz_dpm_get_max_sclk_level(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
if (!pi->max_sclk_level) {
cz_send_msg_to_smc(adev, PPSMC_MSG_GetMaxSclkLevel);
pi->max_sclk_level = cz_get_argument(adev) + 1;
}
if (pi->max_sclk_level > CZ_MAX_HARDWARE_POWERLEVELS) {
DRM_ERROR("Invalid max sclk level!\n");
return -EINVAL;
}
return pi->max_sclk_level;
}
static int cz_dpm_unforce_dpm_levels(struct amdgpu_device *adev)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct amdgpu_clock_voltage_dependency_table *dep_table =
&adev->pm.dpm.dyn_state.vddc_dependency_on_sclk;
uint32_t level = 0;
int ret = 0;
pi->sclk_dpm.soft_min_clk = dep_table->entries[0].clk;
level = cz_dpm_get_max_sclk_level(adev) - 1;
if (level < dep_table->count)
pi->sclk_dpm.soft_max_clk = dep_table->entries[level].clk;
else
pi->sclk_dpm.soft_max_clk =
dep_table->entries[dep_table->count - 1].clk;
/* get min/max sclk soft value
* notify SMU to execute */
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMin,
cz_get_sclk_level(adev,
pi->sclk_dpm.soft_min_clk,
PPSMC_MSG_SetSclkSoftMin));
if (ret)
return ret;
ret = cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetSclkSoftMax,
cz_get_sclk_level(adev,
pi->sclk_dpm.soft_max_clk,
PPSMC_MSG_SetSclkSoftMax));
if (ret)
return ret;
DRM_INFO("DPM unforce state min=%d, max=%d.\n",
pi->sclk_dpm.soft_min_clk,
pi->sclk_dpm.soft_max_clk);
return 0;
}
static int cz_dpm_force_dpm_level(struct amdgpu_device *adev,
enum amdgpu_dpm_forced_level level)
{
int ret = 0;
switch (level) {
case AMDGPU_DPM_FORCED_LEVEL_HIGH:
ret = cz_dpm_force_highest(adev);
if (ret)
return ret;
break;
case AMDGPU_DPM_FORCED_LEVEL_LOW:
ret = cz_dpm_force_lowest(adev);
if (ret)
return ret;
break;
case AMDGPU_DPM_FORCED_LEVEL_AUTO:
ret = cz_dpm_unforce_dpm_levels(adev);
if (ret)
return ret;
break;
default:
break;
}
return ret;
}
/* fix me, display configuration change lists here
* mostly dal related*/
static void cz_dpm_display_configuration_changed(struct amdgpu_device *adev)
{
}
static uint32_t cz_dpm_get_sclk(struct amdgpu_device *adev, bool low)
{
struct cz_power_info *pi = cz_get_pi(adev);
struct cz_ps *requested_state = cz_get_ps(&pi->requested_rps);
if (low)
return requested_state->levels[0].sclk;
else
return requested_state->levels[requested_state->num_levels - 1].sclk;
}
static uint32_t cz_dpm_get_mclk(struct amdgpu_device *adev, bool low)
{
struct cz_power_info *pi = cz_get_pi(adev);
return pi->sys_info.bootup_uma_clk;
}
const struct amdgpu_ip_funcs cz_dpm_ip_funcs = {
.early_init = cz_dpm_early_init,
.late_init = NULL,
.sw_init = cz_dpm_sw_init,
.sw_fini = cz_dpm_sw_fini,
.hw_init = cz_dpm_hw_init,
.hw_fini = cz_dpm_hw_fini,
.suspend = cz_dpm_suspend,
.resume = cz_dpm_resume,
.is_idle = NULL,
.wait_for_idle = NULL,
.soft_reset = NULL,
.print_status = NULL,
.set_clockgating_state = cz_dpm_set_clockgating_state,
.set_powergating_state = cz_dpm_set_powergating_state,
};
static const struct amdgpu_dpm_funcs cz_dpm_funcs = {
.get_temperature = cz_dpm_get_temperature,
.pre_set_power_state = cz_dpm_pre_set_power_state,
.set_power_state = cz_dpm_set_power_state,
.post_set_power_state = cz_dpm_post_set_power_state,
.display_configuration_changed = cz_dpm_display_configuration_changed,
.get_sclk = cz_dpm_get_sclk,
.get_mclk = cz_dpm_get_mclk,
.print_power_state = cz_dpm_print_power_state,
.debugfs_print_current_performance_level =
cz_dpm_debugfs_print_current_performance_level,
.force_performance_level = cz_dpm_force_dpm_level,
.vblank_too_short = NULL,
.powergate_uvd = NULL,
};
static void cz_dpm_set_funcs(struct amdgpu_device *adev)
{
if (NULL == adev->pm.funcs)
adev->pm.funcs = &cz_dpm_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __CZ_DPM_H__
#define __CZ_DPM_H__
#include "smu8_fusion.h"
#define CZ_AT_DFLT 30
#define CZ_NUM_NBPSTATES 4
#define CZ_NUM_NBPMEMORY_CLOCK 2
#define CZ_MAX_HARDWARE_POWERLEVELS 8
#define CZ_MAX_DISPLAY_CLOCK_LEVEL 8
#define CZ_MAX_DISPLAYPHY_IDS 10
#define PPCZ_VOTINGRIGHTSCLIENTS_DFLT0 0x3FFFC102
#define SMC_RAM_END 0x40000
#define DPMFlags_SCLK_Enabled 0x00000001
#define DPMFlags_UVD_Enabled 0x00000002
#define DPMFlags_VCE_Enabled 0x00000004
#define DPMFlags_ACP_Enabled 0x00000008
#define DPMFlags_ForceHighestValid 0x40000000
#define DPMFlags_Debug 0x80000000
/* Do not change the following, it is also defined in SMU8.h */
#define SMU_EnabledFeatureScoreboard_AcpDpmOn 0x00000001
#define SMU_EnabledFeatureScoreboard_SclkDpmOn 0x00100000
#define SMU_EnabledFeatureScoreboard_UvdDpmOn 0x00800000
#define SMU_EnabledFeatureScoreboard_VceDpmOn 0x01000000
/* temporary solution to SetMinDeepSleepSclk
* should indicate by display adaptor
* 10k Hz unit*/
#define CZ_MIN_DEEP_SLEEP_SCLK 800
enum cz_pt_config_reg_type {
CZ_CONFIGREG_MMR = 0,
CZ_CONFIGREG_SMC_IND,
CZ_CONFIGREG_DIDT_IND,
CZ_CONFIGREG_CACHE,
CZ_CONFIGREG_MAX
};
struct cz_pt_config_reg {
uint32_t offset;
uint32_t mask;
uint32_t shift;
uint32_t value;
enum cz_pt_config_reg_type type;
};
struct cz_dpm_entry {
uint32_t soft_min_clk;
uint32_t hard_min_clk;
uint32_t soft_max_clk;
uint32_t hard_max_clk;
};
struct cz_pl {
uint32_t sclk;
uint8_t vddc_index;
uint8_t ds_divider_index;
uint8_t ss_divider_index;
uint8_t allow_gnb_slow;
uint8_t force_nbp_state;
uint8_t display_wm;
uint8_t vce_wm;
};
struct cz_ps {
struct cz_pl levels[CZ_MAX_HARDWARE_POWERLEVELS];
uint32_t num_levels;
bool need_dfs_bypass;
uint8_t dpm0_pg_nb_ps_lo;
uint8_t dpm0_pg_nb_ps_hi;
uint8_t dpmx_nb_ps_lo;
uint8_t dpmx_nb_ps_hi;
bool force_high;
};
struct cz_displayphy_entry {
uint8_t phy_present;
uint8_t active_lane_mapping;
uint8_t display_conf_type;
uint8_t num_active_lanes;
};
struct cz_displayphy_info {
bool phy_access_initialized;
struct cz_displayphy_entry entries[CZ_MAX_DISPLAYPHY_IDS];
};
struct cz_sys_info {
uint32_t bootup_uma_clk;
uint32_t bootup_sclk;
uint32_t dentist_vco_freq;
uint32_t nb_dpm_enable;
uint32_t nbp_memory_clock[CZ_NUM_NBPMEMORY_CLOCK];
uint32_t nbp_n_clock[CZ_NUM_NBPSTATES];
uint8_t nbp_voltage_index[CZ_NUM_NBPSTATES];
uint32_t display_clock[CZ_MAX_DISPLAY_CLOCK_LEVEL];
uint16_t bootup_nb_voltage_index;
uint8_t htc_tmp_lmt;
uint8_t htc_hyst_lmt;
uint32_t uma_channel_number;
};
struct cz_power_info {
uint32_t active_target[CZ_MAX_HARDWARE_POWERLEVELS];
struct cz_sys_info sys_info;
struct cz_pl boot_pl;
bool disable_nb_ps3_in_battery;
bool battery_state;
uint32_t lowest_valid;
uint32_t highest_valid;
uint16_t high_voltage_threshold;
/* smc offsets */
uint32_t sram_end;
uint32_t dpm_table_start;
uint32_t soft_regs_start;
/* dpm SMU tables */
uint8_t uvd_level_count;
uint8_t vce_level_count;
uint8_t acp_level_count;
uint32_t fps_high_threshold;
uint32_t fps_low_threshold;
/* dpm table */
uint32_t dpm_flags;
struct cz_dpm_entry sclk_dpm;
struct cz_dpm_entry uvd_dpm;
struct cz_dpm_entry vce_dpm;
struct cz_dpm_entry acp_dpm;
uint8_t uvd_boot_level;
uint8_t uvd_interval;
uint8_t vce_boot_level;
uint8_t vce_interval;
uint8_t acp_boot_level;
uint8_t acp_interval;
uint8_t graphics_boot_level;
uint8_t graphics_interval;
uint8_t graphics_therm_throttle_enable;
uint8_t graphics_voltage_change_enable;
uint8_t graphics_clk_slow_enable;
uint8_t graphics_clk_slow_divider;
uint32_t low_sclk_interrupt_threshold;
bool uvd_power_gated;
bool vce_power_gated;
bool acp_power_gated;
uint32_t active_process_mask;
uint32_t mgcg_cgtt_local0;
uint32_t mgcg_cgtt_local1;
uint32_t clock_slow_down_step;
uint32_t skip_clock_slow_down;
bool enable_nb_ps_policy;
uint32_t voting_clients;
uint32_t voltage_drop_threshold;
uint32_t gfx_pg_threshold;
uint32_t max_sclk_level;
/* flags */
bool didt_enabled;
bool video_start;
bool cac_enabled;
bool bapm_enabled;
bool nb_dpm_enabled_by_driver;
bool nb_dpm_enabled;
bool auto_thermal_throttling_enabled;
bool dpm_enabled;
bool need_pptable_upload;
/* caps */
bool caps_cac;
bool caps_power_containment;
bool caps_sq_ramping;
bool caps_db_ramping;
bool caps_td_ramping;
bool caps_tcp_ramping;
bool caps_sclk_throttle_low_notification;
bool caps_fps;
bool caps_uvd_dpm;
bool caps_uvd_pg;
bool caps_vce_dpm;
bool caps_vce_pg;
bool caps_acp_dpm;
bool caps_acp_pg;
bool caps_stable_power_state;
bool caps_enable_dfs_bypass;
bool caps_sclk_ds;
bool caps_voltage_island;
/* power state */
struct amdgpu_ps current_rps;
struct cz_ps current_ps;
struct amdgpu_ps requested_rps;
struct cz_ps requested_ps;
bool uvd_power_down;
bool vce_power_down;
bool acp_power_down;
};
/* cz_smc.c */
uint32_t cz_get_argument(struct amdgpu_device *adev);
int cz_send_msg_to_smc(struct amdgpu_device *adev, uint16_t msg);
int cz_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
uint16_t msg, uint32_t parameter);
int cz_read_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address, uint32_t *value, uint32_t limit);
int cz_smu_upload_pptable(struct amdgpu_device *adev);
int cz_smu_download_pptable(struct amdgpu_device *adev, void **table);
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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 "drmP.h"
#include "amdgpu.h"
#include "amdgpu_ih.h"
#include "vid.h"
#include "oss/oss_3_0_1_d.h"
#include "oss/oss_3_0_1_sh_mask.h"
#include "bif/bif_5_1_d.h"
#include "bif/bif_5_1_sh_mask.h"
/*
* Interrupts
* Starting with r6xx, interrupts are handled via a ring buffer.
* Ring buffers are areas of GPU accessible memory that the GPU
* writes interrupt vectors into and the host reads vectors out of.
* There is a rptr (read pointer) that determines where the
* host is currently reading, and a wptr (write pointer)
* which determines where the GPU has written. When the
* pointers are equal, the ring is idle. When the GPU
* writes vectors to the ring buffer, it increments the
* wptr. When there is an interrupt, the host then starts
* fetching commands and processing them until the pointers are
* equal again at which point it updates the rptr.
*/
static void cz_ih_set_interrupt_funcs(struct amdgpu_device *adev);
/**
* cz_ih_enable_interrupts - Enable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Enable the interrupt ring buffer (VI).
*/
static void cz_ih_enable_interrupts(struct amdgpu_device *adev)
{
u32 ih_cntl = RREG32(mmIH_CNTL);
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
WREG32(mmIH_CNTL, ih_cntl);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
adev->irq.ih.enabled = true;
}
/**
* cz_ih_disable_interrupts - Disable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Disable the interrupt ring buffer (VI).
*/
static void cz_ih_disable_interrupts(struct amdgpu_device *adev)
{
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
u32 ih_cntl = RREG32(mmIH_CNTL);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 0);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
WREG32(mmIH_CNTL, ih_cntl);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
adev->irq.ih.enabled = false;
adev->irq.ih.rptr = 0;
}
/**
* cz_ih_irq_init - init and enable the interrupt ring
*
* @adev: amdgpu_device pointer
*
* Allocate a ring buffer for the interrupt controller,
* enable the RLC, disable interrupts, enable the IH
* ring buffer and enable it (VI).
* Called at device load and reume.
* Returns 0 for success, errors for failure.
*/
static int cz_ih_irq_init(struct amdgpu_device *adev)
{
int ret = 0;
int rb_bufsz;
u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
u64 wptr_off;
/* disable irqs */
cz_ih_disable_interrupts(adev);
/* setup interrupt control */
WREG32(mmINTERRUPT_CNTL2, adev->dummy_page.addr >> 8);
interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
/* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
*/
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
/* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_ENABLE, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
/* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
/* set the writeback address whether it's enabled or not */
wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
/* Default settings for IH_CNTL (disabled at first) */
ih_cntl = RREG32(mmIH_CNTL);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, MC_VMID, 0);
if (adev->irq.msi_enabled)
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, RPTR_REARM, 1);
WREG32(mmIH_CNTL, ih_cntl);
pci_set_master(adev->pdev);
/* enable interrupts */
cz_ih_enable_interrupts(adev);
return ret;
}
/**
* cz_ih_irq_disable - disable interrupts
*
* @adev: amdgpu_device pointer
*
* Disable interrupts on the hw (VI).
*/
static void cz_ih_irq_disable(struct amdgpu_device *adev)
{
cz_ih_disable_interrupts(adev);
/* Wait and acknowledge irq */
mdelay(1);
}
/**
* cz_ih_get_wptr - get the IH ring buffer wptr
*
* @adev: amdgpu_device pointer
*
* Get the IH ring buffer wptr from either the register
* or the writeback memory buffer (VI). Also check for
* ring buffer overflow and deal with it.
* Used by cz_irq_process(VI).
* Returns the value of the wptr.
*/
static u32 cz_ih_get_wptr(struct amdgpu_device *adev)
{
u32 wptr, tmp;
wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
tmp = RREG32(mmIH_RB_CNTL);
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
WREG32(mmIH_RB_CNTL, tmp);
}
return (wptr & adev->irq.ih.ptr_mask);
}
/**
* cz_ih_decode_iv - decode an interrupt vector
*
* @adev: amdgpu_device pointer
*
* Decodes the interrupt vector at the current rptr
* position and also advance the position.
*/
static void cz_ih_decode_iv(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
/* wptr/rptr are in bytes! */
u32 ring_index = adev->irq.ih.rptr >> 2;
uint32_t dw[4];
dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
entry->src_id = dw[0] & 0xff;
entry->src_data = dw[1] & 0xfffffff;
entry->ring_id = dw[2] & 0xff;
entry->vm_id = (dw[2] >> 8) & 0xff;
entry->pas_id = (dw[2] >> 16) & 0xffff;
/* wptr/rptr are in bytes! */
adev->irq.ih.rptr += 16;
}
/**
* cz_ih_set_rptr - set the IH ring buffer rptr
*
* @adev: amdgpu_device pointer
*
* Set the IH ring buffer rptr.
*/
static void cz_ih_set_rptr(struct amdgpu_device *adev)
{
WREG32(mmIH_RB_RPTR, adev->irq.ih.rptr);
}
static int cz_ih_early_init(struct amdgpu_device *adev)
{
cz_ih_set_interrupt_funcs(adev);
return 0;
}
static int cz_ih_sw_init(struct amdgpu_device *adev)
{
int r;
r = amdgpu_ih_ring_init(adev, 64 * 1024, false);
if (r)
return r;
r = amdgpu_irq_init(adev);
return r;
}
static int cz_ih_sw_fini(struct amdgpu_device *adev)
{
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
return 0;
}
static int cz_ih_hw_init(struct amdgpu_device *adev)
{
int r;
r = cz_ih_irq_init(adev);
if (r)
return r;
return 0;
}
static int cz_ih_hw_fini(struct amdgpu_device *adev)
{
cz_ih_irq_disable(adev);
return 0;
}
static int cz_ih_suspend(struct amdgpu_device *adev)
{
return cz_ih_hw_fini(adev);
}
static int cz_ih_resume(struct amdgpu_device *adev)
{
return cz_ih_hw_init(adev);
}
static bool cz_ih_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS);
if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return false;
return true;
}
static int cz_ih_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
/* read MC_STATUS */
tmp = RREG32(mmSRBM_STATUS);
if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void cz_ih_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "CZ IH registers\n");
dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
RREG32(mmSRBM_STATUS));
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
dev_info(adev->dev, " INTERRUPT_CNTL=0x%08X\n",
RREG32(mmINTERRUPT_CNTL));
dev_info(adev->dev, " INTERRUPT_CNTL2=0x%08X\n",
RREG32(mmINTERRUPT_CNTL2));
dev_info(adev->dev, " IH_CNTL=0x%08X\n",
RREG32(mmIH_CNTL));
dev_info(adev->dev, " IH_RB_CNTL=0x%08X\n",
RREG32(mmIH_RB_CNTL));
dev_info(adev->dev, " IH_RB_BASE=0x%08X\n",
RREG32(mmIH_RB_BASE));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_LO=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_LO));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_HI=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_HI));
dev_info(adev->dev, " IH_RB_RPTR=0x%08X\n",
RREG32(mmIH_RB_RPTR));
dev_info(adev->dev, " IH_RB_WPTR=0x%08X\n",
RREG32(mmIH_RB_WPTR));
}
static int cz_ih_soft_reset(struct amdgpu_device *adev)
{
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS);
if (tmp & SRBM_STATUS__IH_BUSY_MASK)
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
SOFT_RESET_IH, 1);
if (srbm_soft_reset) {
cz_ih_print_status(adev);
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
cz_ih_print_status(adev);
}
return 0;
}
static int cz_ih_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
// TODO
return 0;
}
static int cz_ih_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
// TODO
return 0;
}
const struct amdgpu_ip_funcs cz_ih_ip_funcs = {
.early_init = cz_ih_early_init,
.late_init = NULL,
.sw_init = cz_ih_sw_init,
.sw_fini = cz_ih_sw_fini,
.hw_init = cz_ih_hw_init,
.hw_fini = cz_ih_hw_fini,
.suspend = cz_ih_suspend,
.resume = cz_ih_resume,
.is_idle = cz_ih_is_idle,
.wait_for_idle = cz_ih_wait_for_idle,
.soft_reset = cz_ih_soft_reset,
.print_status = cz_ih_print_status,
.set_clockgating_state = cz_ih_set_clockgating_state,
.set_powergating_state = cz_ih_set_powergating_state,
};
static const struct amdgpu_ih_funcs cz_ih_funcs = {
.get_wptr = cz_ih_get_wptr,
.decode_iv = cz_ih_decode_iv,
.set_rptr = cz_ih_set_rptr
};
static void cz_ih_set_interrupt_funcs(struct amdgpu_device *adev)
{
if (adev->irq.ih_funcs == NULL)
adev->irq.ih_funcs = &cz_ih_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __CZ_IH_H__
#define __CZ_IH_H__
extern const struct amdgpu_ip_funcs cz_ih_ip_funcs;
#endif /* __CZ_IH_H__ */
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef CZ_PP_SMC_H
#define CZ_PP_SMC_H
#pragma pack(push, 1)
/* Fan control algorithm:*/
#define FDO_MODE_HARDWARE 0
#define FDO_MODE_PIECE_WISE_LINEAR 1
enum FAN_CONTROL {
FAN_CONTROL_FUZZY,
FAN_CONTROL_TABLE
};
enum DPM_ARRAY {
DPM_ARRAY_HARD_MAX,
DPM_ARRAY_HARD_MIN,
DPM_ARRAY_SOFT_MAX,
DPM_ARRAY_SOFT_MIN
};
/*
* Return codes for driver to SMC communication.
* Leave these #define-s, enums might not be exactly 8-bits on the microcontroller.
*/
#define PPSMC_Result_OK ((uint16_t)0x01)
#define PPSMC_Result_NoMore ((uint16_t)0x02)
#define PPSMC_Result_NotNow ((uint16_t)0x03)
#define PPSMC_Result_Failed ((uint16_t)0xFF)
#define PPSMC_Result_UnknownCmd ((uint16_t)0xFE)
#define PPSMC_Result_UnknownVT ((uint16_t)0xFD)
#define PPSMC_isERROR(x) ((uint16_t)0x80 & (x))
/*
* Supported driver messages
*/
#define PPSMC_MSG_Test ((uint16_t) 0x1)
#define PPSMC_MSG_GetFeatureStatus ((uint16_t) 0x2)
#define PPSMC_MSG_EnableAllSmuFeatures ((uint16_t) 0x3)
#define PPSMC_MSG_DisableAllSmuFeatures ((uint16_t) 0x4)
#define PPSMC_MSG_OptimizeBattery ((uint16_t) 0x5)
#define PPSMC_MSG_MaximizePerf ((uint16_t) 0x6)
#define PPSMC_MSG_UVDPowerOFF ((uint16_t) 0x7)
#define PPSMC_MSG_UVDPowerON ((uint16_t) 0x8)
#define PPSMC_MSG_VCEPowerOFF ((uint16_t) 0x9)
#define PPSMC_MSG_VCEPowerON ((uint16_t) 0xA)
#define PPSMC_MSG_ACPPowerOFF ((uint16_t) 0xB)
#define PPSMC_MSG_ACPPowerON ((uint16_t) 0xC)
#define PPSMC_MSG_SDMAPowerOFF ((uint16_t) 0xD)
#define PPSMC_MSG_SDMAPowerON ((uint16_t) 0xE)
#define PPSMC_MSG_XDMAPowerOFF ((uint16_t) 0xF)
#define PPSMC_MSG_XDMAPowerON ((uint16_t) 0x10)
#define PPSMC_MSG_SetMinDeepSleepSclk ((uint16_t) 0x11)
#define PPSMC_MSG_SetSclkSoftMin ((uint16_t) 0x12)
#define PPSMC_MSG_SetSclkSoftMax ((uint16_t) 0x13)
#define PPSMC_MSG_SetSclkHardMin ((uint16_t) 0x14)
#define PPSMC_MSG_SetSclkHardMax ((uint16_t) 0x15)
#define PPSMC_MSG_SetLclkSoftMin ((uint16_t) 0x16)
#define PPSMC_MSG_SetLclkSoftMax ((uint16_t) 0x17)
#define PPSMC_MSG_SetLclkHardMin ((uint16_t) 0x18)
#define PPSMC_MSG_SetLclkHardMax ((uint16_t) 0x19)
#define PPSMC_MSG_SetUvdSoftMin ((uint16_t) 0x1A)
#define PPSMC_MSG_SetUvdSoftMax ((uint16_t) 0x1B)
#define PPSMC_MSG_SetUvdHardMin ((uint16_t) 0x1C)
#define PPSMC_MSG_SetUvdHardMax ((uint16_t) 0x1D)
#define PPSMC_MSG_SetEclkSoftMin ((uint16_t) 0x1E)
#define PPSMC_MSG_SetEclkSoftMax ((uint16_t) 0x1F)
#define PPSMC_MSG_SetEclkHardMin ((uint16_t) 0x20)
#define PPSMC_MSG_SetEclkHardMax ((uint16_t) 0x21)
#define PPSMC_MSG_SetAclkSoftMin ((uint16_t) 0x22)
#define PPSMC_MSG_SetAclkSoftMax ((uint16_t) 0x23)
#define PPSMC_MSG_SetAclkHardMin ((uint16_t) 0x24)
#define PPSMC_MSG_SetAclkHardMax ((uint16_t) 0x25)
#define PPSMC_MSG_SetNclkSoftMin ((uint16_t) 0x26)
#define PPSMC_MSG_SetNclkSoftMax ((uint16_t) 0x27)
#define PPSMC_MSG_SetNclkHardMin ((uint16_t) 0x28)
#define PPSMC_MSG_SetNclkHardMax ((uint16_t) 0x29)
#define PPSMC_MSG_SetPstateSoftMin ((uint16_t) 0x2A)
#define PPSMC_MSG_SetPstateSoftMax ((uint16_t) 0x2B)
#define PPSMC_MSG_SetPstateHardMin ((uint16_t) 0x2C)
#define PPSMC_MSG_SetPstateHardMax ((uint16_t) 0x2D)
#define PPSMC_MSG_DisableLowMemoryPstate ((uint16_t) 0x2E)
#define PPSMC_MSG_EnableLowMemoryPstate ((uint16_t) 0x2F)
#define PPSMC_MSG_UcodeAddressLow ((uint16_t) 0x30)
#define PPSMC_MSG_UcodeAddressHigh ((uint16_t) 0x31)
#define PPSMC_MSG_UcodeLoadStatus ((uint16_t) 0x32)
#define PPSMC_MSG_DriverDramAddrHi ((uint16_t) 0x33)
#define PPSMC_MSG_DriverDramAddrLo ((uint16_t) 0x34)
#define PPSMC_MSG_CondExecDramAddrHi ((uint16_t) 0x35)
#define PPSMC_MSG_CondExecDramAddrLo ((uint16_t) 0x36)
#define PPSMC_MSG_LoadUcodes ((uint16_t) 0x37)
#define PPSMC_MSG_DriverResetMode ((uint16_t) 0x38)
#define PPSMC_MSG_PowerStateNotify ((uint16_t) 0x39)
#define PPSMC_MSG_SetDisplayPhyConfig ((uint16_t) 0x3A)
#define PPSMC_MSG_GetMaxSclkLevel ((uint16_t) 0x3B)
#define PPSMC_MSG_GetMaxLclkLevel ((uint16_t) 0x3C)
#define PPSMC_MSG_GetMaxUvdLevel ((uint16_t) 0x3D)
#define PPSMC_MSG_GetMaxEclkLevel ((uint16_t) 0x3E)
#define PPSMC_MSG_GetMaxAclkLevel ((uint16_t) 0x3F)
#define PPSMC_MSG_GetMaxNclkLevel ((uint16_t) 0x40)
#define PPSMC_MSG_GetMaxPstate ((uint16_t) 0x41)
#define PPSMC_MSG_DramAddrHiVirtual ((uint16_t) 0x42)
#define PPSMC_MSG_DramAddrLoVirtual ((uint16_t) 0x43)
#define PPSMC_MSG_DramAddrHiPhysical ((uint16_t) 0x44)
#define PPSMC_MSG_DramAddrLoPhysical ((uint16_t) 0x45)
#define PPSMC_MSG_DramBufferSize ((uint16_t) 0x46)
#define PPSMC_MSG_SetMmPwrLogDramAddrHi ((uint16_t) 0x47)
#define PPSMC_MSG_SetMmPwrLogDramAddrLo ((uint16_t) 0x48)
#define PPSMC_MSG_SetClkTableAddrHi ((uint16_t) 0x49)
#define PPSMC_MSG_SetClkTableAddrLo ((uint16_t) 0x4A)
#define PPSMC_MSG_GetConservativePowerLimit ((uint16_t) 0x4B)
#define PPSMC_MSG_InitJobs ((uint16_t) 0x252)
#define PPSMC_MSG_ExecuteJob ((uint16_t) 0x254)
#define PPSMC_MSG_NBDPM_Enable ((uint16_t) 0x140)
#define PPSMC_MSG_NBDPM_Disable ((uint16_t) 0x141)
#define PPSMC_MSG_DPM_FPS_Mode ((uint16_t) 0x15d)
#define PPSMC_MSG_DPM_Activity_Mode ((uint16_t) 0x15e)
#define PPSMC_MSG_PmStatusLogStart ((uint16_t) 0x170)
#define PPSMC_MSG_PmStatusLogSample ((uint16_t) 0x171)
#define PPSMC_MSG_AllowLowSclkInterrupt ((uint16_t) 0x184)
#define PPSMC_MSG_MmPowerMonitorStart ((uint16_t) 0x18F)
#define PPSMC_MSG_MmPowerMonitorStop ((uint16_t) 0x190)
#define PPSMC_MSG_MmPowerMonitorRestart ((uint16_t) 0x191)
#define PPSMC_MSG_SetClockGateMask ((uint16_t) 0x260)
#define PPSMC_MSG_SetFpsThresholdLo ((uint16_t) 0x264)
#define PPSMC_MSG_SetFpsThresholdHi ((uint16_t) 0x265)
#define PPSMC_MSG_SetLowSclkIntrThreshold ((uint16_t) 0x266)
#define PPSMC_MSG_ClkTableXferToDram ((uint16_t) 0x267)
#define PPSMC_MSG_ClkTableXferToSmu ((uint16_t) 0x268)
#define PPSMC_MSG_GetAverageGraphicsActivity ((uint16_t) 0x269)
#define PPSMC_MSG_GetAverageGioActivity ((uint16_t) 0x26A)
#define PPSMC_MSG_SetLoggerBufferSize ((uint16_t) 0x26B)
#define PPSMC_MSG_SetLoggerAddressHigh ((uint16_t) 0x26C)
#define PPSMC_MSG_SetLoggerAddressLow ((uint16_t) 0x26D)
#define PPSMC_MSG_SetWatermarkFrequency ((uint16_t) 0x26E)
/* REMOVE LATER*/
#define PPSMC_MSG_DPM_ForceState ((uint16_t) 0x104)
/* Feature Enable Masks*/
#define NB_DPM_MASK 0x00000800
#define VDDGFX_MASK 0x00800000
#define VCE_DPM_MASK 0x00400000
#define ACP_DPM_MASK 0x00040000
#define UVD_DPM_MASK 0x00010000
#define GFX_CU_PG_MASK 0x00004000
#define SCLK_DPM_MASK 0x00080000
#if !defined(SMC_MICROCODE)
#pragma pack(pop)
#endif
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "smu8.h"
#include "smu8_fusion.h"
#include "cz_ppsmc.h"
#include "cz_smumgr.h"
#include "smu_ucode_xfer_cz.h"
#include "amdgpu_ucode.h"
#include "smu/smu_8_0_d.h"
#include "smu/smu_8_0_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_sh_mask.h"
uint32_t cz_get_argument(struct amdgpu_device *adev)
{
return RREG32(mmSMU_MP1_SRBM2P_ARG_0);
}
static struct cz_smu_private_data *cz_smu_get_priv(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv =
(struct cz_smu_private_data *)(adev->smu.priv);
return priv;
}
int cz_send_msg_to_smc_async(struct amdgpu_device *adev, u16 msg)
{
int i;
u32 content = 0, tmp;
for (i = 0; i < adev->usec_timeout; i++) {
tmp = REG_GET_FIELD(RREG32(mmSMU_MP1_SRBM2P_RESP_0),
SMU_MP1_SRBM2P_RESP_0, CONTENT);
if (content != tmp)
break;
udelay(1);
}
/* timeout means wrong logic*/
if (i == adev->usec_timeout)
return -EINVAL;
WREG32(mmSMU_MP1_SRBM2P_RESP_0, 0);
WREG32(mmSMU_MP1_SRBM2P_MSG_0, msg);
return 0;
}
int cz_send_msg_to_smc(struct amdgpu_device *adev, u16 msg)
{
int i;
u32 content = 0, tmp = 0;
if (cz_send_msg_to_smc_async(adev, msg))
return -EINVAL;
for (i = 0; i < adev->usec_timeout; i++) {
tmp = REG_GET_FIELD(RREG32(mmSMU_MP1_SRBM2P_RESP_0),
SMU_MP1_SRBM2P_RESP_0, CONTENT);
if (content != tmp)
break;
udelay(1);
}
/* timeout means wrong logic*/
if (i == adev->usec_timeout)
return -EINVAL;
if (PPSMC_Result_OK != tmp) {
dev_err(adev->dev, "SMC Failed to send Message.\n");
return -EINVAL;
}
return 0;
}
int cz_send_msg_to_smc_with_parameter_async(struct amdgpu_device *adev,
u16 msg, u32 parameter)
{
WREG32(mmSMU_MP1_SRBM2P_ARG_0, parameter);
return cz_send_msg_to_smc_async(adev, msg);
}
int cz_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
u16 msg, u32 parameter)
{
WREG32(mmSMU_MP1_SRBM2P_ARG_0, parameter);
return cz_send_msg_to_smc(adev, msg);
}
static int cz_set_smc_sram_address(struct amdgpu_device *adev,
u32 smc_address, u32 limit)
{
if (smc_address & 3)
return -EINVAL;
if ((smc_address + 3) > limit)
return -EINVAL;
WREG32(mmMP0PUB_IND_INDEX_0, SMN_MP1_SRAM_START_ADDR + smc_address);
return 0;
}
int cz_read_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
u32 *value, u32 limit)
{
int ret;
ret = cz_set_smc_sram_address(adev, smc_address, limit);
if (ret)
return ret;
*value = RREG32(mmMP0PUB_IND_DATA_0);
return 0;
}
int cz_write_smc_sram_dword(struct amdgpu_device *adev, u32 smc_address,
u32 value, u32 limit)
{
int ret;
ret = cz_set_smc_sram_address(adev, smc_address, limit);
if (ret)
return ret;
WREG32(mmMP0PUB_IND_DATA_0, value);
return 0;
}
static int cz_smu_request_load_fw(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
uint32_t smc_addr = SMU8_FIRMWARE_HEADER_LOCATION +
offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus);
cz_write_smc_sram_dword(adev, smc_addr, 0, smc_addr + 4);
/*prepare toc buffers*/
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_DriverDramAddrHi,
priv->toc_buffer.mc_addr_high);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_DriverDramAddrLo,
priv->toc_buffer.mc_addr_low);
cz_send_msg_to_smc(adev, PPSMC_MSG_InitJobs);
/*execute jobs*/
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_ExecuteJob,
priv->toc_entry_aram);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_ExecuteJob,
priv->toc_entry_power_profiling_index);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_ExecuteJob,
priv->toc_entry_initialize_index);
return 0;
}
/*
*Check if the FW has been loaded, SMU will not return if loading
*has not finished.
*/
static int cz_smu_check_fw_load_finish(struct amdgpu_device *adev,
uint32_t fw_mask)
{
int i;
uint32_t index = SMN_MP1_SRAM_START_ADDR +
SMU8_FIRMWARE_HEADER_LOCATION +
offsetof(struct SMU8_Firmware_Header, UcodeLoadStatus);
WREG32(mmMP0PUB_IND_INDEX, index);
for (i = 0; i < adev->usec_timeout; i++) {
if (fw_mask == (RREG32(mmMP0PUB_IND_DATA) & fw_mask))
break;
udelay(1);
}
if (i >= adev->usec_timeout) {
dev_err(adev->dev,
"SMU check loaded firmware failed, expecting 0x%x, getting 0x%x",
fw_mask, RREG32(mmMP0PUB_IND_DATA));
return -EINVAL;
}
return 0;
}
/*
* interfaces for different ip blocks to check firmware loading status
* 0 for success otherwise failed
*/
static int cz_smu_check_finished(struct amdgpu_device *adev,
enum AMDGPU_UCODE_ID id)
{
switch (id) {
case AMDGPU_UCODE_ID_SDMA0:
if (adev->smu.fw_flags & AMDGPU_SDMA0_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_SDMA1:
if (adev->smu.fw_flags & AMDGPU_SDMA1_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_CP_CE:
if (adev->smu.fw_flags & AMDGPU_CPCE_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_CP_PFP:
if (adev->smu.fw_flags & AMDGPU_CPPFP_UCODE_LOADED)
return 0;
case AMDGPU_UCODE_ID_CP_ME:
if (adev->smu.fw_flags & AMDGPU_CPME_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_CP_MEC1:
if (adev->smu.fw_flags & AMDGPU_CPMEC1_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_CP_MEC2:
if (adev->smu.fw_flags & AMDGPU_CPMEC2_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_RLC_G:
if (adev->smu.fw_flags & AMDGPU_CPRLC_UCODE_LOADED)
return 0;
break;
case AMDGPU_UCODE_ID_MAXIMUM:
default:
break;
}
return 1;
}
static int cz_load_mec_firmware(struct amdgpu_device *adev)
{
struct amdgpu_firmware_info *ucode =
&adev->firmware.ucode[AMDGPU_UCODE_ID_CP_MEC1];
uint32_t reg_data;
uint32_t tmp;
if (ucode->fw == NULL)
return -EINVAL;
/* Disable MEC parsing/prefetching */
tmp = RREG32(mmCP_MEC_CNTL);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME1_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME2_HALT, 1);
WREG32(mmCP_MEC_CNTL, tmp);
tmp = RREG32(mmCP_CPC_IC_BASE_CNTL);
tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, VMID, 0);
tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, ATC, 0);
tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, CACHE_POLICY, 0);
tmp = REG_SET_FIELD(tmp, CP_CPC_IC_BASE_CNTL, MTYPE, 1);
WREG32(mmCP_CPC_IC_BASE_CNTL, tmp);
reg_data = lower_32_bits(ucode->mc_addr) &
REG_FIELD_MASK(CP_CPC_IC_BASE_LO, IC_BASE_LO);
WREG32(mmCP_CPC_IC_BASE_LO, reg_data);
reg_data = upper_32_bits(ucode->mc_addr) &
REG_FIELD_MASK(CP_CPC_IC_BASE_HI, IC_BASE_HI);
WREG32(mmCP_CPC_IC_BASE_HI, reg_data);
return 0;
}
int cz_smu_start(struct amdgpu_device *adev)
{
int ret = 0;
uint32_t fw_to_check = UCODE_ID_RLC_G_MASK |
UCODE_ID_SDMA0_MASK |
UCODE_ID_SDMA1_MASK |
UCODE_ID_CP_CE_MASK |
UCODE_ID_CP_ME_MASK |
UCODE_ID_CP_PFP_MASK |
UCODE_ID_CP_MEC_JT1_MASK |
UCODE_ID_CP_MEC_JT2_MASK;
cz_smu_request_load_fw(adev);
ret = cz_smu_check_fw_load_finish(adev, fw_to_check);
if (ret)
return ret;
/* manually load MEC firmware for CZ */
if (adev->asic_type == CHIP_CARRIZO) {
ret = cz_load_mec_firmware(adev);
if (ret) {
dev_err(adev->dev, "(%d) Mec Firmware load failed\n", ret);
return ret;
}
}
/* setup fw load flag */
adev->smu.fw_flags = AMDGPU_SDMA0_UCODE_LOADED |
AMDGPU_SDMA1_UCODE_LOADED |
AMDGPU_CPCE_UCODE_LOADED |
AMDGPU_CPPFP_UCODE_LOADED |
AMDGPU_CPME_UCODE_LOADED |
AMDGPU_CPMEC1_UCODE_LOADED |
AMDGPU_CPMEC2_UCODE_LOADED |
AMDGPU_CPRLC_UCODE_LOADED;
return ret;
}
static uint32_t cz_convert_fw_type(uint32_t fw_type)
{
enum AMDGPU_UCODE_ID result = AMDGPU_UCODE_ID_MAXIMUM;
switch (fw_type) {
case UCODE_ID_SDMA0:
result = AMDGPU_UCODE_ID_SDMA0;
break;
case UCODE_ID_SDMA1:
result = AMDGPU_UCODE_ID_SDMA1;
break;
case UCODE_ID_CP_CE:
result = AMDGPU_UCODE_ID_CP_CE;
break;
case UCODE_ID_CP_PFP:
result = AMDGPU_UCODE_ID_CP_PFP;
break;
case UCODE_ID_CP_ME:
result = AMDGPU_UCODE_ID_CP_ME;
break;
case UCODE_ID_CP_MEC_JT1:
case UCODE_ID_CP_MEC_JT2:
result = AMDGPU_UCODE_ID_CP_MEC1;
break;
case UCODE_ID_RLC_G:
result = AMDGPU_UCODE_ID_RLC_G;
break;
default:
DRM_ERROR("UCode type is out of range!");
}
return result;
}
static uint8_t cz_smu_translate_firmware_enum_to_arg(
enum cz_scratch_entry firmware_enum)
{
uint8_t ret = 0;
switch (firmware_enum) {
case CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0:
ret = UCODE_ID_SDMA0;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1:
ret = UCODE_ID_SDMA1;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE:
ret = UCODE_ID_CP_CE;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP:
ret = UCODE_ID_CP_PFP;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME:
ret = UCODE_ID_CP_ME;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1:
ret = UCODE_ID_CP_MEC_JT1;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2:
ret = UCODE_ID_CP_MEC_JT2;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG:
ret = UCODE_ID_GMCON_RENG;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G:
ret = UCODE_ID_RLC_G;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH:
ret = UCODE_ID_RLC_SCRATCH;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM:
ret = UCODE_ID_RLC_SRM_ARAM;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM:
ret = UCODE_ID_RLC_SRM_DRAM;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM:
ret = UCODE_ID_DMCU_ERAM;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM:
ret = UCODE_ID_DMCU_IRAM;
break;
case CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING:
ret = TASK_ARG_INIT_MM_PWR_LOG;
break;
case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_HALT:
case CZ_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING:
case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS:
case CZ_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT:
case CZ_SCRATCH_ENTRY_DATA_ID_SDMA_START:
case CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS:
ret = TASK_ARG_REG_MMIO;
break;
case CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE:
ret = TASK_ARG_INIT_CLK_TABLE;
break;
}
return ret;
}
static int cz_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
enum cz_scratch_entry firmware_enum,
struct cz_buffer_entry *entry)
{
uint64_t gpu_addr;
uint32_t data_size;
uint8_t ucode_id = cz_smu_translate_firmware_enum_to_arg(firmware_enum);
enum AMDGPU_UCODE_ID id = cz_convert_fw_type(ucode_id);
struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
const struct gfx_firmware_header_v1_0 *header;
if (ucode->fw == NULL)
return -EINVAL;
gpu_addr = ucode->mc_addr;
header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
data_size = le32_to_cpu(header->header.ucode_size_bytes);
if ((firmware_enum == CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1) ||
(firmware_enum == CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2)) {
gpu_addr += le32_to_cpu(header->jt_offset) << 2;
data_size = le32_to_cpu(header->jt_size) << 2;
}
entry->mc_addr_low = lower_32_bits(gpu_addr);
entry->mc_addr_high = upper_32_bits(gpu_addr);
entry->data_size = data_size;
entry->firmware_ID = firmware_enum;
return 0;
}
static int cz_smu_populate_single_scratch_entry(struct amdgpu_device *adev,
enum cz_scratch_entry scratch_type,
uint32_t size_in_byte,
struct cz_buffer_entry *entry)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
uint64_t mc_addr = (((uint64_t) priv->smu_buffer.mc_addr_high) << 32) |
priv->smu_buffer.mc_addr_low;
mc_addr += size_in_byte;
priv->smu_buffer_used_bytes += size_in_byte;
entry->data_size = size_in_byte;
entry->kaddr = priv->smu_buffer.kaddr + priv->smu_buffer_used_bytes;
entry->mc_addr_low = lower_32_bits(mc_addr);
entry->mc_addr_high = upper_32_bits(mc_addr);
entry->firmware_ID = scratch_type;
return 0;
}
static int cz_smu_populate_single_ucode_load_task(struct amdgpu_device *adev,
enum cz_scratch_entry firmware_enum,
bool is_last)
{
uint8_t i;
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr;
struct SMU_Task *task = &toc->tasks[priv->toc_entry_used_count++];
task->type = TASK_TYPE_UCODE_LOAD;
task->arg = cz_smu_translate_firmware_enum_to_arg(firmware_enum);
task->next = is_last ? END_OF_TASK_LIST : priv->toc_entry_used_count;
for (i = 0; i < priv->driver_buffer_length; i++)
if (priv->driver_buffer[i].firmware_ID == firmware_enum)
break;
if (i >= priv->driver_buffer_length) {
dev_err(adev->dev, "Invalid Firmware Type\n");
return -EINVAL;
}
task->addr.low = priv->driver_buffer[i].mc_addr_low;
task->addr.high = priv->driver_buffer[i].mc_addr_high;
task->size_bytes = priv->driver_buffer[i].data_size;
return 0;
}
static int cz_smu_populate_single_scratch_task(struct amdgpu_device *adev,
enum cz_scratch_entry firmware_enum,
uint8_t type, bool is_last)
{
uint8_t i;
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr;
struct SMU_Task *task = &toc->tasks[priv->toc_entry_used_count++];
task->type = type;
task->arg = cz_smu_translate_firmware_enum_to_arg(firmware_enum);
task->next = is_last ? END_OF_TASK_LIST : priv->toc_entry_used_count;
for (i = 0; i < priv->scratch_buffer_length; i++)
if (priv->scratch_buffer[i].firmware_ID == firmware_enum)
break;
if (i >= priv->scratch_buffer_length) {
dev_err(adev->dev, "Invalid Firmware Type\n");
return -EINVAL;
}
task->addr.low = priv->scratch_buffer[i].mc_addr_low;
task->addr.high = priv->scratch_buffer[i].mc_addr_high;
task->size_bytes = priv->scratch_buffer[i].data_size;
if (CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS == firmware_enum) {
struct cz_ih_meta_data *pIHReg_restore =
(struct cz_ih_meta_data *)priv->scratch_buffer[i].kaddr;
pIHReg_restore->command =
METADATA_CMD_MODE0 | METADATA_PERFORM_ON_LOAD;
}
return 0;
}
static int cz_smu_construct_toc_for_rlc_aram_save(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
priv->toc_entry_aram = priv->toc_entry_used_count;
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
TASK_TYPE_UCODE_SAVE, true);
return 0;
}
static int cz_smu_construct_toc_for_vddgfx_enter(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr;
toc->JobList[JOB_GFX_SAVE] = (uint8_t)priv->toc_entry_used_count;
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
TASK_TYPE_UCODE_SAVE, false);
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
TASK_TYPE_UCODE_SAVE, true);
return 0;
}
static int cz_smu_construct_toc_for_vddgfx_exit(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr;
toc->JobList[JOB_GFX_RESTORE] = (uint8_t)priv->toc_entry_used_count;
/* populate ucode */
if (adev->firmware.smu_load) {
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, false);
}
/* populate scratch */
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
TASK_TYPE_UCODE_LOAD, false);
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
TASK_TYPE_UCODE_LOAD, false);
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
TASK_TYPE_UCODE_LOAD, true);
return 0;
}
static int cz_smu_construct_toc_for_power_profiling(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
priv->toc_entry_power_profiling_index = priv->toc_entry_used_count;
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
TASK_TYPE_INITIALIZE, true);
return 0;
}
static int cz_smu_construct_toc_for_bootup(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
priv->toc_entry_initialize_index = priv->toc_entry_used_count;
if (adev->firmware.smu_load) {
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2, false);
cz_smu_populate_single_ucode_load_task(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G, true);
}
return 0;
}
static int cz_smu_construct_toc_for_clock_table(struct amdgpu_device *adev)
{
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
priv->toc_entry_clock_table = priv->toc_entry_used_count;
cz_smu_populate_single_scratch_task(adev,
CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE,
TASK_TYPE_INITIALIZE, true);
return 0;
}
static int cz_smu_initialize_toc_empty_job_list(struct amdgpu_device *adev)
{
int i;
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
struct TOC *toc = (struct TOC *)priv->toc_buffer.kaddr;
for (i = 0; i < NUM_JOBLIST_ENTRIES; i++)
toc->JobList[i] = (uint8_t)IGNORE_JOB;
return 0;
}
/*
* cz smu uninitialization
*/
int cz_smu_fini(struct amdgpu_device *adev)
{
amdgpu_bo_unref(&adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
kfree(adev->smu.priv);
adev->smu.priv = NULL;
if (adev->firmware.smu_load)
amdgpu_ucode_fini_bo(adev);
return 0;
}
int cz_smu_download_pptable(struct amdgpu_device *adev, void **table)
{
uint8_t i;
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
for (i = 0; i < priv->scratch_buffer_length; i++)
if (priv->scratch_buffer[i].firmware_ID ==
CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE)
break;
if (i >= priv->scratch_buffer_length) {
dev_err(adev->dev, "Invalid Scratch Type\n");
return -EINVAL;
}
*table = (struct SMU8_Fusion_ClkTable *)priv->scratch_buffer[i].kaddr;
/* prepare buffer for pptable */
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetClkTableAddrHi,
priv->scratch_buffer[i].mc_addr_high);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetClkTableAddrLo,
priv->scratch_buffer[i].mc_addr_low);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_ExecuteJob,
priv->toc_entry_clock_table);
/* actual downloading */
cz_send_msg_to_smc(adev, PPSMC_MSG_ClkTableXferToDram);
return 0;
}
int cz_smu_upload_pptable(struct amdgpu_device *adev)
{
uint8_t i;
struct cz_smu_private_data *priv = cz_smu_get_priv(adev);
for (i = 0; i < priv->scratch_buffer_length; i++)
if (priv->scratch_buffer[i].firmware_ID ==
CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE)
break;
if (i >= priv->scratch_buffer_length) {
dev_err(adev->dev, "Invalid Scratch Type\n");
return -EINVAL;
}
/* prepare SMU */
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetClkTableAddrHi,
priv->scratch_buffer[i].mc_addr_high);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_SetClkTableAddrLo,
priv->scratch_buffer[i].mc_addr_low);
cz_send_msg_to_smc_with_parameter(adev,
PPSMC_MSG_ExecuteJob,
priv->toc_entry_clock_table);
/* actual uploading */
cz_send_msg_to_smc(adev, PPSMC_MSG_ClkTableXferToSmu);
return 0;
}
/*
* cz smumgr functions initialization
*/
static const struct amdgpu_smumgr_funcs cz_smumgr_funcs = {
.check_fw_load_finish = cz_smu_check_finished,
.request_smu_load_fw = NULL,
.request_smu_specific_fw = NULL,
};
/*
* cz smu initialization
*/
int cz_smu_init(struct amdgpu_device *adev)
{
int ret = -EINVAL;
uint64_t mc_addr = 0;
struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
struct amdgpu_bo **smu_buf = &adev->smu.smu_buf;
void *toc_buf_ptr = NULL;
void *smu_buf_ptr = NULL;
struct cz_smu_private_data *priv =
kzalloc(sizeof(struct cz_smu_private_data), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
/* allocate firmware buffers */
if (adev->firmware.smu_load)
amdgpu_ucode_init_bo(adev);
adev->smu.priv = priv;
adev->smu.fw_flags = 0;
priv->toc_buffer.data_size = 4096;
priv->smu_buffer.data_size =
ALIGN(UCODE_ID_RLC_SCRATCH_SIZE_BYTE, 32) +
ALIGN(UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE, 32) +
ALIGN(UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE, 32) +
ALIGN(sizeof(struct SMU8_MultimediaPowerLogData), 32) +
ALIGN(sizeof(struct SMU8_Fusion_ClkTable), 32);
/* prepare toc buffer and smu buffer:
* 1. create amdgpu_bo for toc buffer and smu buffer
* 2. pin mc address
* 3. map kernel virtual address
*/
ret = amdgpu_bo_create(adev, priv->toc_buffer.data_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, toc_buf);
if (ret) {
dev_err(adev->dev, "(%d) SMC TOC buffer allocation failed\n", ret);
return ret;
}
ret = amdgpu_bo_create(adev, priv->smu_buffer.data_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, smu_buf);
if (ret) {
dev_err(adev->dev, "(%d) SMC Internal buffer allocation failed\n", ret);
return ret;
}
/* toc buffer reserve/pin/map */
ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.toc_buf);
dev_err(adev->dev, "(%d) SMC TOC buffer reserve failed\n", ret);
return ret;
}
ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_GTT, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
dev_err(adev->dev, "(%d) SMC TOC buffer pin failed\n", ret);
return ret;
}
ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
if (ret)
goto smu_init_failed;
amdgpu_bo_unreserve(adev->smu.toc_buf);
priv->toc_buffer.mc_addr_low = lower_32_bits(mc_addr);
priv->toc_buffer.mc_addr_high = upper_32_bits(mc_addr);
priv->toc_buffer.kaddr = toc_buf_ptr;
/* smu buffer reserve/pin/map */
ret = amdgpu_bo_reserve(adev->smu.smu_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.smu_buf);
dev_err(adev->dev, "(%d) SMC Internal buffer reserve failed\n", ret);
return ret;
}
ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_GTT, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
dev_err(adev->dev, "(%d) SMC Internal buffer pin failed\n", ret);
return ret;
}
ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr);
if (ret)
goto smu_init_failed;
amdgpu_bo_unreserve(adev->smu.smu_buf);
priv->smu_buffer.mc_addr_low = lower_32_bits(mc_addr);
priv->smu_buffer.mc_addr_high = upper_32_bits(mc_addr);
priv->smu_buffer.kaddr = smu_buf_ptr;
if (adev->firmware.smu_load) {
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_firmware_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G,
&priv->driver_buffer[priv->driver_buffer_length++]))
goto smu_init_failed;
}
if (cz_smu_populate_single_scratch_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
UCODE_ID_RLC_SCRATCH_SIZE_BYTE,
&priv->scratch_buffer[priv->scratch_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_scratch_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE,
&priv->scratch_buffer[priv->scratch_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_scratch_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE,
&priv->scratch_buffer[priv->scratch_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_scratch_entry(adev,
CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
sizeof(struct SMU8_MultimediaPowerLogData),
&priv->scratch_buffer[priv->scratch_buffer_length++]))
goto smu_init_failed;
if (cz_smu_populate_single_scratch_entry(adev,
CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE,
sizeof(struct SMU8_Fusion_ClkTable),
&priv->scratch_buffer[priv->scratch_buffer_length++]))
goto smu_init_failed;
cz_smu_initialize_toc_empty_job_list(adev);
cz_smu_construct_toc_for_rlc_aram_save(adev);
cz_smu_construct_toc_for_vddgfx_enter(adev);
cz_smu_construct_toc_for_vddgfx_exit(adev);
cz_smu_construct_toc_for_power_profiling(adev);
cz_smu_construct_toc_for_bootup(adev);
cz_smu_construct_toc_for_clock_table(adev);
/* init the smumgr functions */
adev->smu.smumgr_funcs = &cz_smumgr_funcs;
return 0;
smu_init_failed:
amdgpu_bo_unref(toc_buf);
amdgpu_bo_unref(smu_buf);
return ret;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __CZ_SMC_H__
#define __CZ_SMC_H__
#define MAX_NUM_FIRMWARE 8
#define MAX_NUM_SCRATCH 11
#define CZ_SCRATCH_SIZE_NONGFX_CLOCKGATING 1024
#define CZ_SCRATCH_SIZE_NONGFX_GOLDENSETTING 2048
#define CZ_SCRATCH_SIZE_SDMA_METADATA 1024
#define CZ_SCRATCH_SIZE_IH ((2*256+1)*4)
enum cz_scratch_entry {
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0 = 0,
CZ_SCRATCH_ENTRY_UCODE_ID_SDMA1,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_CE,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_PFP,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_ME,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1,
CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT2,
CZ_SCRATCH_ENTRY_UCODE_ID_GMCON_RENG,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_G,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SCRATCH,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_ARAM,
CZ_SCRATCH_ENTRY_UCODE_ID_RLC_SRM_DRAM,
CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_ERAM,
CZ_SCRATCH_ENTRY_UCODE_ID_DMCU_IRAM,
CZ_SCRATCH_ENTRY_UCODE_ID_POWER_PROFILING,
CZ_SCRATCH_ENTRY_DATA_ID_SDMA_HALT,
CZ_SCRATCH_ENTRY_DATA_ID_SYS_CLOCKGATING,
CZ_SCRATCH_ENTRY_DATA_ID_SDMA_RING_REGS,
CZ_SCRATCH_ENTRY_DATA_ID_NONGFX_REINIT,
CZ_SCRATCH_ENTRY_DATA_ID_SDMA_START,
CZ_SCRATCH_ENTRY_DATA_ID_IH_REGISTERS,
CZ_SCRATCH_ENTRY_SMU8_FUSION_CLKTABLE
};
struct cz_buffer_entry {
uint32_t data_size;
uint32_t mc_addr_low;
uint32_t mc_addr_high;
void *kaddr;
enum cz_scratch_entry firmware_ID;
};
struct cz_register_index_data_pair {
uint32_t offset;
uint32_t value;
};
struct cz_ih_meta_data {
uint32_t command;
struct cz_register_index_data_pair register_index_value_pair[1];
};
struct cz_smu_private_data {
uint8_t driver_buffer_length;
uint8_t scratch_buffer_length;
uint16_t toc_entry_used_count;
uint16_t toc_entry_initialize_index;
uint16_t toc_entry_power_profiling_index;
uint16_t toc_entry_aram;
uint16_t toc_entry_ih_register_restore_task_index;
uint16_t toc_entry_clock_table;
uint16_t ih_register_restore_task_size;
uint16_t smu_buffer_used_bytes;
struct cz_buffer_entry toc_buffer;
struct cz_buffer_entry smu_buffer;
struct cz_buffer_entry driver_buffer[MAX_NUM_FIRMWARE];
struct cz_buffer_entry scratch_buffer[MAX_NUM_SCRATCH];
};
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __DCE_V10_0_H__
#define __DCE_V10_0_H__
extern const struct amdgpu_ip_funcs dce_v10_0_ip_funcs;
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __DCE_V11_0_H__
#define __DCE_V11_0_H__
extern const struct amdgpu_ip_funcs dce_v11_0_ip_funcs;
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __GFX_V8_0_H__
#define __GFX_V8_0_H__
extern const struct amdgpu_ip_funcs gfx_v8_0_ip_funcs;
uint64_t gfx_v8_0_get_gpu_clock_counter(struct amdgpu_device *adev);
void gfx_v8_0_select_se_sh(struct amdgpu_device *adev, u32 se_num, u32 sh_num);
int gfx_v8_0_get_cu_info(struct amdgpu_device *adev, struct amdgpu_cu_info *cu_info);
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "gmc_v8_0.h"
#include "amdgpu_ucode.h"
#include "gmc/gmc_8_1_d.h"
#include "gmc/gmc_8_1_sh_mask.h"
#include "bif/bif_5_0_d.h"
#include "bif/bif_5_0_sh_mask.h"
#include "oss/oss_3_0_d.h"
#include "oss/oss_3_0_sh_mask.h"
#include "vid.h"
#include "vi.h"
static void gmc_v8_0_set_gart_funcs(struct amdgpu_device *adev);
static void gmc_v8_0_set_irq_funcs(struct amdgpu_device *adev);
MODULE_FIRMWARE("radeon/topaz_mc.bin");
MODULE_FIRMWARE("radeon/tonga_mc.bin");
static const u32 golden_settings_tonga_a11[] =
{
mmMC_ARB_WTM_GRPWT_RD, 0x00000003, 0x00000000,
mmMC_HUB_RDREQ_DMIF_LIMIT, 0x0000007f, 0x00000028,
mmMC_HUB_WDP_UMC, 0x00007fb6, 0x00000991,
mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff,
};
static const u32 tonga_mgcg_cgcg_init[] =
{
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
static const u32 golden_settings_iceland_a11[] =
{
mmVM_PRT_APERTURE0_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE1_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE2_LOW_ADDR, 0x0fffffff, 0x0fffffff,
mmVM_PRT_APERTURE3_LOW_ADDR, 0x0fffffff, 0x0fffffff
};
static const u32 iceland_mgcg_cgcg_init[] =
{
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
static const u32 cz_mgcg_cgcg_init[] =
{
mmMC_MEM_POWER_LS, 0xffffffff, 0x00000104
};
static void gmc_v8_0_init_golden_registers(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_TOPAZ:
amdgpu_program_register_sequence(adev,
iceland_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(iceland_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_iceland_a11,
(const u32)ARRAY_SIZE(golden_settings_iceland_a11));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(tonga_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_tonga_a11,
(const u32)ARRAY_SIZE(golden_settings_tonga_a11));
break;
case CHIP_CARRIZO:
amdgpu_program_register_sequence(adev,
cz_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(cz_mgcg_cgcg_init));
break;
default:
break;
}
}
/**
* gmc8_mc_wait_for_idle - wait for MC idle callback.
*
* @adev: amdgpu_device pointer
*
* Wait for the MC (memory controller) to be idle.
* (evergreen+).
* Returns 0 if the MC is idle, -1 if not.
*/
int gmc_v8_0_mc_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
/* read MC_STATUS */
tmp = RREG32(mmSRBM_STATUS) & (SRBM_STATUS__VMC_BUSY_MASK |
SRBM_STATUS__MCB_BUSY_MASK |
SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK |
SRBM_STATUS__MCD_BUSY_MASK |
SRBM_STATUS__VMC1_BUSY_MASK);
if (!tmp)
return 0;
udelay(1);
}
return -1;
}
void gmc_v8_0_mc_stop(struct amdgpu_device *adev,
struct amdgpu_mode_mc_save *save)
{
u32 blackout;
if (adev->mode_info.num_crtc)
amdgpu_display_stop_mc_access(adev, save);
amdgpu_asic_wait_for_mc_idle(adev);
blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
if (REG_GET_FIELD(blackout, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE) != 1) {
/* Block CPU access */
WREG32(mmBIF_FB_EN, 0);
/* blackout the MC */
blackout = REG_SET_FIELD(blackout,
MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 1);
WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout);
}
/* wait for the MC to settle */
udelay(100);
}
void gmc_v8_0_mc_resume(struct amdgpu_device *adev,
struct amdgpu_mode_mc_save *save)
{
u32 tmp;
/* unblackout the MC */
tmp = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
tmp = REG_SET_FIELD(tmp, MC_SHARED_BLACKOUT_CNTL, BLACKOUT_MODE, 0);
WREG32(mmMC_SHARED_BLACKOUT_CNTL, tmp);
/* allow CPU access */
tmp = REG_SET_FIELD(0, BIF_FB_EN, FB_READ_EN, 1);
tmp = REG_SET_FIELD(tmp, BIF_FB_EN, FB_WRITE_EN, 1);
WREG32(mmBIF_FB_EN, tmp);
if (adev->mode_info.num_crtc)
amdgpu_display_resume_mc_access(adev, save);
}
/**
* gmc_v8_0_init_microcode - load ucode images from disk
*
* @adev: amdgpu_device pointer
*
* Use the firmware interface to load the ucode images into
* the driver (not loaded into hw).
* Returns 0 on success, error on failure.
*/
static int gmc_v8_0_init_microcode(struct amdgpu_device *adev)
{
const char *chip_name;
char fw_name[30];
int err;
DRM_DEBUG("\n");
switch (adev->asic_type) {
case CHIP_TOPAZ:
chip_name = "topaz";
break;
case CHIP_TONGA:
chip_name = "tonga";
break;
case CHIP_CARRIZO:
return 0;
default: BUG();
}
snprintf(fw_name, sizeof(fw_name), "radeon/%s_mc.bin", chip_name);
err = request_firmware(&adev->mc.fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->mc.fw);
out:
if (err) {
printk(KERN_ERR
"mc: Failed to load firmware \"%s\"\n",
fw_name);
release_firmware(adev->mc.fw);
adev->mc.fw = NULL;
}
return err;
}
/**
* gmc_v8_0_mc_load_microcode - load MC ucode into the hw
*
* @adev: amdgpu_device pointer
*
* Load the GDDR MC ucode into the hw (CIK).
* Returns 0 on success, error on failure.
*/
static int gmc_v8_0_mc_load_microcode(struct amdgpu_device *adev)
{
const struct mc_firmware_header_v1_0 *hdr;
const __le32 *fw_data = NULL;
const __le32 *io_mc_regs = NULL;
u32 running, blackout = 0;
int i, ucode_size, regs_size;
if (!adev->mc.fw)
return -EINVAL;
hdr = (const struct mc_firmware_header_v1_0 *)adev->mc.fw->data;
amdgpu_ucode_print_mc_hdr(&hdr->header);
adev->mc.fw_version = le32_to_cpu(hdr->header.ucode_version);
regs_size = le32_to_cpu(hdr->io_debug_size_bytes) / (4 * 2);
io_mc_regs = (const __le32 *)
(adev->mc.fw->data + le32_to_cpu(hdr->io_debug_array_offset_bytes));
ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
fw_data = (const __le32 *)
(adev->mc.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
running = REG_GET_FIELD(RREG32(mmMC_SEQ_SUP_CNTL), MC_SEQ_SUP_CNTL, RUN);
if (running == 0) {
if (running) {
blackout = RREG32(mmMC_SHARED_BLACKOUT_CNTL);
WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout | 1);
}
/* reset the engine and set to writable */
WREG32(mmMC_SEQ_SUP_CNTL, 0x00000008);
WREG32(mmMC_SEQ_SUP_CNTL, 0x00000010);
/* load mc io regs */
for (i = 0; i < regs_size; i++) {
WREG32(mmMC_SEQ_IO_DEBUG_INDEX, le32_to_cpup(io_mc_regs++));
WREG32(mmMC_SEQ_IO_DEBUG_DATA, le32_to_cpup(io_mc_regs++));
}
/* load the MC ucode */
for (i = 0; i < ucode_size; i++)
WREG32(mmMC_SEQ_SUP_PGM, le32_to_cpup(fw_data++));
/* put the engine back into the active state */
WREG32(mmMC_SEQ_SUP_CNTL, 0x00000008);
WREG32(mmMC_SEQ_SUP_CNTL, 0x00000004);
WREG32(mmMC_SEQ_SUP_CNTL, 0x00000001);
/* wait for training to complete */
for (i = 0; i < adev->usec_timeout; i++) {
if (REG_GET_FIELD(RREG32(mmMC_SEQ_TRAIN_WAKEUP_CNTL),
MC_SEQ_TRAIN_WAKEUP_CNTL, TRAIN_DONE_D0))
break;
udelay(1);
}
for (i = 0; i < adev->usec_timeout; i++) {
if (REG_GET_FIELD(RREG32(mmMC_SEQ_TRAIN_WAKEUP_CNTL),
MC_SEQ_TRAIN_WAKEUP_CNTL, TRAIN_DONE_D1))
break;
udelay(1);
}
if (running)
WREG32(mmMC_SHARED_BLACKOUT_CNTL, blackout);
}
return 0;
}
static void gmc_v8_0_vram_gtt_location(struct amdgpu_device *adev,
struct amdgpu_mc *mc)
{
if (mc->mc_vram_size > 0xFFC0000000ULL) {
/* leave room for at least 1024M GTT */
dev_warn(adev->dev, "limiting VRAM\n");
mc->real_vram_size = 0xFFC0000000ULL;
mc->mc_vram_size = 0xFFC0000000ULL;
}
amdgpu_vram_location(adev, &adev->mc, 0);
adev->mc.gtt_base_align = 0;
amdgpu_gtt_location(adev, mc);
}
/**
* gmc_v8_0_mc_program - program the GPU memory controller
*
* @adev: amdgpu_device pointer
*
* Set the location of vram, gart, and AGP in the GPU's
* physical address space (CIK).
*/
static void gmc_v8_0_mc_program(struct amdgpu_device *adev)
{
struct amdgpu_mode_mc_save save;
u32 tmp;
int i, j;
/* Initialize HDP */
for (i = 0, j = 0; i < 32; i++, j += 0x6) {
WREG32((0xb05 + j), 0x00000000);
WREG32((0xb06 + j), 0x00000000);
WREG32((0xb07 + j), 0x00000000);
WREG32((0xb08 + j), 0x00000000);
WREG32((0xb09 + j), 0x00000000);
}
WREG32(mmHDP_REG_COHERENCY_FLUSH_CNTL, 0);
if (adev->mode_info.num_crtc)
amdgpu_display_set_vga_render_state(adev, false);
gmc_v8_0_mc_stop(adev, &save);
if (amdgpu_asic_wait_for_mc_idle(adev)) {
dev_warn(adev->dev, "Wait for MC idle timedout !\n");
}
/* Update configuration */
WREG32(mmMC_VM_SYSTEM_APERTURE_LOW_ADDR,
adev->mc.vram_start >> 12);
WREG32(mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR,
adev->mc.vram_end >> 12);
WREG32(mmMC_VM_SYSTEM_APERTURE_DEFAULT_ADDR,
adev->vram_scratch.gpu_addr >> 12);
tmp = ((adev->mc.vram_end >> 24) & 0xFFFF) << 16;
tmp |= ((adev->mc.vram_start >> 24) & 0xFFFF);
WREG32(mmMC_VM_FB_LOCATION, tmp);
/* XXX double check these! */
WREG32(mmHDP_NONSURFACE_BASE, (adev->mc.vram_start >> 8));
WREG32(mmHDP_NONSURFACE_INFO, (2 << 7) | (1 << 30));
WREG32(mmHDP_NONSURFACE_SIZE, 0x3FFFFFFF);
WREG32(mmMC_VM_AGP_BASE, 0);
WREG32(mmMC_VM_AGP_TOP, 0x0FFFFFFF);
WREG32(mmMC_VM_AGP_BOT, 0x0FFFFFFF);
if (amdgpu_asic_wait_for_mc_idle(adev)) {
dev_warn(adev->dev, "Wait for MC idle timedout !\n");
}
gmc_v8_0_mc_resume(adev, &save);
WREG32(mmBIF_FB_EN, BIF_FB_EN__FB_READ_EN_MASK | BIF_FB_EN__FB_WRITE_EN_MASK);
tmp = RREG32(mmHDP_MISC_CNTL);
tmp = REG_SET_FIELD(tmp, HDP_MISC_CNTL, FLUSH_INVALIDATE_CACHE, 1);
WREG32(mmHDP_MISC_CNTL, tmp);
tmp = RREG32(mmHDP_HOST_PATH_CNTL);
WREG32(mmHDP_HOST_PATH_CNTL, tmp);
}
/**
* gmc_v8_0_mc_init - initialize the memory controller driver params
*
* @adev: amdgpu_device pointer
*
* Look up the amount of vram, vram width, and decide how to place
* vram and gart within the GPU's physical address space (CIK).
* Returns 0 for success.
*/
static int gmc_v8_0_mc_init(struct amdgpu_device *adev)
{
u32 tmp;
int chansize, numchan;
/* Get VRAM informations */
tmp = RREG32(mmMC_ARB_RAMCFG);
if (REG_GET_FIELD(tmp, MC_ARB_RAMCFG, CHANSIZE)) {
chansize = 64;
} else {
chansize = 32;
}
tmp = RREG32(mmMC_SHARED_CHMAP);
switch (REG_GET_FIELD(tmp, MC_SHARED_CHMAP, NOOFCHAN)) {
case 0:
default:
numchan = 1;
break;
case 1:
numchan = 2;
break;
case 2:
numchan = 4;
break;
case 3:
numchan = 8;
break;
case 4:
numchan = 3;
break;
case 5:
numchan = 6;
break;
case 6:
numchan = 10;
break;
case 7:
numchan = 12;
break;
case 8:
numchan = 16;
break;
}
adev->mc.vram_width = numchan * chansize;
/* Could aper size report 0 ? */
adev->mc.aper_base = pci_resource_start(adev->pdev, 0);
adev->mc.aper_size = pci_resource_len(adev->pdev, 0);
/* size in MB on si */
adev->mc.mc_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL;
adev->mc.real_vram_size = RREG32(mmCONFIG_MEMSIZE) * 1024ULL * 1024ULL;
adev->mc.visible_vram_size = adev->mc.aper_size;
/* unless the user had overridden it, set the gart
* size equal to the 1024 or vram, whichever is larger.
*/
if (amdgpu_gart_size == -1)
adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size);
else
adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20;
gmc_v8_0_vram_gtt_location(adev, &adev->mc);
return 0;
}
/*
* GART
* VMID 0 is the physical GPU addresses as used by the kernel.
* VMIDs 1-15 are used for userspace clients and are handled
* by the amdgpu vm/hsa code.
*/
/**
* gmc_v8_0_gart_flush_gpu_tlb - gart tlb flush callback
*
* @adev: amdgpu_device pointer
* @vmid: vm instance to flush
*
* Flush the TLB for the requested page table (CIK).
*/
static void gmc_v8_0_gart_flush_gpu_tlb(struct amdgpu_device *adev,
uint32_t vmid)
{
/* flush hdp cache */
WREG32(mmHDP_MEM_COHERENCY_FLUSH_CNTL, 0);
/* bits 0-15 are the VM contexts0-15 */
WREG32(mmVM_INVALIDATE_REQUEST, 1 << vmid);
}
/**
* gmc_v8_0_gart_set_pte_pde - update the page tables using MMIO
*
* @adev: amdgpu_device pointer
* @cpu_pt_addr: cpu address of the page table
* @gpu_page_idx: entry in the page table to update
* @addr: dst addr to write into pte/pde
* @flags: access flags
*
* Update the page tables using the CPU.
*/
static int gmc_v8_0_gart_set_pte_pde(struct amdgpu_device *adev,
void *cpu_pt_addr,
uint32_t gpu_page_idx,
uint64_t addr,
uint32_t flags)
{
void __iomem *ptr = (void *)cpu_pt_addr;
uint64_t value;
/*
* PTE format on VI:
* 63:40 reserved
* 39:12 4k physical page base address
* 11:7 fragment
* 6 write
* 5 read
* 4 exe
* 3 reserved
* 2 snooped
* 1 system
* 0 valid
*
* PDE format on VI:
* 63:59 block fragment size
* 58:40 reserved
* 39:1 physical base address of PTE
* bits 5:1 must be 0.
* 0 valid
*/
value = addr & 0x000000FFFFFFF000ULL;
value |= flags;
writeq(value, ptr + (gpu_page_idx * 8));
return 0;
}
/**
* gmc_v8_0_gart_enable - gart enable
*
* @adev: amdgpu_device pointer
*
* This sets up the TLBs, programs the page tables for VMID0,
* sets up the hw for VMIDs 1-15 which are allocated on
* demand, and sets up the global locations for the LDS, GDS,
* and GPUVM for FSA64 clients (CIK).
* Returns 0 for success, errors for failure.
*/
static int gmc_v8_0_gart_enable(struct amdgpu_device *adev)
{
int r, i;
u32 tmp;
if (adev->gart.robj == NULL) {
dev_err(adev->dev, "No VRAM object for PCIE GART.\n");
return -EINVAL;
}
r = amdgpu_gart_table_vram_pin(adev);
if (r)
return r;
/* Setup TLB control */
tmp = RREG32(mmMC_VM_MX_L1_TLB_CNTL);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_L1_TLB, 1);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_L1_FRAGMENT_PROCESSING, 1);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, SYSTEM_ACCESS_MODE, 3);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_ADVANCED_DRIVER_MODEL, 1);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, SYSTEM_APERTURE_UNMAPPED_ACCESS, 0);
WREG32(mmMC_VM_MX_L1_TLB_CNTL, tmp);
/* Setup L2 cache */
tmp = RREG32(mmVM_L2_CNTL);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_CACHE, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_FRAGMENT_PROCESSING, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_PTE_CACHE_LRU_UPDATE_BY_WRITE, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_PDE0_CACHE_LRU_UPDATE_BY_WRITE, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, EFFECTIVE_L2_QUEUE_SIZE, 7);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, CONTEXT1_IDENTITY_ACCESS_MODE, 1);
WREG32(mmVM_L2_CNTL, tmp);
tmp = RREG32(mmVM_L2_CNTL2);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL2, INVALIDATE_ALL_L1_TLBS, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL2, INVALIDATE_L2_CACHE, 1);
WREG32(mmVM_L2_CNTL2, tmp);
tmp = RREG32(mmVM_L2_CNTL3);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL3, L2_CACHE_BIGK_ASSOCIATIVITY, 1);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL3, BANK_SELECT, 4);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL3, L2_CACHE_BIGK_FRAGMENT_SIZE, 4);
WREG32(mmVM_L2_CNTL3, tmp);
/* XXX: set to enable PTE/PDE in system memory */
tmp = RREG32(mmVM_L2_CNTL4);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PDE_REQUEST_PHYSICAL, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PDE_REQUEST_SHARED, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PDE_REQUEST_SNOOP, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PTE_REQUEST_PHYSICAL, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PTE_REQUEST_SHARED, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT0_PTE_REQUEST_SNOOP, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PDE_REQUEST_PHYSICAL, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PDE_REQUEST_SHARED, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PDE_REQUEST_SNOOP, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PTE_REQUEST_PHYSICAL, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PTE_REQUEST_SHARED, 0);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL4, VMC_TAP_CONTEXT1_PTE_REQUEST_SNOOP, 0);
WREG32(mmVM_L2_CNTL4, tmp);
/* setup context0 */
WREG32(mmVM_CONTEXT0_PAGE_TABLE_START_ADDR, adev->mc.gtt_start >> 12);
WREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR, adev->mc.gtt_end >> 12);
WREG32(mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR, adev->gart.table_addr >> 12);
WREG32(mmVM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR,
(u32)(adev->dummy_page.addr >> 12));
WREG32(mmVM_CONTEXT0_CNTL2, 0);
tmp = RREG32(mmVM_CONTEXT0_CNTL);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT0_CNTL, ENABLE_CONTEXT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT0_CNTL, PAGE_TABLE_DEPTH, 0);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT0_CNTL, RANGE_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
WREG32(mmVM_CONTEXT0_CNTL, tmp);
WREG32(mmVM_L2_CONTEXT1_IDENTITY_APERTURE_LOW_ADDR, 0);
WREG32(mmVM_L2_CONTEXT1_IDENTITY_APERTURE_HIGH_ADDR, 0);
WREG32(mmVM_L2_CONTEXT_IDENTITY_PHYSICAL_OFFSET, 0);
/* empty context1-15 */
/* FIXME start with 4G, once using 2 level pt switch to full
* vm size space
*/
/* set vm size, must be a multiple of 4 */
WREG32(mmVM_CONTEXT1_PAGE_TABLE_START_ADDR, 0);
WREG32(mmVM_CONTEXT1_PAGE_TABLE_END_ADDR, adev->vm_manager.max_pfn);
for (i = 1; i < 16; i++) {
if (i < 8)
WREG32(mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR + i,
adev->gart.table_addr >> 12);
else
WREG32(mmVM_CONTEXT8_PAGE_TABLE_BASE_ADDR + i - 8,
adev->gart.table_addr >> 12);
}
/* enable context1-15 */
WREG32(mmVM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR,
(u32)(adev->dummy_page.addr >> 12));
WREG32(mmVM_CONTEXT1_CNTL2, 4);
tmp = RREG32(mmVM_CONTEXT1_CNTL);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, ENABLE_CONTEXT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, PAGE_TABLE_DEPTH, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, RANGE_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, DUMMY_PAGE_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, PDE0_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, VALID_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, VALID_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, READ_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, READ_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, WRITE_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_INTERRUPT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, EXECUTE_PROTECTION_FAULT_ENABLE_DEFAULT, 1);
tmp = REG_SET_FIELD(tmp, VM_CONTEXT1_CNTL, PAGE_TABLE_BLOCK_SIZE,
amdgpu_vm_block_size - 9);
WREG32(mmVM_CONTEXT1_CNTL, tmp);
gmc_v8_0_gart_flush_gpu_tlb(adev, 0);
DRM_INFO("PCIE GART of %uM enabled (table at 0x%016llX).\n",
(unsigned)(adev->mc.gtt_size >> 20),
(unsigned long long)adev->gart.table_addr);
adev->gart.ready = true;
return 0;
}
static int gmc_v8_0_gart_init(struct amdgpu_device *adev)
{
int r;
if (adev->gart.robj) {
WARN(1, "R600 PCIE GART already initialized\n");
return 0;
}
/* Initialize common gart structure */
r = amdgpu_gart_init(adev);
if (r)
return r;
adev->gart.table_size = adev->gart.num_gpu_pages * 8;
return amdgpu_gart_table_vram_alloc(adev);
}
/**
* gmc_v8_0_gart_disable - gart disable
*
* @adev: amdgpu_device pointer
*
* This disables all VM page table (CIK).
*/
static void gmc_v8_0_gart_disable(struct amdgpu_device *adev)
{
u32 tmp;
/* Disable all tables */
WREG32(mmVM_CONTEXT0_CNTL, 0);
WREG32(mmVM_CONTEXT1_CNTL, 0);
/* Setup TLB control */
tmp = RREG32(mmMC_VM_MX_L1_TLB_CNTL);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_L1_TLB, 0);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_L1_FRAGMENT_PROCESSING, 0);
tmp = REG_SET_FIELD(tmp, MC_VM_MX_L1_TLB_CNTL, ENABLE_ADVANCED_DRIVER_MODEL, 0);
WREG32(mmMC_VM_MX_L1_TLB_CNTL, tmp);
/* Setup L2 cache */
tmp = RREG32(mmVM_L2_CNTL);
tmp = REG_SET_FIELD(tmp, VM_L2_CNTL, ENABLE_L2_CACHE, 0);
WREG32(mmVM_L2_CNTL, tmp);
WREG32(mmVM_L2_CNTL2, 0);
amdgpu_gart_table_vram_unpin(adev);
}
/**
* gmc_v8_0_gart_fini - vm fini callback
*
* @adev: amdgpu_device pointer
*
* Tears down the driver GART/VM setup (CIK).
*/
static void gmc_v8_0_gart_fini(struct amdgpu_device *adev)
{
amdgpu_gart_table_vram_free(adev);
amdgpu_gart_fini(adev);
}
/*
* vm
* VMID 0 is the physical GPU addresses as used by the kernel.
* VMIDs 1-15 are used for userspace clients and are handled
* by the amdgpu vm/hsa code.
*/
/**
* gmc_v8_0_vm_init - cik vm init callback
*
* @adev: amdgpu_device pointer
*
* Inits cik specific vm parameters (number of VMs, base of vram for
* VMIDs 1-15) (CIK).
* Returns 0 for success.
*/
static int gmc_v8_0_vm_init(struct amdgpu_device *adev)
{
/*
* number of VMs
* VMID 0 is reserved for System
* amdgpu graphics/compute will use VMIDs 1-7
* amdkfd will use VMIDs 8-15
*/
adev->vm_manager.nvm = AMDGPU_NUM_OF_VMIDS;
/* base offset of vram pages */
if (adev->flags & AMDGPU_IS_APU) {
u64 tmp = RREG32(mmMC_VM_FB_OFFSET);
tmp <<= 22;
adev->vm_manager.vram_base_offset = tmp;
} else
adev->vm_manager.vram_base_offset = 0;
return 0;
}
/**
* gmc_v8_0_vm_fini - cik vm fini callback
*
* @adev: amdgpu_device pointer
*
* Tear down any asic specific VM setup (CIK).
*/
static void gmc_v8_0_vm_fini(struct amdgpu_device *adev)
{
}
/**
* gmc_v8_0_vm_decode_fault - print human readable fault info
*
* @adev: amdgpu_device pointer
* @status: VM_CONTEXT1_PROTECTION_FAULT_STATUS register value
* @addr: VM_CONTEXT1_PROTECTION_FAULT_ADDR register value
*
* Print human readable fault information (CIK).
*/
static void gmc_v8_0_vm_decode_fault(struct amdgpu_device *adev,
u32 status, u32 addr, u32 mc_client)
{
u32 mc_id;
u32 vmid = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS, VMID);
u32 protections = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
PROTECTIONS);
char block[5] = { mc_client >> 24, (mc_client >> 16) & 0xff,
(mc_client >> 8) & 0xff, mc_client & 0xff, 0 };
mc_id = REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
MEMORY_CLIENT_ID);
printk("VM fault (0x%02x, vmid %d) at page %u, %s from '%s' (0x%08x) (%d)\n",
protections, vmid, addr,
REG_GET_FIELD(status, VM_CONTEXT1_PROTECTION_FAULT_STATUS,
MEMORY_CLIENT_RW) ?
"write" : "read", block, mc_client, mc_id);
}
static int gmc_v8_0_early_init(struct amdgpu_device *adev)
{
gmc_v8_0_set_gart_funcs(adev);
gmc_v8_0_set_irq_funcs(adev);
if (adev->flags & AMDGPU_IS_APU) {
adev->mc.is_gddr5 = false;
} else {
u32 tmp = RREG32(mmMC_SEQ_MISC0);
if (((tmp & MC_SEQ_MISC0__GDDR5_MASK) >>
MC_SEQ_MISC0__GDDR5__SHIFT) == MC_SEQ_MISC0__GDDR5_VALUE)
adev->mc.is_gddr5 = true;
else
adev->mc.is_gddr5 = false;
}
return 0;
}
static int gmc_v8_0_sw_init(struct amdgpu_device *adev)
{
int r;
int dma_bits;
r = amdgpu_gem_init(adev);
if (r)
return r;
r = amdgpu_irq_add_id(adev, 146, &adev->mc.vm_fault);
if (r)
return r;
r = amdgpu_irq_add_id(adev, 147, &adev->mc.vm_fault);
if (r)
return r;
/* Adjust VM size here.
* Currently set to 4GB ((1 << 20) 4k pages).
* Max GPUVM size for cayman and SI is 40 bits.
*/
adev->vm_manager.max_pfn = amdgpu_vm_size << 18;
/* Set the internal MC address mask
* This is the max address of the GPU's
* internal address space.
*/
adev->mc.mc_mask = 0xffffffffffULL; /* 40 bit MC */
/* set DMA mask + need_dma32 flags.
* PCIE - can handle 40-bits.
* IGP - can handle 40-bits
* PCI - dma32 for legacy pci gart, 40 bits on newer asics
*/
adev->need_dma32 = false;
dma_bits = adev->need_dma32 ? 32 : 40;
r = pci_set_dma_mask(adev->pdev, DMA_BIT_MASK(dma_bits));
if (r) {
adev->need_dma32 = true;
dma_bits = 32;
printk(KERN_WARNING "amdgpu: No suitable DMA available.\n");
}
r = pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(dma_bits));
if (r) {
pci_set_consistent_dma_mask(adev->pdev, DMA_BIT_MASK(32));
printk(KERN_WARNING "amdgpu: No coherent DMA available.\n");
}
r = gmc_v8_0_init_microcode(adev);
if (r) {
DRM_ERROR("Failed to load mc firmware!\n");
return r;
}
r = gmc_v8_0_mc_init(adev);
if (r)
return r;
/* Memory manager */
r = amdgpu_bo_init(adev);
if (r)
return r;
r = gmc_v8_0_gart_init(adev);
if (r)
return r;
if (!adev->vm_manager.enabled) {
r = gmc_v8_0_vm_init(adev);
if (r) {
dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
return r;
}
adev->vm_manager.enabled = true;
}
return r;
}
static int gmc_v8_0_sw_fini(struct amdgpu_device *adev)
{
int i;
if (adev->vm_manager.enabled) {
for (i = 0; i < AMDGPU_NUM_VM; ++i)
amdgpu_fence_unref(&adev->vm_manager.active[i]);
gmc_v8_0_vm_fini(adev);
adev->vm_manager.enabled = false;
}
gmc_v8_0_gart_fini(adev);
amdgpu_gem_fini(adev);
amdgpu_bo_fini(adev);
return 0;
}
static int gmc_v8_0_hw_init(struct amdgpu_device *adev)
{
int r;
gmc_v8_0_init_golden_registers(adev);
gmc_v8_0_mc_program(adev);
if (!(adev->flags & AMDGPU_IS_APU)) {
r = gmc_v8_0_mc_load_microcode(adev);
if (r) {
DRM_ERROR("Failed to load MC firmware!\n");
return r;
}
}
r = gmc_v8_0_gart_enable(adev);
if (r)
return r;
return r;
}
static int gmc_v8_0_hw_fini(struct amdgpu_device *adev)
{
gmc_v8_0_gart_disable(adev);
return 0;
}
static int gmc_v8_0_suspend(struct amdgpu_device *adev)
{
int i;
if (adev->vm_manager.enabled) {
for (i = 0; i < AMDGPU_NUM_VM; ++i)
amdgpu_fence_unref(&adev->vm_manager.active[i]);
gmc_v8_0_vm_fini(adev);
adev->vm_manager.enabled = false;
}
gmc_v8_0_hw_fini(adev);
return 0;
}
static int gmc_v8_0_resume(struct amdgpu_device *adev)
{
int r;
r = gmc_v8_0_hw_init(adev);
if (r)
return r;
if (!adev->vm_manager.enabled) {
r = gmc_v8_0_vm_init(adev);
if (r) {
dev_err(adev->dev, "vm manager initialization failed (%d).\n", r);
return r;
}
adev->vm_manager.enabled = true;
}
return r;
}
static bool gmc_v8_0_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS);
if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK | SRBM_STATUS__VMC_BUSY_MASK))
return false;
return true;
}
static int gmc_v8_0_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
/* read MC_STATUS */
tmp = RREG32(mmSRBM_STATUS) & (SRBM_STATUS__MCB_BUSY_MASK |
SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK |
SRBM_STATUS__MCD_BUSY_MASK |
SRBM_STATUS__VMC_BUSY_MASK |
SRBM_STATUS__VMC1_BUSY_MASK);
if (!tmp)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void gmc_v8_0_print_status(struct amdgpu_device *adev)
{
int i, j;
dev_info(adev->dev, "GMC 8.x registers\n");
dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
RREG32(mmSRBM_STATUS));
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
dev_info(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n",
RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR));
dev_info(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS));
dev_info(adev->dev, " MC_VM_MX_L1_TLB_CNTL=0x%08X\n",
RREG32(mmMC_VM_MX_L1_TLB_CNTL));
dev_info(adev->dev, " VM_L2_CNTL=0x%08X\n",
RREG32(mmVM_L2_CNTL));
dev_info(adev->dev, " VM_L2_CNTL2=0x%08X\n",
RREG32(mmVM_L2_CNTL2));
dev_info(adev->dev, " VM_L2_CNTL3=0x%08X\n",
RREG32(mmVM_L2_CNTL3));
dev_info(adev->dev, " VM_L2_CNTL4=0x%08X\n",
RREG32(mmVM_L2_CNTL4));
dev_info(adev->dev, " VM_CONTEXT0_PAGE_TABLE_START_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT0_PAGE_TABLE_START_ADDR));
dev_info(adev->dev, " VM_CONTEXT0_PAGE_TABLE_END_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT0_PAGE_TABLE_END_ADDR));
dev_info(adev->dev, " VM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT0_PROTECTION_FAULT_DEFAULT_ADDR));
dev_info(adev->dev, " VM_CONTEXT0_CNTL2=0x%08X\n",
RREG32(mmVM_CONTEXT0_CNTL2));
dev_info(adev->dev, " VM_CONTEXT0_CNTL=0x%08X\n",
RREG32(mmVM_CONTEXT0_CNTL));
dev_info(adev->dev, " VM_L2_CONTEXT1_IDENTITY_APERTURE_LOW_ADDR=0x%08X\n",
RREG32(mmVM_L2_CONTEXT1_IDENTITY_APERTURE_LOW_ADDR));
dev_info(adev->dev, " VM_L2_CONTEXT1_IDENTITY_APERTURE_HIGH_ADDR=0x%08X\n",
RREG32(mmVM_L2_CONTEXT1_IDENTITY_APERTURE_HIGH_ADDR));
dev_info(adev->dev, " mmVM_L2_CONTEXT_IDENTITY_PHYSICAL_OFFSET=0x%08X\n",
RREG32(mmVM_L2_CONTEXT_IDENTITY_PHYSICAL_OFFSET));
dev_info(adev->dev, " VM_CONTEXT1_PAGE_TABLE_START_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT1_PAGE_TABLE_START_ADDR));
dev_info(adev->dev, " VM_CONTEXT1_PAGE_TABLE_END_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT1_PAGE_TABLE_END_ADDR));
dev_info(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR=0x%08X\n",
RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_DEFAULT_ADDR));
dev_info(adev->dev, " VM_CONTEXT1_CNTL2=0x%08X\n",
RREG32(mmVM_CONTEXT1_CNTL2));
dev_info(adev->dev, " VM_CONTEXT1_CNTL=0x%08X\n",
RREG32(mmVM_CONTEXT1_CNTL));
for (i = 0; i < 16; i++) {
if (i < 8)
dev_info(adev->dev, " VM_CONTEXT%d_PAGE_TABLE_BASE_ADDR=0x%08X\n",
i, RREG32(mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR + i));
else
dev_info(adev->dev, " VM_CONTEXT%d_PAGE_TABLE_BASE_ADDR=0x%08X\n",
i, RREG32(mmVM_CONTEXT8_PAGE_TABLE_BASE_ADDR + i - 8));
}
dev_info(adev->dev, " MC_VM_SYSTEM_APERTURE_LOW_ADDR=0x%08X\n",
RREG32(mmMC_VM_SYSTEM_APERTURE_LOW_ADDR));
dev_info(adev->dev, " MC_VM_SYSTEM_APERTURE_HIGH_ADDR=0x%08X\n",
RREG32(mmMC_VM_SYSTEM_APERTURE_HIGH_ADDR));
dev_info(adev->dev, " MC_VM_SYSTEM_APERTURE_DEFAULT_ADDR=0x%08X\n",
RREG32(mmMC_VM_SYSTEM_APERTURE_DEFAULT_ADDR));
dev_info(adev->dev, " MC_VM_FB_LOCATION=0x%08X\n",
RREG32(mmMC_VM_FB_LOCATION));
dev_info(adev->dev, " MC_VM_AGP_BASE=0x%08X\n",
RREG32(mmMC_VM_AGP_BASE));
dev_info(adev->dev, " MC_VM_AGP_TOP=0x%08X\n",
RREG32(mmMC_VM_AGP_TOP));
dev_info(adev->dev, " MC_VM_AGP_BOT=0x%08X\n",
RREG32(mmMC_VM_AGP_BOT));
dev_info(adev->dev, " HDP_REG_COHERENCY_FLUSH_CNTL=0x%08X\n",
RREG32(mmHDP_REG_COHERENCY_FLUSH_CNTL));
dev_info(adev->dev, " HDP_NONSURFACE_BASE=0x%08X\n",
RREG32(mmHDP_NONSURFACE_BASE));
dev_info(adev->dev, " HDP_NONSURFACE_INFO=0x%08X\n",
RREG32(mmHDP_NONSURFACE_INFO));
dev_info(adev->dev, " HDP_NONSURFACE_SIZE=0x%08X\n",
RREG32(mmHDP_NONSURFACE_SIZE));
dev_info(adev->dev, " HDP_MISC_CNTL=0x%08X\n",
RREG32(mmHDP_MISC_CNTL));
dev_info(adev->dev, " HDP_HOST_PATH_CNTL=0x%08X\n",
RREG32(mmHDP_HOST_PATH_CNTL));
for (i = 0, j = 0; i < 32; i++, j += 0x6) {
dev_info(adev->dev, " %d:\n", i);
dev_info(adev->dev, " 0x%04X=0x%08X\n",
0xb05 + j, RREG32(0xb05 + j));
dev_info(adev->dev, " 0x%04X=0x%08X\n",
0xb06 + j, RREG32(0xb06 + j));
dev_info(adev->dev, " 0x%04X=0x%08X\n",
0xb07 + j, RREG32(0xb07 + j));
dev_info(adev->dev, " 0x%04X=0x%08X\n",
0xb08 + j, RREG32(0xb08 + j));
dev_info(adev->dev, " 0x%04X=0x%08X\n",
0xb09 + j, RREG32(0xb09 + j));
}
dev_info(adev->dev, " BIF_FB_EN=0x%08X\n",
RREG32(mmBIF_FB_EN));
}
static int gmc_v8_0_soft_reset(struct amdgpu_device *adev)
{
struct amdgpu_mode_mc_save save;
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS);
if (tmp & SRBM_STATUS__VMC_BUSY_MASK)
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
SRBM_SOFT_RESET, SOFT_RESET_VMC, 1);
if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK)) {
if (!(adev->flags & AMDGPU_IS_APU))
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset,
SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
}
if (srbm_soft_reset) {
gmc_v8_0_print_status(adev);
gmc_v8_0_mc_stop(adev, &save);
if (gmc_v8_0_wait_for_idle(adev)) {
dev_warn(adev->dev, "Wait for GMC idle timed out !\n");
}
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
gmc_v8_0_mc_resume(adev, &save);
udelay(50);
gmc_v8_0_print_status(adev);
}
return 0;
}
static int gmc_v8_0_vm_fault_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned type,
enum amdgpu_interrupt_state state)
{
u32 tmp;
u32 bits = (VM_CONTEXT1_CNTL__RANGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__DUMMY_PAGE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__PDE0_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__VALID_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__READ_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__WRITE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK |
VM_CONTEXT1_CNTL__EXECUTE_PROTECTION_FAULT_ENABLE_INTERRUPT_MASK);
switch (state) {
case AMDGPU_IRQ_STATE_DISABLE:
/* system context */
tmp = RREG32(mmVM_CONTEXT0_CNTL);
tmp &= ~bits;
WREG32(mmVM_CONTEXT0_CNTL, tmp);
/* VMs */
tmp = RREG32(mmVM_CONTEXT1_CNTL);
tmp &= ~bits;
WREG32(mmVM_CONTEXT1_CNTL, tmp);
break;
case AMDGPU_IRQ_STATE_ENABLE:
/* system context */
tmp = RREG32(mmVM_CONTEXT0_CNTL);
tmp |= bits;
WREG32(mmVM_CONTEXT0_CNTL, tmp);
/* VMs */
tmp = RREG32(mmVM_CONTEXT1_CNTL);
tmp |= bits;
WREG32(mmVM_CONTEXT1_CNTL, tmp);
break;
default:
break;
}
return 0;
}
static int gmc_v8_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
u32 addr, status, mc_client;
addr = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR);
status = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS);
mc_client = RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_MCCLIENT);
dev_err(adev->dev, "GPU fault detected: %d 0x%08x\n",
entry->src_id, entry->src_data);
dev_err(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n",
addr);
dev_err(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
status);
gmc_v8_0_vm_decode_fault(adev, status, addr, mc_client);
/* reset addr and status */
WREG32_P(mmVM_CONTEXT1_CNTL2, 1, ~1);
return 0;
}
static int gmc_v8_0_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
/* XXX handled via the smc on VI */
return 0;
}
static int gmc_v8_0_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs gmc_v8_0_ip_funcs = {
.early_init = gmc_v8_0_early_init,
.late_init = NULL,
.sw_init = gmc_v8_0_sw_init,
.sw_fini = gmc_v8_0_sw_fini,
.hw_init = gmc_v8_0_hw_init,
.hw_fini = gmc_v8_0_hw_fini,
.suspend = gmc_v8_0_suspend,
.resume = gmc_v8_0_resume,
.is_idle = gmc_v8_0_is_idle,
.wait_for_idle = gmc_v8_0_wait_for_idle,
.soft_reset = gmc_v8_0_soft_reset,
.print_status = gmc_v8_0_print_status,
.set_clockgating_state = gmc_v8_0_set_clockgating_state,
.set_powergating_state = gmc_v8_0_set_powergating_state,
};
static const struct amdgpu_gart_funcs gmc_v8_0_gart_funcs = {
.flush_gpu_tlb = gmc_v8_0_gart_flush_gpu_tlb,
.set_pte_pde = gmc_v8_0_gart_set_pte_pde,
};
static const struct amdgpu_irq_src_funcs gmc_v8_0_irq_funcs = {
.set = gmc_v8_0_vm_fault_interrupt_state,
.process = gmc_v8_0_process_interrupt,
};
static void gmc_v8_0_set_gart_funcs(struct amdgpu_device *adev)
{
if (adev->gart.gart_funcs == NULL)
adev->gart.gart_funcs = &gmc_v8_0_gart_funcs;
}
static void gmc_v8_0_set_irq_funcs(struct amdgpu_device *adev)
{
adev->mc.vm_fault.num_types = 1;
adev->mc.vm_fault.funcs = &gmc_v8_0_irq_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __GMC_V8_0_H__
#define __GMC_V8_0_H__
extern const struct amdgpu_ip_funcs gmc_v8_0_ip_funcs;
/* XXX these shouldn't be exported */
void gmc_v8_0_mc_stop(struct amdgpu_device *adev,
struct amdgpu_mode_mc_save *save);
void gmc_v8_0_mc_resume(struct amdgpu_device *adev,
struct amdgpu_mode_mc_save *save);
int gmc_v8_0_mc_wait_for_idle(struct amdgpu_device *adev);
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "iceland_smumgr.h"
MODULE_FIRMWARE("radeon/topaz_smc.bin");
static void iceland_dpm_set_funcs(struct amdgpu_device *adev);
static int iceland_dpm_early_init(struct amdgpu_device *adev)
{
iceland_dpm_set_funcs(adev);
return 0;
}
static int iceland_dpm_init_microcode(struct amdgpu_device *adev)
{
char fw_name[30] = "radeon/topaz_smc.bin";
int err;
err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->pm.fw);
out:
if (err) {
DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
release_firmware(adev->pm.fw);
adev->pm.fw = NULL;
}
return err;
}
static int iceland_dpm_sw_init(struct amdgpu_device *adev)
{
int ret;
ret = iceland_dpm_init_microcode(adev);
if (ret)
return ret;
return 0;
}
static int iceland_dpm_sw_fini(struct amdgpu_device *adev)
{
return 0;
}
static int iceland_dpm_hw_init(struct amdgpu_device *adev)
{
int ret;
mutex_lock(&adev->pm.mutex);
ret = iceland_smu_init(adev);
if (ret) {
DRM_ERROR("SMU initialization failed\n");
goto fail;
}
ret = iceland_smu_start(adev);
if (ret) {
DRM_ERROR("SMU start failed\n");
goto fail;
}
mutex_unlock(&adev->pm.mutex);
return 0;
fail:
adev->firmware.smu_load = false;
mutex_unlock(&adev->pm.mutex);
return -EINVAL;
}
static int iceland_dpm_hw_fini(struct amdgpu_device *adev)
{
mutex_lock(&adev->pm.mutex);
iceland_smu_fini(adev);
mutex_unlock(&adev->pm.mutex);
return 0;
}
static int iceland_dpm_suspend(struct amdgpu_device *adev)
{
iceland_dpm_hw_fini(adev);
return 0;
}
static int iceland_dpm_resume(struct amdgpu_device *adev)
{
iceland_dpm_hw_init(adev);
return 0;
}
static int iceland_dpm_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
return 0;
}
static int iceland_dpm_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs iceland_dpm_ip_funcs = {
.early_init = iceland_dpm_early_init,
.late_init = NULL,
.sw_init = iceland_dpm_sw_init,
.sw_fini = iceland_dpm_sw_fini,
.hw_init = iceland_dpm_hw_init,
.hw_fini = iceland_dpm_hw_fini,
.suspend = iceland_dpm_suspend,
.resume = iceland_dpm_resume,
.is_idle = NULL,
.wait_for_idle = NULL,
.soft_reset = NULL,
.print_status = NULL,
.set_clockgating_state = iceland_dpm_set_clockgating_state,
.set_powergating_state = iceland_dpm_set_powergating_state,
};
static const struct amdgpu_dpm_funcs iceland_dpm_funcs = {
.get_temperature = NULL,
.pre_set_power_state = NULL,
.set_power_state = NULL,
.post_set_power_state = NULL,
.display_configuration_changed = NULL,
.get_sclk = NULL,
.get_mclk = NULL,
.print_power_state = NULL,
.debugfs_print_current_performance_level = NULL,
.force_performance_level = NULL,
.vblank_too_short = NULL,
.powergate_uvd = NULL,
};
static void iceland_dpm_set_funcs(struct amdgpu_device *adev)
{
if (NULL == adev->pm.funcs)
adev->pm.funcs = &iceland_dpm_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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 "drmP.h"
#include "amdgpu.h"
#include "amdgpu_ih.h"
#include "vid.h"
#include "oss/oss_2_4_d.h"
#include "oss/oss_2_4_sh_mask.h"
#include "bif/bif_5_1_d.h"
#include "bif/bif_5_1_sh_mask.h"
/*
* Interrupts
* Starting with r6xx, interrupts are handled via a ring buffer.
* Ring buffers are areas of GPU accessible memory that the GPU
* writes interrupt vectors into and the host reads vectors out of.
* There is a rptr (read pointer) that determines where the
* host is currently reading, and a wptr (write pointer)
* which determines where the GPU has written. When the
* pointers are equal, the ring is idle. When the GPU
* writes vectors to the ring buffer, it increments the
* wptr. When there is an interrupt, the host then starts
* fetching commands and processing them until the pointers are
* equal again at which point it updates the rptr.
*/
static void iceland_ih_set_interrupt_funcs(struct amdgpu_device *adev);
/**
* iceland_ih_enable_interrupts - Enable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Enable the interrupt ring buffer (VI).
*/
static void iceland_ih_enable_interrupts(struct amdgpu_device *adev)
{
u32 ih_cntl = RREG32(mmIH_CNTL);
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
WREG32(mmIH_CNTL, ih_cntl);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
adev->irq.ih.enabled = true;
}
/**
* iceland_ih_disable_interrupts - Disable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Disable the interrupt ring buffer (VI).
*/
static void iceland_ih_disable_interrupts(struct amdgpu_device *adev)
{
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
u32 ih_cntl = RREG32(mmIH_CNTL);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, ENABLE_INTR, 0);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
WREG32(mmIH_CNTL, ih_cntl);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
adev->irq.ih.enabled = false;
adev->irq.ih.rptr = 0;
}
/**
* iceland_ih_irq_init - init and enable the interrupt ring
*
* @adev: amdgpu_device pointer
*
* Allocate a ring buffer for the interrupt controller,
* enable the RLC, disable interrupts, enable the IH
* ring buffer and enable it (VI).
* Called at device load and reume.
* Returns 0 for success, errors for failure.
*/
static int iceland_ih_irq_init(struct amdgpu_device *adev)
{
int ret = 0;
int rb_bufsz;
u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
u64 wptr_off;
/* disable irqs */
iceland_ih_disable_interrupts(adev);
/* setup interrupt control */
WREG32(mmINTERRUPT_CNTL2, adev->dummy_page.addr >> 8);
interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
/* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
*/
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
/* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_ENABLE, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
/* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
/* set the writeback address whether it's enabled or not */
wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
/* Default settings for IH_CNTL (disabled at first) */
ih_cntl = RREG32(mmIH_CNTL);
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, MC_VMID, 0);
if (adev->irq.msi_enabled)
ih_cntl = REG_SET_FIELD(ih_cntl, IH_CNTL, RPTR_REARM, 1);
WREG32(mmIH_CNTL, ih_cntl);
pci_set_master(adev->pdev);
/* enable interrupts */
iceland_ih_enable_interrupts(adev);
return ret;
}
/**
* iceland_ih_irq_disable - disable interrupts
*
* @adev: amdgpu_device pointer
*
* Disable interrupts on the hw (VI).
*/
static void iceland_ih_irq_disable(struct amdgpu_device *adev)
{
iceland_ih_disable_interrupts(adev);
/* Wait and acknowledge irq */
mdelay(1);
}
/**
* iceland_ih_get_wptr - get the IH ring buffer wptr
*
* @adev: amdgpu_device pointer
*
* Get the IH ring buffer wptr from either the register
* or the writeback memory buffer (VI). Also check for
* ring buffer overflow and deal with it.
* Used by cz_irq_process(VI).
* Returns the value of the wptr.
*/
static u32 iceland_ih_get_wptr(struct amdgpu_device *adev)
{
u32 wptr, tmp;
wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
tmp = RREG32(mmIH_RB_CNTL);
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
WREG32(mmIH_RB_CNTL, tmp);
}
return (wptr & adev->irq.ih.ptr_mask);
}
/**
* iceland_ih_decode_iv - decode an interrupt vector
*
* @adev: amdgpu_device pointer
*
* Decodes the interrupt vector at the current rptr
* position and also advance the position.
*/
static void iceland_ih_decode_iv(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
/* wptr/rptr are in bytes! */
u32 ring_index = adev->irq.ih.rptr >> 2;
uint32_t dw[4];
dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
entry->src_id = dw[0] & 0xff;
entry->src_data = dw[1] & 0xfffffff;
entry->ring_id = dw[2] & 0xff;
entry->vm_id = (dw[2] >> 8) & 0xff;
entry->pas_id = (dw[2] >> 16) & 0xffff;
/* wptr/rptr are in bytes! */
adev->irq.ih.rptr += 16;
}
/**
* iceland_ih_set_rptr - set the IH ring buffer rptr
*
* @adev: amdgpu_device pointer
*
* Set the IH ring buffer rptr.
*/
static void iceland_ih_set_rptr(struct amdgpu_device *adev)
{
WREG32(mmIH_RB_RPTR, adev->irq.ih.rptr);
}
static int iceland_ih_early_init(struct amdgpu_device *adev)
{
iceland_ih_set_interrupt_funcs(adev);
return 0;
}
static int iceland_ih_sw_init(struct amdgpu_device *adev)
{
int r;
r = amdgpu_ih_ring_init(adev, 64 * 1024, false);
if (r)
return r;
r = amdgpu_irq_init(adev);
return r;
}
static int iceland_ih_sw_fini(struct amdgpu_device *adev)
{
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
return 0;
}
static int iceland_ih_hw_init(struct amdgpu_device *adev)
{
int r;
r = iceland_ih_irq_init(adev);
if (r)
return r;
return 0;
}
static int iceland_ih_hw_fini(struct amdgpu_device *adev)
{
iceland_ih_irq_disable(adev);
return 0;
}
static int iceland_ih_suspend(struct amdgpu_device *adev)
{
return iceland_ih_hw_fini(adev);
}
static int iceland_ih_resume(struct amdgpu_device *adev)
{
return iceland_ih_hw_init(adev);
}
static bool iceland_ih_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS);
if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return false;
return true;
}
static int iceland_ih_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
/* read MC_STATUS */
tmp = RREG32(mmSRBM_STATUS);
if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void iceland_ih_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "ICELAND IH registers\n");
dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
RREG32(mmSRBM_STATUS));
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
dev_info(adev->dev, " INTERRUPT_CNTL=0x%08X\n",
RREG32(mmINTERRUPT_CNTL));
dev_info(adev->dev, " INTERRUPT_CNTL2=0x%08X\n",
RREG32(mmINTERRUPT_CNTL2));
dev_info(adev->dev, " IH_CNTL=0x%08X\n",
RREG32(mmIH_CNTL));
dev_info(adev->dev, " IH_RB_CNTL=0x%08X\n",
RREG32(mmIH_RB_CNTL));
dev_info(adev->dev, " IH_RB_BASE=0x%08X\n",
RREG32(mmIH_RB_BASE));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_LO=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_LO));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_HI=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_HI));
dev_info(adev->dev, " IH_RB_RPTR=0x%08X\n",
RREG32(mmIH_RB_RPTR));
dev_info(adev->dev, " IH_RB_WPTR=0x%08X\n",
RREG32(mmIH_RB_WPTR));
}
static int iceland_ih_soft_reset(struct amdgpu_device *adev)
{
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS);
if (tmp & SRBM_STATUS__IH_BUSY_MASK)
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
SOFT_RESET_IH, 1);
if (srbm_soft_reset) {
iceland_ih_print_status(adev);
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
iceland_ih_print_status(adev);
}
return 0;
}
static int iceland_ih_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
// TODO
return 0;
}
static int iceland_ih_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
// TODO
return 0;
}
const struct amdgpu_ip_funcs iceland_ih_ip_funcs = {
.early_init = iceland_ih_early_init,
.late_init = NULL,
.sw_init = iceland_ih_sw_init,
.sw_fini = iceland_ih_sw_fini,
.hw_init = iceland_ih_hw_init,
.hw_fini = iceland_ih_hw_fini,
.suspend = iceland_ih_suspend,
.resume = iceland_ih_resume,
.is_idle = iceland_ih_is_idle,
.wait_for_idle = iceland_ih_wait_for_idle,
.soft_reset = iceland_ih_soft_reset,
.print_status = iceland_ih_print_status,
.set_clockgating_state = iceland_ih_set_clockgating_state,
.set_powergating_state = iceland_ih_set_powergating_state,
};
static const struct amdgpu_ih_funcs iceland_ih_funcs = {
.get_wptr = iceland_ih_get_wptr,
.decode_iv = iceland_ih_decode_iv,
.set_rptr = iceland_ih_set_rptr
};
static void iceland_ih_set_interrupt_funcs(struct amdgpu_device *adev)
{
if (adev->irq.ih_funcs == NULL)
adev->irq.ih_funcs = &iceland_ih_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __ICELAND_IH_H__
#define __ICELAND_IH_H__
extern const struct amdgpu_ip_funcs iceland_ih_ip_funcs;
#endif /* __ICELAND_IH_H__ */
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "ppsmc.h"
#include "iceland_smumgr.h"
#include "smu_ucode_xfer_vi.h"
#include "amdgpu_ucode.h"
#include "smu/smu_7_1_1_d.h"
#include "smu/smu_7_1_1_sh_mask.h"
#define ICELAND_SMC_SIZE 0x20000
static int iceland_set_smc_sram_address(struct amdgpu_device *adev,
uint32_t smc_address, uint32_t limit)
{
uint32_t val;
if (smc_address & 3)
return -EINVAL;
if ((smc_address + 3) > limit)
return -EINVAL;
WREG32(mmSMC_IND_INDEX_0, smc_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
return 0;
}
static int iceland_copy_bytes_to_smc(struct amdgpu_device *adev,
uint32_t smc_start_address,
const uint8_t *src,
uint32_t byte_count, uint32_t limit)
{
uint32_t addr;
uint32_t data, orig_data;
int result = 0;
uint32_t extra_shift;
unsigned long flags;
if (smc_start_address & 3)
return -EINVAL;
if ((smc_start_address + byte_count) > limit)
return -EINVAL;
addr = smc_start_address;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
while (byte_count >= 4) {
/* Bytes are written into the SMC addres space with the MSB first */
data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
result = iceland_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
src += 4;
byte_count -= 4;
addr += 4;
}
if (0 != byte_count) {
/* Now write odd bytes left, do a read modify write cycle */
data = 0;
result = iceland_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
orig_data = RREG32(mmSMC_IND_DATA_0);
extra_shift = 8 * (4 - byte_count);
while (byte_count > 0) {
data = (data << 8) + *src++;
byte_count--;
}
data <<= extra_shift;
data |= (orig_data & ~((~0UL) << extra_shift));
result = iceland_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
}
out:
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
void iceland_start_smc(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
}
void iceland_reset_smc(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
}
static int iceland_program_jump_on_start(struct amdgpu_device *adev)
{
static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
iceland_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
return 0;
}
void iceland_stop_smc_clock(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
}
void iceland_start_smc_clock(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
}
static bool iceland_is_smc_ram_running(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
}
static int wait_smu_response(struct amdgpu_device *adev)
{
int i;
uint32_t val;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32(mmSMC_RESP_0);
if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
static int iceland_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
{
if (!iceland_is_smc_ram_running(adev))
return -EINVAL;
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send message\n");
return -EINVAL;
}
return 0;
}
static int iceland_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
PPSMC_Msg msg)
{
if (!iceland_is_smc_ram_running(adev))
return -EINVAL;;
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
return 0;
}
static int iceland_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
PPSMC_Msg msg,
uint32_t parameter)
{
WREG32(mmSMC_MSG_ARG_0, parameter);
return iceland_send_msg_to_smc(adev, msg);
}
static int iceland_send_msg_to_smc_with_parameter_without_waiting(
struct amdgpu_device *adev,
PPSMC_Msg msg, uint32_t parameter)
{
WREG32(mmSMC_MSG_ARG_0, parameter);
return iceland_send_msg_to_smc_without_waiting(adev, msg);
}
#if 0 /* not used yet */
static int iceland_wait_for_smc_inactive(struct amdgpu_device *adev)
{
int i;
uint32_t val;
if (!iceland_is_smc_ram_running(adev))
return -EINVAL;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
#endif
static int iceland_smu_upload_firmware_image(struct amdgpu_device *adev)
{
const struct smc_firmware_header_v1_0 *hdr;
uint32_t ucode_size;
uint32_t ucode_start_address;
const uint8_t *src;
uint32_t val;
uint32_t byte_count;
uint32_t data;
unsigned long flags;
int i;
if (!adev->pm.fw)
return -EINVAL;
hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
amdgpu_ucode_print_smc_hdr(&hdr->header);
adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
src = (const uint8_t *)
(adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
if (ucode_size & 3) {
DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
return -EINVAL;
}
if (ucode_size > ICELAND_SMC_SIZE) {
DRM_ERROR("SMC address is beyond the SMC RAM area\n");
return -EINVAL;
}
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixRCU_UC_EVENTS);
if (REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done) == 0)
break;
udelay(1);
}
val = RREG32_SMC(ixSMC_SYSCON_MISC_CNTL);
WREG32_SMC(ixSMC_SYSCON_MISC_CNTL, val | 1);
iceland_stop_smc_clock(adev);
iceland_reset_smc(adev);
spin_lock_irqsave(&adev->smc_idx_lock, flags);
WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
byte_count = ucode_size;
while (byte_count >= 4) {
data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
WREG32(mmSMC_IND_DATA_0, data);
src += 4;
byte_count -= 4;
}
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return 0;
}
#if 0 /* not used yet */
static int iceland_read_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t *value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = iceland_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
*value = RREG32(mmSMC_IND_DATA_0);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int iceland_write_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = iceland_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
WREG32(mmSMC_IND_DATA_0, value);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int iceland_smu_stop_smc(struct amdgpu_device *adev)
{
iceland_reset_smc(adev);
iceland_stop_smc_clock(adev);
return 0;
}
#endif
static int iceland_smu_start_smc(struct amdgpu_device *adev)
{
int i;
uint32_t val;
iceland_program_jump_on_start(adev);
iceland_start_smc_clock(adev);
iceland_start_smc(adev);
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixFIRMWARE_FLAGS);
if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED) == 1)
break;
udelay(1);
}
return 0;
}
static enum AMDGPU_UCODE_ID iceland_convert_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case UCODE_ID_SDMA0:
return AMDGPU_UCODE_ID_SDMA0;
case UCODE_ID_SDMA1:
return AMDGPU_UCODE_ID_SDMA1;
case UCODE_ID_CP_CE:
return AMDGPU_UCODE_ID_CP_CE;
case UCODE_ID_CP_PFP:
return AMDGPU_UCODE_ID_CP_PFP;
case UCODE_ID_CP_ME:
return AMDGPU_UCODE_ID_CP_ME;
case UCODE_ID_CP_MEC:
case UCODE_ID_CP_MEC_JT1:
return AMDGPU_UCODE_ID_CP_MEC1;
case UCODE_ID_CP_MEC_JT2:
return AMDGPU_UCODE_ID_CP_MEC2;
case UCODE_ID_RLC_G:
return AMDGPU_UCODE_ID_RLC_G;
default:
DRM_ERROR("ucode type is out of range!\n");
return AMDGPU_UCODE_ID_MAXIMUM;
}
}
static uint32_t iceland_smu_get_mask_for_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case AMDGPU_UCODE_ID_SDMA0:
return UCODE_ID_SDMA0_MASK;
case AMDGPU_UCODE_ID_SDMA1:
return UCODE_ID_SDMA1_MASK;
case AMDGPU_UCODE_ID_CP_CE:
return UCODE_ID_CP_CE_MASK;
case AMDGPU_UCODE_ID_CP_PFP:
return UCODE_ID_CP_PFP_MASK;
case AMDGPU_UCODE_ID_CP_ME:
return UCODE_ID_CP_ME_MASK;
case AMDGPU_UCODE_ID_CP_MEC1:
return UCODE_ID_CP_MEC_MASK | UCODE_ID_CP_MEC_JT1_MASK | UCODE_ID_CP_MEC_JT2_MASK;
case AMDGPU_UCODE_ID_CP_MEC2:
return UCODE_ID_CP_MEC_MASK;
case AMDGPU_UCODE_ID_RLC_G:
return UCODE_ID_RLC_G_MASK;
default:
DRM_ERROR("ucode type is out of range!\n");
return 0;
}
}
static int iceland_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
uint32_t fw_type,
struct SMU_Entry *entry)
{
enum AMDGPU_UCODE_ID id = iceland_convert_fw_type(fw_type);
struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
const struct gfx_firmware_header_v1_0 *header = NULL;
uint64_t gpu_addr;
uint32_t data_size;
if (ucode->fw == NULL)
return -EINVAL;
gpu_addr = ucode->mc_addr;
header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
data_size = le32_to_cpu(header->header.ucode_size_bytes);
entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
entry->id = (uint16_t)fw_type;
entry->image_addr_high = upper_32_bits(gpu_addr);
entry->image_addr_low = lower_32_bits(gpu_addr);
entry->meta_data_addr_high = 0;
entry->meta_data_addr_low = 0;
entry->data_size_byte = data_size;
entry->num_register_entries = 0;
entry->flags = 0;
return 0;
}
static int iceland_smu_request_load_fw(struct amdgpu_device *adev)
{
struct iceland_smu_private_data *private = (struct iceland_smu_private_data *)adev->smu.priv;
struct SMU_DRAMData_TOC *toc;
uint32_t fw_to_load;
toc = (struct SMU_DRAMData_TOC *)private->header;
toc->num_entries = 0;
toc->structure_version = 1;
if (!adev->firmware.smu_load)
return 0;
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for RLC\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for CE\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for PFP\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for ME\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT2,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT2\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA0\n");
return -EINVAL;
}
if (iceland_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA1\n");
return -EINVAL;
}
iceland_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
iceland_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
fw_to_load = UCODE_ID_RLC_G_MASK |
UCODE_ID_SDMA0_MASK |
UCODE_ID_SDMA1_MASK |
UCODE_ID_CP_CE_MASK |
UCODE_ID_CP_ME_MASK |
UCODE_ID_CP_PFP_MASK |
UCODE_ID_CP_MEC_MASK |
UCODE_ID_CP_MEC_JT1_MASK |
UCODE_ID_CP_MEC_JT2_MASK;
if (iceland_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
DRM_ERROR("Fail to request SMU load ucode\n");
return -EINVAL;
}
return 0;
}
static int iceland_smu_check_fw_load_finish(struct amdgpu_device *adev,
uint32_t fw_type)
{
uint32_t fw_mask = iceland_smu_get_mask_for_fw_type(fw_type);
int i;
for (i = 0; i < adev->usec_timeout; i++) {
if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_27) & fw_mask))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("check firmware loading failed\n");
return -EINVAL;
}
return 0;
}
int iceland_smu_start(struct amdgpu_device *adev)
{
int result;
result = iceland_smu_upload_firmware_image(adev);
if (result)
return result;
result = iceland_smu_start_smc(adev);
if (result)
return result;
return iceland_smu_request_load_fw(adev);
}
static const struct amdgpu_smumgr_funcs iceland_smumgr_funcs = {
.check_fw_load_finish = iceland_smu_check_fw_load_finish,
.request_smu_load_fw = NULL,
.request_smu_specific_fw = NULL,
};
int iceland_smu_init(struct amdgpu_device *adev)
{
struct iceland_smu_private_data *private;
uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
uint64_t mc_addr;
void *toc_buf_ptr;
int ret;
private = kzalloc(sizeof(struct iceland_smu_private_data), GFP_KERNEL);
if (NULL == private)
return -ENOMEM;
/* allocate firmware buffers */
if (adev->firmware.smu_load)
amdgpu_ucode_init_bo(adev);
adev->smu.priv = private;
adev->smu.fw_flags = 0;
/* Allocate FW image data structure and header buffer */
ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, toc_buf);
if (ret) {
DRM_ERROR("Failed to allocate memory for TOC buffer\n");
return -ENOMEM;
}
/* Retrieve GPU address for header buffer and internal buffer */
ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to reserve the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to pin the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to map the TOC buffer\n");
return -EINVAL;
}
amdgpu_bo_unreserve(adev->smu.toc_buf);
private->header_addr_low = lower_32_bits(mc_addr);
private->header_addr_high = upper_32_bits(mc_addr);
private->header = toc_buf_ptr;
adev->smu.smumgr_funcs = &iceland_smumgr_funcs;
return 0;
}
int iceland_smu_fini(struct amdgpu_device *adev)
{
amdgpu_bo_unref(&adev->smu.toc_buf);
kfree(adev->smu.priv);
adev->smu.priv = NULL;
if (adev->firmware.fw_buf)
amdgpu_ucode_fini_bo(adev);
return 0;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef ICELAND_SMUMGR_H
#define ICELAND_SMUMGR_H
#include "ppsmc.h"
extern int iceland_smu_init(struct amdgpu_device *adev);
extern int iceland_smu_fini(struct amdgpu_device *adev);
extern int iceland_smu_start(struct amdgpu_device *adev);
struct iceland_smu_private_data
{
uint8_t *header;
uint8_t *mec_image;
uint32_t header_addr_high;
uint32_t header_addr_low;
};
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
* Authors: Alex Deucher
*/
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "amdgpu.h"
#include "amdgpu_ucode.h"
#include "amdgpu_trace.h"
#include "vi.h"
#include "vid.h"
#include "oss/oss_2_4_d.h"
#include "oss/oss_2_4_sh_mask.h"
#include "gmc/gmc_8_1_d.h"
#include "gmc/gmc_8_1_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_sh_mask.h"
#include "bif/bif_5_0_d.h"
#include "bif/bif_5_0_sh_mask.h"
#include "iceland_sdma_pkt_open.h"
static void sdma_v2_4_set_ring_funcs(struct amdgpu_device *adev);
static void sdma_v2_4_set_buffer_funcs(struct amdgpu_device *adev);
static void sdma_v2_4_set_vm_pte_funcs(struct amdgpu_device *adev);
static void sdma_v2_4_set_irq_funcs(struct amdgpu_device *adev);
MODULE_FIRMWARE("radeon/topaz_sdma.bin");
MODULE_FIRMWARE("radeon/topaz_sdma1.bin");
static const u32 sdma_offsets[SDMA_MAX_INSTANCE] =
{
SDMA0_REGISTER_OFFSET,
SDMA1_REGISTER_OFFSET
};
static const u32 golden_settings_iceland_a11[] =
{
mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA0_CLK_CTRL, 0xff000fff, 0x00000000,
mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA1_CLK_CTRL, 0xff000fff, 0x00000000,
};
static const u32 iceland_mgcg_cgcg_init[] =
{
mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100,
mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100
};
/*
* sDMA - System DMA
* Starting with CIK, the GPU has new asynchronous
* DMA engines. These engines are used for compute
* and gfx. There are two DMA engines (SDMA0, SDMA1)
* and each one supports 1 ring buffer used for gfx
* and 2 queues used for compute.
*
* The programming model is very similar to the CP
* (ring buffer, IBs, etc.), but sDMA has it's own
* packet format that is different from the PM4 format
* used by the CP. sDMA supports copying data, writing
* embedded data, solid fills, and a number of other
* things. It also has support for tiling/detiling of
* buffers.
*/
static void sdma_v2_4_init_golden_registers(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_TOPAZ:
amdgpu_program_register_sequence(adev,
iceland_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(iceland_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_iceland_a11,
(const u32)ARRAY_SIZE(golden_settings_iceland_a11));
break;
default:
break;
}
}
/**
* sdma_v2_4_init_microcode - load ucode images from disk
*
* @adev: amdgpu_device pointer
*
* Use the firmware interface to load the ucode images into
* the driver (not loaded into hw).
* Returns 0 on success, error on failure.
*/
static int sdma_v2_4_init_microcode(struct amdgpu_device *adev)
{
const char *chip_name;
char fw_name[30];
int err, i;
struct amdgpu_firmware_info *info = NULL;
const struct common_firmware_header *header = NULL;
DRM_DEBUG("\n");
switch (adev->asic_type) {
case CHIP_TOPAZ:
chip_name = "topaz";
break;
default: BUG();
}
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
if (i == 0)
snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name);
else
snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma1.bin", chip_name);
err = request_firmware(&adev->sdma[i].fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->sdma[i].fw);
if (err)
goto out;
if (adev->firmware.smu_load) {
info = &adev->firmware.ucode[AMDGPU_UCODE_ID_SDMA0 + i];
info->ucode_id = AMDGPU_UCODE_ID_SDMA0 + i;
info->fw = adev->sdma[i].fw;
header = (const struct common_firmware_header *)info->fw->data;
adev->firmware.fw_size +=
ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE);
}
}
out:
if (err) {
printk(KERN_ERR
"sdma_v2_4: Failed to load firmware \"%s\"\n",
fw_name);
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
release_firmware(adev->sdma[i].fw);
adev->sdma[i].fw = NULL;
}
}
return err;
}
/**
* sdma_v2_4_ring_get_rptr - get the current read pointer
*
* @ring: amdgpu ring pointer
*
* Get the current rptr from the hardware (VI+).
*/
static uint32_t sdma_v2_4_ring_get_rptr(struct amdgpu_ring *ring)
{
u32 rptr;
/* XXX check if swapping is necessary on BE */
rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2;
return rptr;
}
/**
* sdma_v2_4_ring_get_wptr - get the current write pointer
*
* @ring: amdgpu ring pointer
*
* Get the current wptr from the hardware (VI+).
*/
static uint32_t sdma_v2_4_ring_get_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
int me = (ring == &ring->adev->sdma[0].ring) ? 0 : 1;
u32 wptr = RREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[me]) >> 2;
return wptr;
}
/**
* sdma_v2_4_ring_set_wptr - commit the write pointer
*
* @ring: amdgpu ring pointer
*
* Write the wptr back to the hardware (VI+).
*/
static void sdma_v2_4_ring_set_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
int me = (ring == &ring->adev->sdma[0].ring) ? 0 : 1;
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[me], ring->wptr << 2);
}
static void sdma_v2_4_hdp_flush_ring_emit(struct amdgpu_ring *);
/**
* sdma_v2_4_ring_emit_ib - Schedule an IB on the DMA engine
*
* @ring: amdgpu ring pointer
* @ib: IB object to schedule
*
* Schedule an IB in the DMA ring (VI).
*/
static void sdma_v2_4_ring_emit_ib(struct amdgpu_ring *ring,
struct amdgpu_ib *ib)
{
u32 vmid = (ib->vm ? ib->vm->ids[ring->idx].id : 0) & 0xf;
u32 next_rptr = ring->wptr + 5;
if (ib->flush_hdp_writefifo)
next_rptr += 6;
while ((next_rptr & 7) != 2)
next_rptr++;
next_rptr += 6;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
amdgpu_ring_write(ring, lower_32_bits(ring->next_rptr_gpu_addr) & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr));
amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1));
amdgpu_ring_write(ring, next_rptr);
if (ib->flush_hdp_writefifo) {
/* flush HDP */
sdma_v2_4_hdp_flush_ring_emit(ring);
}
/* IB packet must end on a 8 DW boundary */
while ((ring->wptr & 7) != 2)
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_NOP));
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_INDIRECT) |
SDMA_PKT_INDIRECT_HEADER_VMID(vmid));
/* base must be 32 byte aligned */
amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr) & 0xffffffe0);
amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, ib->length_dw);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, 0);
}
/**
* sdma_v2_4_hdp_flush_ring_emit - emit an hdp flush on the DMA ring
*
* @ring: amdgpu ring pointer
*
* Emit an hdp flush packet on the requested DMA ring.
*/
static void sdma_v2_4_hdp_flush_ring_emit(struct amdgpu_ring *ring)
{
u32 ref_and_mask = 0;
if (ring == &ring->adev->sdma[0].ring)
ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA0, 1);
else
ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA1, 1);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) |
SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */
amdgpu_ring_write(ring, mmGPU_HDP_FLUSH_DONE << 2);
amdgpu_ring_write(ring, mmGPU_HDP_FLUSH_REQ << 2);
amdgpu_ring_write(ring, ref_and_mask); /* reference */
amdgpu_ring_write(ring, ref_and_mask); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
}
/**
* sdma_v2_4_ring_emit_fence - emit a fence on the DMA ring
*
* @ring: amdgpu ring pointer
* @fence: amdgpu fence object
*
* Add a DMA fence packet to the ring to write
* the fence seq number and DMA trap packet to generate
* an interrupt if needed (VI).
*/
static void sdma_v2_4_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
bool write64bits)
{
/* write the fence */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
/* optionally write high bits as well */
if (write64bits) {
addr += 4;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
}
/* generate an interrupt */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_TRAP));
amdgpu_ring_write(ring, SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(0));
}
/**
* sdma_v2_4_ring_emit_semaphore - emit a semaphore on the dma ring
*
* @ring: amdgpu_ring structure holding ring information
* @semaphore: amdgpu semaphore object
* @emit_wait: wait or signal semaphore
*
* Add a DMA semaphore packet to the ring wait on or signal
* other rings (VI).
*/
static bool sdma_v2_4_ring_emit_semaphore(struct amdgpu_ring *ring,
struct amdgpu_semaphore *semaphore,
bool emit_wait)
{
u64 addr = semaphore->gpu_addr;
u32 sig = emit_wait ? 0 : 1;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SEM) |
SDMA_PKT_SEMAPHORE_HEADER_SIGNAL(sig));
amdgpu_ring_write(ring, lower_32_bits(addr) & 0xfffffff8);
amdgpu_ring_write(ring, upper_32_bits(addr));
return true;
}
/**
* sdma_v2_4_gfx_stop - stop the gfx async dma engines
*
* @adev: amdgpu_device pointer
*
* Stop the gfx async dma ring buffers (VI).
*/
static void sdma_v2_4_gfx_stop(struct amdgpu_device *adev)
{
struct amdgpu_ring *sdma0 = &adev->sdma[0].ring;
struct amdgpu_ring *sdma1 = &adev->sdma[1].ring;
u32 rb_cntl, ib_cntl;
int i;
if ((adev->mman.buffer_funcs_ring == sdma0) ||
(adev->mman.buffer_funcs_ring == sdma1))
amdgpu_ttm_set_active_vram_size(adev, adev->mc.visible_vram_size);
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
rb_cntl = RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 0);
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
ib_cntl = RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]);
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 0);
WREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i], ib_cntl);
}
sdma0->ready = false;
sdma1->ready = false;
}
/**
* sdma_v2_4_rlc_stop - stop the compute async dma engines
*
* @adev: amdgpu_device pointer
*
* Stop the compute async dma queues (VI).
*/
static void sdma_v2_4_rlc_stop(struct amdgpu_device *adev)
{
/* XXX todo */
}
/**
* sdma_v2_4_enable - stop the async dma engines
*
* @adev: amdgpu_device pointer
* @enable: enable/disable the DMA MEs.
*
* Halt or unhalt the async dma engines (VI).
*/
static void sdma_v2_4_enable(struct amdgpu_device *adev, bool enable)
{
u32 f32_cntl;
int i;
if (enable == false) {
sdma_v2_4_gfx_stop(adev);
sdma_v2_4_rlc_stop(adev);
}
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
f32_cntl = RREG32(mmSDMA0_F32_CNTL + sdma_offsets[i]);
if (enable)
f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, 0);
else
f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + sdma_offsets[i], f32_cntl);
}
}
/**
* sdma_v2_4_gfx_resume - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the gfx DMA ring buffers and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v2_4_gfx_resume(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
u32 rb_cntl, ib_cntl;
u32 rb_bufsz;
u32 wb_offset;
int i, j, r;
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
ring = &adev->sdma[i].ring;
wb_offset = (ring->rptr_offs * 4);
mutex_lock(&adev->srbm_mutex);
for (j = 0; j < 16; j++) {
vi_srbm_select(adev, 0, 0, 0, j);
/* SDMA GFX */
WREG32(mmSDMA0_GFX_VIRTUAL_ADDR + sdma_offsets[i], 0);
WREG32(mmSDMA0_GFX_APE1_CNTL + sdma_offsets[i], 0);
}
vi_srbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
WREG32(mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL + sdma_offsets[i], 0);
/* Set ring buffer size in dwords */
rb_bufsz = order_base_2(ring->ring_size / 4);
rb_cntl = RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SIZE, rb_bufsz);
#ifdef __BIG_ENDIAN
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SWAP_ENABLE, 1);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL,
RPTR_WRITEBACK_SWAP_ENABLE, 1);
#endif
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
/* Initialize the ring buffer's read and write pointers */
WREG32(mmSDMA0_GFX_RB_RPTR + sdma_offsets[i], 0);
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i], 0);
/* set the wb address whether it's enabled or not */
WREG32(mmSDMA0_GFX_RB_RPTR_ADDR_HI + sdma_offsets[i],
upper_32_bits(adev->wb.gpu_addr + wb_offset) & 0xFFFFFFFF);
WREG32(mmSDMA0_GFX_RB_RPTR_ADDR_LO + sdma_offsets[i],
lower_32_bits(adev->wb.gpu_addr + wb_offset) & 0xFFFFFFFC);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RPTR_WRITEBACK_ENABLE, 1);
WREG32(mmSDMA0_GFX_RB_BASE + sdma_offsets[i], ring->gpu_addr >> 8);
WREG32(mmSDMA0_GFX_RB_BASE_HI + sdma_offsets[i], ring->gpu_addr >> 40);
ring->wptr = 0;
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i], ring->wptr << 2);
/* enable DMA RB */
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 1);
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
ib_cntl = RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]);
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 1);
#ifdef __BIG_ENDIAN
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_SWAP_ENABLE, 1);
#endif
/* enable DMA IBs */
WREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i], ib_cntl);
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
return r;
}
if (adev->mman.buffer_funcs_ring == ring)
amdgpu_ttm_set_active_vram_size(adev, adev->mc.real_vram_size);
}
return 0;
}
/**
* sdma_v2_4_rlc_resume - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the compute DMA queues and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v2_4_rlc_resume(struct amdgpu_device *adev)
{
/* XXX todo */
return 0;
}
/**
* sdma_v2_4_load_microcode - load the sDMA ME ucode
*
* @adev: amdgpu_device pointer
*
* Loads the sDMA0/1 ucode.
* Returns 0 for success, -EINVAL if the ucode is not available.
*/
static int sdma_v2_4_load_microcode(struct amdgpu_device *adev)
{
const struct sdma_firmware_header_v1_0 *hdr;
const __le32 *fw_data;
u32 fw_size;
int i, j;
bool smc_loads_fw = false; /* XXX fix me */
if (!adev->sdma[0].fw || !adev->sdma[1].fw)
return -EINVAL;
/* halt the MEs */
sdma_v2_4_enable(adev, false);
if (smc_loads_fw) {
/* XXX query SMC for fw load complete */
} else {
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
hdr = (const struct sdma_firmware_header_v1_0 *)adev->sdma[i].fw->data;
amdgpu_ucode_print_sdma_hdr(&hdr->header);
fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
adev->sdma[i].fw_version = le32_to_cpu(hdr->header.ucode_version);
fw_data = (const __le32 *)
(adev->sdma[i].fw->data +
le32_to_cpu(hdr->header.ucode_array_offset_bytes));
WREG32(mmSDMA0_UCODE_ADDR + sdma_offsets[i], 0);
for (j = 0; j < fw_size; j++)
WREG32(mmSDMA0_UCODE_DATA + sdma_offsets[i], le32_to_cpup(fw_data++));
WREG32(mmSDMA0_UCODE_ADDR + sdma_offsets[i], adev->sdma[i].fw_version);
}
}
return 0;
}
/**
* sdma_v2_4_start - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the DMA engines and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v2_4_start(struct amdgpu_device *adev)
{
int r;
if (!adev->firmware.smu_load) {
r = sdma_v2_4_load_microcode(adev);
if (r)
return r;
} else {
r = adev->smu.smumgr_funcs->check_fw_load_finish(adev,
AMDGPU_UCODE_ID_SDMA0);
if (r)
return -EINVAL;
r = adev->smu.smumgr_funcs->check_fw_load_finish(adev,
AMDGPU_UCODE_ID_SDMA1);
if (r)
return -EINVAL;
}
/* unhalt the MEs */
sdma_v2_4_enable(adev, true);
/* start the gfx rings and rlc compute queues */
r = sdma_v2_4_gfx_resume(adev);
if (r)
return r;
r = sdma_v2_4_rlc_resume(adev);
if (r)
return r;
return 0;
}
/**
* sdma_v2_4_ring_test_ring - simple async dma engine test
*
* @ring: amdgpu_ring structure holding ring information
*
* Test the DMA engine by writing using it to write an
* value to memory. (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v2_4_ring_test_ring(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
unsigned i;
unsigned index;
int r;
u32 tmp;
u64 gpu_addr;
r = amdgpu_wb_get(adev, &index);
if (r) {
dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
return r;
}
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ring_lock(ring, 5);
if (r) {
DRM_ERROR("amdgpu: dma failed to lock ring %d (%d).\n", ring->idx, r);
amdgpu_wb_free(adev, index);
return r;
}
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
amdgpu_ring_write(ring, upper_32_bits(gpu_addr));
amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1));
amdgpu_ring_write(ring, 0xDEADBEEF);
amdgpu_ring_unlock_commit(ring);
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
} else {
DRM_ERROR("amdgpu: ring %d test failed (0x%08X)\n",
ring->idx, tmp);
r = -EINVAL;
}
amdgpu_wb_free(adev, index);
return r;
}
/**
* sdma_v2_4_ring_test_ib - test an IB on the DMA engine
*
* @ring: amdgpu_ring structure holding ring information
*
* Test a simple IB in the DMA ring (VI).
* Returns 0 on success, error on failure.
*/
static int sdma_v2_4_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
unsigned i;
unsigned index;
int r;
u32 tmp = 0;
u64 gpu_addr;
r = amdgpu_wb_get(adev, &index);
if (r) {
dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
return r;
}
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
}
ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
ib.ptr[1] = lower_32_bits(gpu_addr);
ib.ptr[2] = upper_32_bits(gpu_addr);
ib.ptr[3] = SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1);
ib.ptr[4] = 0xDEADBEEF;
ib.ptr[5] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
ib.ptr[6] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
ib.ptr[7] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
ib.length_dw = 8;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
return r;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
} else {
DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
r = -EINVAL;
}
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
return r;
}
/**
* sdma_v2_4_vm_copy_pte - update PTEs by copying them from the GART
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @src: src addr to copy from
* @count: number of page entries to update
*
* Update PTEs by copying them from the GART using sDMA (CIK).
*/
static void sdma_v2_4_vm_copy_pte(struct amdgpu_ib *ib,
uint64_t pe, uint64_t src,
unsigned count)
{
while (count) {
unsigned bytes = count * 8;
if (bytes > 0x1FFFF8)
bytes = 0x1FFFF8;
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
ib->ptr[ib->length_dw++] = bytes;
ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
ib->ptr[ib->length_dw++] = lower_32_bits(src);
ib->ptr[ib->length_dw++] = upper_32_bits(src);
ib->ptr[ib->length_dw++] = lower_32_bits(pe);
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
pe += bytes;
src += bytes;
count -= bytes / 8;
}
}
/**
* sdma_v2_4_vm_write_pte - update PTEs by writing them manually
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @addr: dst addr to write into pe
* @count: number of page entries to update
* @incr: increase next addr by incr bytes
* @flags: access flags
*
* Update PTEs by writing them manually using sDMA (CIK).
*/
static void sdma_v2_4_vm_write_pte(struct amdgpu_ib *ib,
uint64_t pe,
uint64_t addr, unsigned count,
uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
while (count) {
ndw = count * 2;
if (ndw > 0xFFFFE)
ndw = 0xFFFFE;
/* for non-physically contiguous pages (system) */
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
ib->ptr[ib->length_dw++] = pe;
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
ib->ptr[ib->length_dw++] = ndw;
for (; ndw > 0; ndw -= 2, --count, pe += 8) {
if (flags & AMDGPU_PTE_SYSTEM) {
value = amdgpu_vm_map_gart(ib->ring->adev, addr);
value &= 0xFFFFFFFFFFFFF000ULL;
} else if (flags & AMDGPU_PTE_VALID) {
value = addr;
} else {
value = 0;
}
addr += incr;
value |= flags;
ib->ptr[ib->length_dw++] = value;
ib->ptr[ib->length_dw++] = upper_32_bits(value);
}
}
}
/**
* sdma_v2_4_vm_set_pte_pde - update the page tables using sDMA
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @addr: dst addr to write into pe
* @count: number of page entries to update
* @incr: increase next addr by incr bytes
* @flags: access flags
*
* Update the page tables using sDMA (CIK).
*/
static void sdma_v2_4_vm_set_pte_pde(struct amdgpu_ib *ib,
uint64_t pe,
uint64_t addr, unsigned count,
uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
while (count) {
ndw = count;
if (ndw > 0x7FFFF)
ndw = 0x7FFFF;
if (flags & AMDGPU_PTE_VALID)
value = addr;
else
value = 0;
/* for physically contiguous pages (vram) */
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
ib->ptr[ib->length_dw++] = pe; /* dst addr */
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
ib->ptr[ib->length_dw++] = flags; /* mask */
ib->ptr[ib->length_dw++] = 0;
ib->ptr[ib->length_dw++] = value; /* value */
ib->ptr[ib->length_dw++] = upper_32_bits(value);
ib->ptr[ib->length_dw++] = incr; /* increment size */
ib->ptr[ib->length_dw++] = 0;
ib->ptr[ib->length_dw++] = ndw; /* number of entries */
pe += ndw * 8;
addr += ndw * incr;
count -= ndw;
}
}
/**
* sdma_v2_4_vm_pad_ib - pad the IB to the required number of dw
*
* @ib: indirect buffer to fill with padding
*
*/
static void sdma_v2_4_vm_pad_ib(struct amdgpu_ib *ib)
{
while (ib->length_dw & 0x7)
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
}
/**
* sdma_v2_4_ring_emit_vm_flush - cik vm flush using sDMA
*
* @ring: amdgpu_ring pointer
* @vm: amdgpu_vm pointer
*
* Update the page table base and flush the VM TLB
* using sDMA (VI).
*/
static void sdma_v2_4_ring_emit_vm_flush(struct amdgpu_ring *ring,
unsigned vm_id, uint64_t pd_addr)
{
u32 srbm_gfx_cntl = 0;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
if (vm_id < 8) {
amdgpu_ring_write(ring, (mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR + vm_id));
} else {
amdgpu_ring_write(ring, (mmVM_CONTEXT8_PAGE_TABLE_BASE_ADDR + vm_id - 8));
}
amdgpu_ring_write(ring, pd_addr >> 12);
/* update SH_MEM_* regs */
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, VMID, vm_id);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSRBM_GFX_CNTL);
amdgpu_ring_write(ring, srbm_gfx_cntl);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_BASES);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_CONFIG);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_APE1_BASE);
amdgpu_ring_write(ring, 1);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_APE1_LIMIT);
amdgpu_ring_write(ring, 0);
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, VMID, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSRBM_GFX_CNTL);
amdgpu_ring_write(ring, srbm_gfx_cntl);
/* flush TLB */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmVM_INVALIDATE_REQUEST);
amdgpu_ring_write(ring, 1 << vm_id);
/* wait for flush */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
SDMA_PKT_POLL_REGMEM_HEADER_FUNC(0)); /* always */
amdgpu_ring_write(ring, mmVM_INVALIDATE_REQUEST << 2);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, 0); /* reference */
amdgpu_ring_write(ring, 0); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
}
static int sdma_v2_4_early_init(struct amdgpu_device *adev)
{
sdma_v2_4_set_ring_funcs(adev);
sdma_v2_4_set_buffer_funcs(adev);
sdma_v2_4_set_vm_pte_funcs(adev);
sdma_v2_4_set_irq_funcs(adev);
return 0;
}
static int sdma_v2_4_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
/* SDMA trap event */
r = amdgpu_irq_add_id(adev, 224, &adev->sdma_trap_irq);
if (r)
return r;
/* SDMA Privileged inst */
r = amdgpu_irq_add_id(adev, 241, &adev->sdma_illegal_inst_irq);
if (r)
return r;
/* SDMA Privileged inst */
r = amdgpu_irq_add_id(adev, 247, &adev->sdma_illegal_inst_irq);
if (r)
return r;
r = sdma_v2_4_init_microcode(adev);
if (r) {
DRM_ERROR("Failed to load sdma firmware!\n");
return r;
}
ring = &adev->sdma[0].ring;
ring->ring_obj = NULL;
ring->use_doorbell = false;
ring = &adev->sdma[1].ring;
ring->ring_obj = NULL;
ring->use_doorbell = false;
ring = &adev->sdma[0].ring;
sprintf(ring->name, "sdma0");
r = amdgpu_ring_init(adev, ring, 256 * 1024,
SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP), 0xf,
&adev->sdma_trap_irq, AMDGPU_SDMA_IRQ_TRAP0,
AMDGPU_RING_TYPE_SDMA);
if (r)
return r;
ring = &adev->sdma[1].ring;
sprintf(ring->name, "sdma1");
r = amdgpu_ring_init(adev, ring, 256 * 1024,
SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP), 0xf,
&adev->sdma_trap_irq, AMDGPU_SDMA_IRQ_TRAP1,
AMDGPU_RING_TYPE_SDMA);
if (r)
return r;
return r;
}
static int sdma_v2_4_sw_fini(struct amdgpu_device *adev)
{
amdgpu_ring_fini(&adev->sdma[0].ring);
amdgpu_ring_fini(&adev->sdma[1].ring);
return 0;
}
static int sdma_v2_4_hw_init(struct amdgpu_device *adev)
{
int r;
sdma_v2_4_init_golden_registers(adev);
r = sdma_v2_4_start(adev);
if (r)
return r;
return r;
}
static int sdma_v2_4_hw_fini(struct amdgpu_device *adev)
{
sdma_v2_4_enable(adev, false);
return 0;
}
static int sdma_v2_4_suspend(struct amdgpu_device *adev)
{
return sdma_v2_4_hw_fini(adev);
}
static int sdma_v2_4_resume(struct amdgpu_device *adev)
{
return sdma_v2_4_hw_init(adev);
}
static bool sdma_v2_4_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS2);
if (tmp & (SRBM_STATUS2__SDMA_BUSY_MASK |
SRBM_STATUS2__SDMA1_BUSY_MASK))
return false;
return true;
}
static int sdma_v2_4_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(mmSRBM_STATUS2) & (SRBM_STATUS2__SDMA_BUSY_MASK |
SRBM_STATUS2__SDMA1_BUSY_MASK);
if (!tmp)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void sdma_v2_4_print_status(struct amdgpu_device *adev)
{
int i, j;
dev_info(adev->dev, "VI SDMA registers\n");
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
dev_info(adev->dev, " SDMA%d_STATUS_REG=0x%08X\n",
i, RREG32(mmSDMA0_STATUS_REG + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_F32_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_F32_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_SEM_WAIT_FAIL_TIMER_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_IB_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_WPTR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR_ADDR_HI=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR_ADDR_HI + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR_ADDR_LO=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR_ADDR_LO + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_BASE=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_BASE + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_BASE_HI=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_BASE_HI + sdma_offsets[i]));
mutex_lock(&adev->srbm_mutex);
for (j = 0; j < 16; j++) {
vi_srbm_select(adev, 0, 0, 0, j);
dev_info(adev->dev, " VM %d:\n", j);
dev_info(adev->dev, " SDMA%d_GFX_VIRTUAL_ADDR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_VIRTUAL_ADDR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_APE1_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_APE1_CNTL + sdma_offsets[i]));
}
vi_srbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
}
}
static int sdma_v2_4_soft_reset(struct amdgpu_device *adev)
{
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS2);
if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK) {
/* sdma0 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
}
if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK) {
/* sdma1 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
WREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET, tmp);
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK;
}
if (srbm_soft_reset) {
sdma_v2_4_print_status(adev);
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
sdma_v2_4_print_status(adev);
}
return 0;
}
static int sdma_v2_4_set_trap_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *src,
unsigned type,
enum amdgpu_interrupt_state state)
{
u32 sdma_cntl;
switch (type) {
case AMDGPU_SDMA_IRQ_TRAP0:
switch (state) {
case AMDGPU_IRQ_STATE_DISABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 0);
WREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET, sdma_cntl);
break;
case AMDGPU_IRQ_STATE_ENABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 1);
WREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET, sdma_cntl);
break;
default:
break;
}
break;
case AMDGPU_SDMA_IRQ_TRAP1:
switch (state) {
case AMDGPU_IRQ_STATE_DISABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 0);
WREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET, sdma_cntl);
break;
case AMDGPU_IRQ_STATE_ENABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 1);
WREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET, sdma_cntl);
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static int sdma_v2_4_process_trap_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
u8 instance_id, queue_id;
instance_id = (entry->ring_id & 0x3) >> 0;
queue_id = (entry->ring_id & 0xc) >> 2;
DRM_DEBUG("IH: SDMA trap\n");
switch (instance_id) {
case 0:
switch (queue_id) {
case 0:
amdgpu_fence_process(&adev->sdma[0].ring);
break;
case 1:
/* XXX compute */
break;
case 2:
/* XXX compute */
break;
}
break;
case 1:
switch (queue_id) {
case 0:
amdgpu_fence_process(&adev->sdma[1].ring);
break;
case 1:
/* XXX compute */
break;
case 2:
/* XXX compute */
break;
}
break;
}
return 0;
}
static int sdma_v2_4_process_illegal_inst_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
DRM_ERROR("Illegal instruction in SDMA command stream\n");
schedule_work(&adev->reset_work);
return 0;
}
static int sdma_v2_4_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
/* XXX handled via the smc on VI */
return 0;
}
static int sdma_v2_4_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs sdma_v2_4_ip_funcs = {
.early_init = sdma_v2_4_early_init,
.late_init = NULL,
.sw_init = sdma_v2_4_sw_init,
.sw_fini = sdma_v2_4_sw_fini,
.hw_init = sdma_v2_4_hw_init,
.hw_fini = sdma_v2_4_hw_fini,
.suspend = sdma_v2_4_suspend,
.resume = sdma_v2_4_resume,
.is_idle = sdma_v2_4_is_idle,
.wait_for_idle = sdma_v2_4_wait_for_idle,
.soft_reset = sdma_v2_4_soft_reset,
.print_status = sdma_v2_4_print_status,
.set_clockgating_state = sdma_v2_4_set_clockgating_state,
.set_powergating_state = sdma_v2_4_set_powergating_state,
};
/**
* sdma_v2_4_ring_is_lockup - Check if the DMA engine is locked up
*
* @ring: amdgpu_ring structure holding ring information
*
* Check if the async DMA engine is locked up (VI).
* Returns true if the engine appears to be locked up, false if not.
*/
static bool sdma_v2_4_ring_is_lockup(struct amdgpu_ring *ring)
{
if (sdma_v2_4_is_idle(ring->adev)) {
amdgpu_ring_lockup_update(ring);
return false;
}
return amdgpu_ring_test_lockup(ring);
}
static const struct amdgpu_ring_funcs sdma_v2_4_ring_funcs = {
.get_rptr = sdma_v2_4_ring_get_rptr,
.get_wptr = sdma_v2_4_ring_get_wptr,
.set_wptr = sdma_v2_4_ring_set_wptr,
.parse_cs = NULL,
.emit_ib = sdma_v2_4_ring_emit_ib,
.emit_fence = sdma_v2_4_ring_emit_fence,
.emit_semaphore = sdma_v2_4_ring_emit_semaphore,
.emit_vm_flush = sdma_v2_4_ring_emit_vm_flush,
.test_ring = sdma_v2_4_ring_test_ring,
.test_ib = sdma_v2_4_ring_test_ib,
.is_lockup = sdma_v2_4_ring_is_lockup,
};
static void sdma_v2_4_set_ring_funcs(struct amdgpu_device *adev)
{
adev->sdma[0].ring.funcs = &sdma_v2_4_ring_funcs;
adev->sdma[1].ring.funcs = &sdma_v2_4_ring_funcs;
}
static const struct amdgpu_irq_src_funcs sdma_v2_4_trap_irq_funcs = {
.set = sdma_v2_4_set_trap_irq_state,
.process = sdma_v2_4_process_trap_irq,
};
static const struct amdgpu_irq_src_funcs sdma_v2_4_illegal_inst_irq_funcs = {
.process = sdma_v2_4_process_illegal_inst_irq,
};
static void sdma_v2_4_set_irq_funcs(struct amdgpu_device *adev)
{
adev->sdma_trap_irq.num_types = AMDGPU_SDMA_IRQ_LAST;
adev->sdma_trap_irq.funcs = &sdma_v2_4_trap_irq_funcs;
adev->sdma_illegal_inst_irq.funcs = &sdma_v2_4_illegal_inst_irq_funcs;
}
/**
* sdma_v2_4_emit_copy_buffer - copy buffer using the sDMA engine
*
* @ring: amdgpu_ring structure holding ring information
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @byte_count: number of bytes to xfer
*
* Copy GPU buffers using the DMA engine (VI).
* Used by the amdgpu ttm implementation to move pages if
* registered as the asic copy callback.
*/
static void sdma_v2_4_emit_copy_buffer(struct amdgpu_ring *ring,
uint64_t src_offset,
uint64_t dst_offset,
uint32_t byte_count)
{
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR));
amdgpu_ring_write(ring, byte_count);
amdgpu_ring_write(ring, 0); /* src/dst endian swap */
amdgpu_ring_write(ring, lower_32_bits(src_offset));
amdgpu_ring_write(ring, upper_32_bits(src_offset));
amdgpu_ring_write(ring, lower_32_bits(dst_offset));
amdgpu_ring_write(ring, upper_32_bits(dst_offset));
}
/**
* sdma_v2_4_emit_fill_buffer - fill buffer using the sDMA engine
*
* @ring: amdgpu_ring structure holding ring information
* @src_data: value to write to buffer
* @dst_offset: dst GPU address
* @byte_count: number of bytes to xfer
*
* Fill GPU buffers using the DMA engine (VI).
*/
static void sdma_v2_4_emit_fill_buffer(struct amdgpu_ring *ring,
uint32_t src_data,
uint64_t dst_offset,
uint32_t byte_count)
{
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_CONST_FILL));
amdgpu_ring_write(ring, lower_32_bits(dst_offset));
amdgpu_ring_write(ring, upper_32_bits(dst_offset));
amdgpu_ring_write(ring, src_data);
amdgpu_ring_write(ring, byte_count);
}
static const struct amdgpu_buffer_funcs sdma_v2_4_buffer_funcs = {
.copy_max_bytes = 0x1fffff,
.copy_num_dw = 7,
.emit_copy_buffer = sdma_v2_4_emit_copy_buffer,
.fill_max_bytes = 0x1fffff,
.fill_num_dw = 7,
.emit_fill_buffer = sdma_v2_4_emit_fill_buffer,
};
static void sdma_v2_4_set_buffer_funcs(struct amdgpu_device *adev)
{
if (adev->mman.buffer_funcs == NULL) {
adev->mman.buffer_funcs = &sdma_v2_4_buffer_funcs;
adev->mman.buffer_funcs_ring = &adev->sdma[0].ring;
}
}
static const struct amdgpu_vm_pte_funcs sdma_v2_4_vm_pte_funcs = {
.copy_pte = sdma_v2_4_vm_copy_pte,
.write_pte = sdma_v2_4_vm_write_pte,
.set_pte_pde = sdma_v2_4_vm_set_pte_pde,
.pad_ib = sdma_v2_4_vm_pad_ib,
};
static void sdma_v2_4_set_vm_pte_funcs(struct amdgpu_device *adev)
{
if (adev->vm_manager.vm_pte_funcs == NULL) {
adev->vm_manager.vm_pte_funcs = &sdma_v2_4_vm_pte_funcs;
adev->vm_manager.vm_pte_funcs_ring = &adev->sdma[0].ring;
}
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __SDMA_V2_4_H__
#define __SDMA_V2_4_H__
extern const struct amdgpu_ip_funcs sdma_v2_4_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
* Authors: Alex Deucher
*/
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "amdgpu.h"
#include "amdgpu_ucode.h"
#include "amdgpu_trace.h"
#include "vi.h"
#include "vid.h"
#include "oss/oss_3_0_d.h"
#include "oss/oss_3_0_sh_mask.h"
#include "gmc/gmc_8_1_d.h"
#include "gmc/gmc_8_1_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_sh_mask.h"
#include "bif/bif_5_0_d.h"
#include "bif/bif_5_0_sh_mask.h"
#include "tonga_sdma_pkt_open.h"
static void sdma_v3_0_set_ring_funcs(struct amdgpu_device *adev);
static void sdma_v3_0_set_buffer_funcs(struct amdgpu_device *adev);
static void sdma_v3_0_set_vm_pte_funcs(struct amdgpu_device *adev);
static void sdma_v3_0_set_irq_funcs(struct amdgpu_device *adev);
MODULE_FIRMWARE("radeon/tonga_sdma.bin");
MODULE_FIRMWARE("radeon/tonga_sdma1.bin");
MODULE_FIRMWARE("radeon/carrizo_sdma.bin");
MODULE_FIRMWARE("radeon/carrizo_sdma1.bin");
static const u32 sdma_offsets[SDMA_MAX_INSTANCE] =
{
SDMA0_REGISTER_OFFSET,
SDMA1_REGISTER_OFFSET
};
static const u32 golden_settings_tonga_a11[] =
{
mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA0_CLK_CTRL, 0xff000fff, 0x00000000,
mmSDMA0_GFX_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA0_RLC0_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA0_RLC1_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA1_CLK_CTRL, 0xff000fff, 0x00000000,
mmSDMA1_GFX_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_RLC0_IB_CNTL, 0x800f0111, 0x00000100,
mmSDMA1_RLC1_IB_CNTL, 0x800f0111, 0x00000100,
};
static const u32 tonga_mgcg_cgcg_init[] =
{
mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100,
mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100
};
static const u32 cz_golden_settings_a11[] =
{
mmSDMA0_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA0_CLK_CTRL, 0xff000fff, 0x00000000,
mmSDMA0_GFX_IB_CNTL, 0x00000100, 0x00000100,
mmSDMA0_POWER_CNTL, 0x00000800, 0x0003c800,
mmSDMA0_RLC0_IB_CNTL, 0x00000100, 0x00000100,
mmSDMA0_RLC1_IB_CNTL, 0x00000100, 0x00000100,
mmSDMA1_CHICKEN_BITS, 0xfc910007, 0x00810007,
mmSDMA1_CLK_CTRL, 0xff000fff, 0x00000000,
mmSDMA1_GFX_IB_CNTL, 0x00000100, 0x00000100,
mmSDMA1_POWER_CNTL, 0x00000800, 0x0003c800,
mmSDMA1_RLC0_IB_CNTL, 0x00000100, 0x00000100,
mmSDMA1_RLC1_IB_CNTL, 0x00000100, 0x00000100,
};
static const u32 cz_mgcg_cgcg_init[] =
{
mmSDMA0_CLK_CTRL, 0xff000ff0, 0x00000100,
mmSDMA1_CLK_CTRL, 0xff000ff0, 0x00000100
};
/*
* sDMA - System DMA
* Starting with CIK, the GPU has new asynchronous
* DMA engines. These engines are used for compute
* and gfx. There are two DMA engines (SDMA0, SDMA1)
* and each one supports 1 ring buffer used for gfx
* and 2 queues used for compute.
*
* The programming model is very similar to the CP
* (ring buffer, IBs, etc.), but sDMA has it's own
* packet format that is different from the PM4 format
* used by the CP. sDMA supports copying data, writing
* embedded data, solid fills, and a number of other
* things. It also has support for tiling/detiling of
* buffers.
*/
static void sdma_v3_0_init_golden_registers(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(tonga_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
golden_settings_tonga_a11,
(const u32)ARRAY_SIZE(golden_settings_tonga_a11));
break;
case CHIP_CARRIZO:
amdgpu_program_register_sequence(adev,
cz_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(cz_mgcg_cgcg_init));
amdgpu_program_register_sequence(adev,
cz_golden_settings_a11,
(const u32)ARRAY_SIZE(cz_golden_settings_a11));
break;
default:
break;
}
}
/**
* sdma_v3_0_init_microcode - load ucode images from disk
*
* @adev: amdgpu_device pointer
*
* Use the firmware interface to load the ucode images into
* the driver (not loaded into hw).
* Returns 0 on success, error on failure.
*/
static int sdma_v3_0_init_microcode(struct amdgpu_device *adev)
{
const char *chip_name;
char fw_name[30];
int err, i;
struct amdgpu_firmware_info *info = NULL;
const struct common_firmware_header *header = NULL;
DRM_DEBUG("\n");
switch (adev->asic_type) {
case CHIP_TONGA:
chip_name = "tonga";
break;
case CHIP_CARRIZO:
chip_name = "carrizo";
break;
default: BUG();
}
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
if (i == 0)
snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma.bin", chip_name);
else
snprintf(fw_name, sizeof(fw_name), "radeon/%s_sdma1.bin", chip_name);
err = request_firmware(&adev->sdma[i].fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->sdma[i].fw);
if (err)
goto out;
if (adev->firmware.smu_load) {
info = &adev->firmware.ucode[AMDGPU_UCODE_ID_SDMA0 + i];
info->ucode_id = AMDGPU_UCODE_ID_SDMA0 + i;
info->fw = adev->sdma[i].fw;
header = (const struct common_firmware_header *)info->fw->data;
adev->firmware.fw_size +=
ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE);
}
}
out:
if (err) {
printk(KERN_ERR
"sdma_v3_0: Failed to load firmware \"%s\"\n",
fw_name);
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
release_firmware(adev->sdma[i].fw);
adev->sdma[i].fw = NULL;
}
}
return err;
}
/**
* sdma_v3_0_ring_get_rptr - get the current read pointer
*
* @ring: amdgpu ring pointer
*
* Get the current rptr from the hardware (VI+).
*/
static uint32_t sdma_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
{
u32 rptr;
/* XXX check if swapping is necessary on BE */
rptr = ring->adev->wb.wb[ring->rptr_offs] >> 2;
return rptr;
}
/**
* sdma_v3_0_ring_get_wptr - get the current write pointer
*
* @ring: amdgpu ring pointer
*
* Get the current wptr from the hardware (VI+).
*/
static uint32_t sdma_v3_0_ring_get_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
u32 wptr;
if (ring->use_doorbell) {
/* XXX check if swapping is necessary on BE */
wptr = ring->adev->wb.wb[ring->wptr_offs] >> 2;
} else {
int me = (ring == &ring->adev->sdma[0].ring) ? 0 : 1;
wptr = RREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[me]) >> 2;
}
return wptr;
}
/**
* sdma_v3_0_ring_set_wptr - commit the write pointer
*
* @ring: amdgpu ring pointer
*
* Write the wptr back to the hardware (VI+).
*/
static void sdma_v3_0_ring_set_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
if (ring->use_doorbell) {
/* XXX check if swapping is necessary on BE */
adev->wb.wb[ring->wptr_offs] = ring->wptr << 2;
WDOORBELL32(ring->doorbell_index, ring->wptr << 2);
} else {
int me = (ring == &ring->adev->sdma[0].ring) ? 0 : 1;
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[me], ring->wptr << 2);
}
}
static void sdma_v3_0_hdp_flush_ring_emit(struct amdgpu_ring *);
/**
* sdma_v3_0_ring_emit_ib - Schedule an IB on the DMA engine
*
* @ring: amdgpu ring pointer
* @ib: IB object to schedule
*
* Schedule an IB in the DMA ring (VI).
*/
static void sdma_v3_0_ring_emit_ib(struct amdgpu_ring *ring,
struct amdgpu_ib *ib)
{
u32 vmid = (ib->vm ? ib->vm->ids[ring->idx].id : 0) & 0xf;
u32 next_rptr = ring->wptr + 5;
if (ib->flush_hdp_writefifo)
next_rptr += 6;
while ((next_rptr & 7) != 2)
next_rptr++;
next_rptr += 6;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
amdgpu_ring_write(ring, lower_32_bits(ring->next_rptr_gpu_addr) & 0xfffffffc);
amdgpu_ring_write(ring, upper_32_bits(ring->next_rptr_gpu_addr));
amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1));
amdgpu_ring_write(ring, next_rptr);
/* flush HDP */
if (ib->flush_hdp_writefifo) {
sdma_v3_0_hdp_flush_ring_emit(ring);
}
/* IB packet must end on a 8 DW boundary */
while ((ring->wptr & 7) != 2)
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_NOP));
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_INDIRECT) |
SDMA_PKT_INDIRECT_HEADER_VMID(vmid));
/* base must be 32 byte aligned */
amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr) & 0xffffffe0);
amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, ib->length_dw);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, 0);
}
/**
* sdma_v3_0_hdp_flush_ring_emit - emit an hdp flush on the DMA ring
*
* @ring: amdgpu ring pointer
*
* Emit an hdp flush packet on the requested DMA ring.
*/
static void sdma_v3_0_hdp_flush_ring_emit(struct amdgpu_ring *ring)
{
u32 ref_and_mask = 0;
if (ring == &ring->adev->sdma[0].ring)
ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA0, 1);
else
ref_and_mask = REG_SET_FIELD(ref_and_mask, GPU_HDP_FLUSH_DONE, SDMA1, 1);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(1) |
SDMA_PKT_POLL_REGMEM_HEADER_FUNC(3)); /* == */
amdgpu_ring_write(ring, mmGPU_HDP_FLUSH_DONE << 2);
amdgpu_ring_write(ring, mmGPU_HDP_FLUSH_REQ << 2);
amdgpu_ring_write(ring, ref_and_mask); /* reference */
amdgpu_ring_write(ring, ref_and_mask); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
}
/**
* sdma_v3_0_ring_emit_fence - emit a fence on the DMA ring
*
* @ring: amdgpu ring pointer
* @fence: amdgpu fence object
*
* Add a DMA fence packet to the ring to write
* the fence seq number and DMA trap packet to generate
* an interrupt if needed (VI).
*/
static void sdma_v3_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
bool write64bits)
{
/* write the fence */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, lower_32_bits(seq));
/* optionally write high bits as well */
if (write64bits) {
addr += 4;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_FENCE));
amdgpu_ring_write(ring, lower_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(addr));
amdgpu_ring_write(ring, upper_32_bits(seq));
}
/* generate an interrupt */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_TRAP));
amdgpu_ring_write(ring, SDMA_PKT_TRAP_INT_CONTEXT_INT_CONTEXT(0));
}
/**
* sdma_v3_0_ring_emit_semaphore - emit a semaphore on the dma ring
*
* @ring: amdgpu_ring structure holding ring information
* @semaphore: amdgpu semaphore object
* @emit_wait: wait or signal semaphore
*
* Add a DMA semaphore packet to the ring wait on or signal
* other rings (VI).
*/
static bool sdma_v3_0_ring_emit_semaphore(struct amdgpu_ring *ring,
struct amdgpu_semaphore *semaphore,
bool emit_wait)
{
u64 addr = semaphore->gpu_addr;
u32 sig = emit_wait ? 0 : 1;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SEM) |
SDMA_PKT_SEMAPHORE_HEADER_SIGNAL(sig));
amdgpu_ring_write(ring, lower_32_bits(addr) & 0xfffffff8);
amdgpu_ring_write(ring, upper_32_bits(addr));
return true;
}
/**
* sdma_v3_0_gfx_stop - stop the gfx async dma engines
*
* @adev: amdgpu_device pointer
*
* Stop the gfx async dma ring buffers (VI).
*/
static void sdma_v3_0_gfx_stop(struct amdgpu_device *adev)
{
struct amdgpu_ring *sdma0 = &adev->sdma[0].ring;
struct amdgpu_ring *sdma1 = &adev->sdma[1].ring;
u32 rb_cntl, ib_cntl;
int i;
if ((adev->mman.buffer_funcs_ring == sdma0) ||
(adev->mman.buffer_funcs_ring == sdma1))
amdgpu_ttm_set_active_vram_size(adev, adev->mc.visible_vram_size);
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
rb_cntl = RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 0);
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
ib_cntl = RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]);
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 0);
WREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i], ib_cntl);
}
sdma0->ready = false;
sdma1->ready = false;
}
/**
* sdma_v3_0_rlc_stop - stop the compute async dma engines
*
* @adev: amdgpu_device pointer
*
* Stop the compute async dma queues (VI).
*/
static void sdma_v3_0_rlc_stop(struct amdgpu_device *adev)
{
/* XXX todo */
}
/**
* sdma_v3_0_enable - stop the async dma engines
*
* @adev: amdgpu_device pointer
* @enable: enable/disable the DMA MEs.
*
* Halt or unhalt the async dma engines (VI).
*/
static void sdma_v3_0_enable(struct amdgpu_device *adev, bool enable)
{
u32 f32_cntl;
int i;
if (enable == false) {
sdma_v3_0_gfx_stop(adev);
sdma_v3_0_rlc_stop(adev);
}
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
f32_cntl = RREG32(mmSDMA0_F32_CNTL + sdma_offsets[i]);
if (enable)
f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, 0);
else
f32_cntl = REG_SET_FIELD(f32_cntl, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + sdma_offsets[i], f32_cntl);
}
}
/**
* sdma_v3_0_gfx_resume - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the gfx DMA ring buffers and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v3_0_gfx_resume(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
u32 rb_cntl, ib_cntl;
u32 rb_bufsz;
u32 wb_offset;
u32 doorbell;
int i, j, r;
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
ring = &adev->sdma[i].ring;
wb_offset = (ring->rptr_offs * 4);
mutex_lock(&adev->srbm_mutex);
for (j = 0; j < 16; j++) {
vi_srbm_select(adev, 0, 0, 0, j);
/* SDMA GFX */
WREG32(mmSDMA0_GFX_VIRTUAL_ADDR + sdma_offsets[i], 0);
WREG32(mmSDMA0_GFX_APE1_CNTL + sdma_offsets[i], 0);
}
vi_srbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
WREG32(mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL + sdma_offsets[i], 0);
/* Set ring buffer size in dwords */
rb_bufsz = order_base_2(ring->ring_size / 4);
rb_cntl = RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SIZE, rb_bufsz);
#ifdef __BIG_ENDIAN
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_SWAP_ENABLE, 1);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL,
RPTR_WRITEBACK_SWAP_ENABLE, 1);
#endif
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
/* Initialize the ring buffer's read and write pointers */
WREG32(mmSDMA0_GFX_RB_RPTR + sdma_offsets[i], 0);
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i], 0);
/* set the wb address whether it's enabled or not */
WREG32(mmSDMA0_GFX_RB_RPTR_ADDR_HI + sdma_offsets[i],
upper_32_bits(adev->wb.gpu_addr + wb_offset) & 0xFFFFFFFF);
WREG32(mmSDMA0_GFX_RB_RPTR_ADDR_LO + sdma_offsets[i],
lower_32_bits(adev->wb.gpu_addr + wb_offset) & 0xFFFFFFFC);
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RPTR_WRITEBACK_ENABLE, 1);
WREG32(mmSDMA0_GFX_RB_BASE + sdma_offsets[i], ring->gpu_addr >> 8);
WREG32(mmSDMA0_GFX_RB_BASE_HI + sdma_offsets[i], ring->gpu_addr >> 40);
ring->wptr = 0;
WREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i], ring->wptr << 2);
doorbell = RREG32(mmSDMA0_GFX_DOORBELL + sdma_offsets[i]);
if (ring->use_doorbell) {
doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL,
OFFSET, ring->doorbell_index);
doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 1);
} else {
doorbell = REG_SET_FIELD(doorbell, SDMA0_GFX_DOORBELL, ENABLE, 0);
}
WREG32(mmSDMA0_GFX_DOORBELL + sdma_offsets[i], doorbell);
/* enable DMA RB */
rb_cntl = REG_SET_FIELD(rb_cntl, SDMA0_GFX_RB_CNTL, RB_ENABLE, 1);
WREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i], rb_cntl);
ib_cntl = RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]);
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_ENABLE, 1);
#ifdef __BIG_ENDIAN
ib_cntl = REG_SET_FIELD(ib_cntl, SDMA0_GFX_IB_CNTL, IB_SWAP_ENABLE, 1);
#endif
/* enable DMA IBs */
WREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i], ib_cntl);
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
return r;
}
if (adev->mman.buffer_funcs_ring == ring)
amdgpu_ttm_set_active_vram_size(adev, adev->mc.real_vram_size);
}
return 0;
}
/**
* sdma_v3_0_rlc_resume - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the compute DMA queues and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v3_0_rlc_resume(struct amdgpu_device *adev)
{
/* XXX todo */
return 0;
}
/**
* sdma_v3_0_load_microcode - load the sDMA ME ucode
*
* @adev: amdgpu_device pointer
*
* Loads the sDMA0/1 ucode.
* Returns 0 for success, -EINVAL if the ucode is not available.
*/
static int sdma_v3_0_load_microcode(struct amdgpu_device *adev)
{
const struct sdma_firmware_header_v1_0 *hdr;
const __le32 *fw_data;
u32 fw_size;
int i, j;
if (!adev->sdma[0].fw || !adev->sdma[1].fw)
return -EINVAL;
/* halt the MEs */
sdma_v3_0_enable(adev, false);
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
hdr = (const struct sdma_firmware_header_v1_0 *)adev->sdma[i].fw->data;
amdgpu_ucode_print_sdma_hdr(&hdr->header);
fw_size = le32_to_cpu(hdr->header.ucode_size_bytes) / 4;
adev->sdma[i].fw_version = le32_to_cpu(hdr->header.ucode_version);
fw_data = (const __le32 *)
(adev->sdma[i].fw->data +
le32_to_cpu(hdr->header.ucode_array_offset_bytes));
WREG32(mmSDMA0_UCODE_ADDR + sdma_offsets[i], 0);
for (j = 0; j < fw_size; j++)
WREG32(mmSDMA0_UCODE_DATA + sdma_offsets[i], le32_to_cpup(fw_data++));
WREG32(mmSDMA0_UCODE_ADDR + sdma_offsets[i], adev->sdma[i].fw_version);
}
return 0;
}
/**
* sdma_v3_0_start - setup and start the async dma engines
*
* @adev: amdgpu_device pointer
*
* Set up the DMA engines and enable them (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v3_0_start(struct amdgpu_device *adev)
{
int r;
if (!adev->firmware.smu_load) {
r = sdma_v3_0_load_microcode(adev);
if (r)
return r;
} else {
r = adev->smu.smumgr_funcs->check_fw_load_finish(adev,
AMDGPU_UCODE_ID_SDMA0);
if (r)
return -EINVAL;
r = adev->smu.smumgr_funcs->check_fw_load_finish(adev,
AMDGPU_UCODE_ID_SDMA1);
if (r)
return -EINVAL;
}
/* unhalt the MEs */
sdma_v3_0_enable(adev, true);
/* start the gfx rings and rlc compute queues */
r = sdma_v3_0_gfx_resume(adev);
if (r)
return r;
r = sdma_v3_0_rlc_resume(adev);
if (r)
return r;
return 0;
}
/**
* sdma_v3_0_ring_test_ring - simple async dma engine test
*
* @ring: amdgpu_ring structure holding ring information
*
* Test the DMA engine by writing using it to write an
* value to memory. (VI).
* Returns 0 for success, error for failure.
*/
static int sdma_v3_0_ring_test_ring(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
unsigned i;
unsigned index;
int r;
u32 tmp;
u64 gpu_addr;
r = amdgpu_wb_get(adev, &index);
if (r) {
dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
return r;
}
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ring_lock(ring, 5);
if (r) {
DRM_ERROR("amdgpu: dma failed to lock ring %d (%d).\n", ring->idx, r);
amdgpu_wb_free(adev, index);
return r;
}
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR));
amdgpu_ring_write(ring, lower_32_bits(gpu_addr));
amdgpu_ring_write(ring, upper_32_bits(gpu_addr));
amdgpu_ring_write(ring, SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1));
amdgpu_ring_write(ring, 0xDEADBEEF);
amdgpu_ring_unlock_commit(ring);
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ring test on %d succeeded in %d usecs\n", ring->idx, i);
} else {
DRM_ERROR("amdgpu: ring %d test failed (0x%08X)\n",
ring->idx, tmp);
r = -EINVAL;
}
amdgpu_wb_free(adev, index);
return r;
}
/**
* sdma_v3_0_ring_test_ib - test an IB on the DMA engine
*
* @ring: amdgpu_ring structure holding ring information
*
* Test a simple IB in the DMA ring (VI).
* Returns 0 on success, error on failure.
*/
static int sdma_v3_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_ib ib;
unsigned i;
unsigned index;
int r;
u32 tmp = 0;
u64 gpu_addr;
r = amdgpu_wb_get(adev, &index);
if (r) {
dev_err(adev->dev, "(%d) failed to allocate wb slot\n", r);
return r;
}
gpu_addr = adev->wb.gpu_addr + (index * 4);
tmp = 0xCAFEDEAD;
adev->wb.wb[index] = cpu_to_le32(tmp);
r = amdgpu_ib_get(ring, NULL, 256, &ib);
if (r) {
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to get ib (%d).\n", r);
return r;
}
ib.ptr[0] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_WRITE_LINEAR);
ib.ptr[1] = lower_32_bits(gpu_addr);
ib.ptr[2] = upper_32_bits(gpu_addr);
ib.ptr[3] = SDMA_PKT_WRITE_UNTILED_DW_3_COUNT(1);
ib.ptr[4] = 0xDEADBEEF;
ib.ptr[5] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
ib.ptr[6] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
ib.ptr[7] = SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP);
ib.length_dw = 8;
r = amdgpu_ib_schedule(adev, 1, &ib, AMDGPU_FENCE_OWNER_UNDEFINED);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: failed to schedule ib (%d).\n", r);
return r;
}
r = amdgpu_fence_wait(ib.fence, false);
if (r) {
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
return r;
}
for (i = 0; i < adev->usec_timeout; i++) {
tmp = le32_to_cpu(adev->wb.wb[index]);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ib test on ring %d succeeded in %u usecs\n",
ib.fence->ring->idx, i);
} else {
DRM_ERROR("amdgpu: ib test failed (0x%08X)\n", tmp);
r = -EINVAL;
}
amdgpu_ib_free(adev, &ib);
amdgpu_wb_free(adev, index);
return r;
}
/**
* sdma_v3_0_vm_copy_pte - update PTEs by copying them from the GART
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @src: src addr to copy from
* @count: number of page entries to update
*
* Update PTEs by copying them from the GART using sDMA (CIK).
*/
static void sdma_v3_0_vm_copy_pte(struct amdgpu_ib *ib,
uint64_t pe, uint64_t src,
unsigned count)
{
while (count) {
unsigned bytes = count * 8;
if (bytes > 0x1FFFF8)
bytes = 0x1FFFF8;
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
ib->ptr[ib->length_dw++] = bytes;
ib->ptr[ib->length_dw++] = 0; /* src/dst endian swap */
ib->ptr[ib->length_dw++] = lower_32_bits(src);
ib->ptr[ib->length_dw++] = upper_32_bits(src);
ib->ptr[ib->length_dw++] = lower_32_bits(pe);
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
pe += bytes;
src += bytes;
count -= bytes / 8;
}
}
/**
* sdma_v3_0_vm_write_pte - update PTEs by writing them manually
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @addr: dst addr to write into pe
* @count: number of page entries to update
* @incr: increase next addr by incr bytes
* @flags: access flags
*
* Update PTEs by writing them manually using sDMA (CIK).
*/
static void sdma_v3_0_vm_write_pte(struct amdgpu_ib *ib,
uint64_t pe,
uint64_t addr, unsigned count,
uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
while (count) {
ndw = count * 2;
if (ndw > 0xFFFFE)
ndw = 0xFFFFE;
/* for non-physically contiguous pages (system) */
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_WRITE) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR);
ib->ptr[ib->length_dw++] = pe;
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
ib->ptr[ib->length_dw++] = ndw;
for (; ndw > 0; ndw -= 2, --count, pe += 8) {
if (flags & AMDGPU_PTE_SYSTEM) {
value = amdgpu_vm_map_gart(ib->ring->adev, addr);
value &= 0xFFFFFFFFFFFFF000ULL;
} else if (flags & AMDGPU_PTE_VALID) {
value = addr;
} else {
value = 0;
}
addr += incr;
value |= flags;
ib->ptr[ib->length_dw++] = value;
ib->ptr[ib->length_dw++] = upper_32_bits(value);
}
}
}
/**
* sdma_v3_0_vm_set_pte_pde - update the page tables using sDMA
*
* @ib: indirect buffer to fill with commands
* @pe: addr of the page entry
* @addr: dst addr to write into pe
* @count: number of page entries to update
* @incr: increase next addr by incr bytes
* @flags: access flags
*
* Update the page tables using sDMA (CIK).
*/
static void sdma_v3_0_vm_set_pte_pde(struct amdgpu_ib *ib,
uint64_t pe,
uint64_t addr, unsigned count,
uint32_t incr, uint32_t flags)
{
uint64_t value;
unsigned ndw;
while (count) {
ndw = count;
if (ndw > 0x7FFFF)
ndw = 0x7FFFF;
if (flags & AMDGPU_PTE_VALID)
value = addr;
else
value = 0;
/* for physically contiguous pages (vram) */
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_GEN_PTEPDE);
ib->ptr[ib->length_dw++] = pe; /* dst addr */
ib->ptr[ib->length_dw++] = upper_32_bits(pe);
ib->ptr[ib->length_dw++] = flags; /* mask */
ib->ptr[ib->length_dw++] = 0;
ib->ptr[ib->length_dw++] = value; /* value */
ib->ptr[ib->length_dw++] = upper_32_bits(value);
ib->ptr[ib->length_dw++] = incr; /* increment size */
ib->ptr[ib->length_dw++] = 0;
ib->ptr[ib->length_dw++] = ndw; /* number of entries */
pe += ndw * 8;
addr += ndw * incr;
count -= ndw;
}
}
/**
* sdma_v3_0_vm_pad_ib - pad the IB to the required number of dw
*
* @ib: indirect buffer to fill with padding
*
*/
static void sdma_v3_0_vm_pad_ib(struct amdgpu_ib *ib)
{
while (ib->length_dw & 0x7)
ib->ptr[ib->length_dw++] = SDMA_PKT_HEADER_OP(SDMA_OP_NOP);
}
/**
* sdma_v3_0_ring_emit_vm_flush - cik vm flush using sDMA
*
* @ring: amdgpu_ring pointer
* @vm: amdgpu_vm pointer
*
* Update the page table base and flush the VM TLB
* using sDMA (VI).
*/
static void sdma_v3_0_ring_emit_vm_flush(struct amdgpu_ring *ring,
unsigned vm_id, uint64_t pd_addr)
{
u32 srbm_gfx_cntl = 0;
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
if (vm_id < 8) {
amdgpu_ring_write(ring, (mmVM_CONTEXT0_PAGE_TABLE_BASE_ADDR + vm_id));
} else {
amdgpu_ring_write(ring, (mmVM_CONTEXT8_PAGE_TABLE_BASE_ADDR + vm_id - 8));
}
amdgpu_ring_write(ring, pd_addr >> 12);
/* update SH_MEM_* regs */
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, VMID, vm_id);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSRBM_GFX_CNTL);
amdgpu_ring_write(ring, srbm_gfx_cntl);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_BASES);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_CONFIG);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_APE1_BASE);
amdgpu_ring_write(ring, 1);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSH_MEM_APE1_LIMIT);
amdgpu_ring_write(ring, 0);
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, VMID, 0);
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmSRBM_GFX_CNTL);
amdgpu_ring_write(ring, srbm_gfx_cntl);
/* flush TLB */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_SRBM_WRITE) |
SDMA_PKT_SRBM_WRITE_HEADER_BYTE_EN(0xf));
amdgpu_ring_write(ring, mmVM_INVALIDATE_REQUEST);
amdgpu_ring_write(ring, 1 << vm_id);
/* wait for flush */
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_POLL_REGMEM) |
SDMA_PKT_POLL_REGMEM_HEADER_HDP_FLUSH(0) |
SDMA_PKT_POLL_REGMEM_HEADER_FUNC(0)); /* always */
amdgpu_ring_write(ring, mmVM_INVALIDATE_REQUEST << 2);
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, 0); /* reference */
amdgpu_ring_write(ring, 0); /* mask */
amdgpu_ring_write(ring, SDMA_PKT_POLL_REGMEM_DW5_RETRY_COUNT(0xfff) |
SDMA_PKT_POLL_REGMEM_DW5_INTERVAL(10)); /* retry count, poll interval */
}
static int sdma_v3_0_early_init(struct amdgpu_device *adev)
{
sdma_v3_0_set_ring_funcs(adev);
sdma_v3_0_set_buffer_funcs(adev);
sdma_v3_0_set_vm_pte_funcs(adev);
sdma_v3_0_set_irq_funcs(adev);
return 0;
}
static int sdma_v3_0_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
/* SDMA trap event */
r = amdgpu_irq_add_id(adev, 224, &adev->sdma_trap_irq);
if (r)
return r;
/* SDMA Privileged inst */
r = amdgpu_irq_add_id(adev, 241, &adev->sdma_illegal_inst_irq);
if (r)
return r;
/* SDMA Privileged inst */
r = amdgpu_irq_add_id(adev, 247, &adev->sdma_illegal_inst_irq);
if (r)
return r;
r = sdma_v3_0_init_microcode(adev);
if (r) {
DRM_ERROR("Failed to load sdma firmware!\n");
return r;
}
ring = &adev->sdma[0].ring;
ring->ring_obj = NULL;
ring->use_doorbell = true;
ring->doorbell_index = AMDGPU_DOORBELL_sDMA_ENGINE0;
ring = &adev->sdma[1].ring;
ring->ring_obj = NULL;
ring->use_doorbell = true;
ring->doorbell_index = AMDGPU_DOORBELL_sDMA_ENGINE1;
ring = &adev->sdma[0].ring;
sprintf(ring->name, "sdma0");
r = amdgpu_ring_init(adev, ring, 256 * 1024,
SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP), 0xf,
&adev->sdma_trap_irq, AMDGPU_SDMA_IRQ_TRAP0,
AMDGPU_RING_TYPE_SDMA);
if (r)
return r;
ring = &adev->sdma[1].ring;
sprintf(ring->name, "sdma1");
r = amdgpu_ring_init(adev, ring, 256 * 1024,
SDMA_PKT_NOP_HEADER_OP(SDMA_OP_NOP), 0xf,
&adev->sdma_trap_irq, AMDGPU_SDMA_IRQ_TRAP1,
AMDGPU_RING_TYPE_SDMA);
if (r)
return r;
return r;
}
static int sdma_v3_0_sw_fini(struct amdgpu_device *adev)
{
amdgpu_ring_fini(&adev->sdma[0].ring);
amdgpu_ring_fini(&adev->sdma[1].ring);
return 0;
}
static int sdma_v3_0_hw_init(struct amdgpu_device *adev)
{
int r;
sdma_v3_0_init_golden_registers(adev);
r = sdma_v3_0_start(adev);
if (r)
return r;
return r;
}
static int sdma_v3_0_hw_fini(struct amdgpu_device *adev)
{
sdma_v3_0_enable(adev, false);
return 0;
}
static int sdma_v3_0_suspend(struct amdgpu_device *adev)
{
return sdma_v3_0_hw_fini(adev);
}
static int sdma_v3_0_resume(struct amdgpu_device *adev)
{
return sdma_v3_0_hw_init(adev);
}
static bool sdma_v3_0_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS2);
if (tmp & (SRBM_STATUS2__SDMA_BUSY_MASK |
SRBM_STATUS2__SDMA1_BUSY_MASK))
return false;
return true;
}
static int sdma_v3_0_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(mmSRBM_STATUS2) & (SRBM_STATUS2__SDMA_BUSY_MASK |
SRBM_STATUS2__SDMA1_BUSY_MASK);
if (!tmp)
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void sdma_v3_0_print_status(struct amdgpu_device *adev)
{
int i, j;
dev_info(adev->dev, "VI SDMA registers\n");
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
for (i = 0; i < SDMA_MAX_INSTANCE; i++) {
dev_info(adev->dev, " SDMA%d_STATUS_REG=0x%08X\n",
i, RREG32(mmSDMA0_STATUS_REG + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_F32_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_F32_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_SEM_WAIT_FAIL_TIMER_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_SEM_WAIT_FAIL_TIMER_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_IB_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_IB_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_CNTL + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_WPTR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_WPTR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR_ADDR_HI=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR_ADDR_HI + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_RPTR_ADDR_LO=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_RPTR_ADDR_LO + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_BASE=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_BASE + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_RB_BASE_HI=0x%08X\n",
i, RREG32(mmSDMA0_GFX_RB_BASE_HI + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_DOORBELL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_DOORBELL + sdma_offsets[i]));
mutex_lock(&adev->srbm_mutex);
for (j = 0; j < 16; j++) {
vi_srbm_select(adev, 0, 0, 0, j);
dev_info(adev->dev, " VM %d:\n", j);
dev_info(adev->dev, " SDMA%d_GFX_VIRTUAL_ADDR=0x%08X\n",
i, RREG32(mmSDMA0_GFX_VIRTUAL_ADDR + sdma_offsets[i]));
dev_info(adev->dev, " SDMA%d_GFX_APE1_CNTL=0x%08X\n",
i, RREG32(mmSDMA0_GFX_APE1_CNTL + sdma_offsets[i]));
}
vi_srbm_select(adev, 0, 0, 0, 0);
mutex_unlock(&adev->srbm_mutex);
}
}
static int sdma_v3_0_soft_reset(struct amdgpu_device *adev)
{
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS2);
if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK) {
/* sdma0 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA_MASK;
}
if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK) {
/* sdma1 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 0);
WREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET, tmp);
srbm_soft_reset |= SRBM_SOFT_RESET__SOFT_RESET_SDMA1_MASK;
}
if (srbm_soft_reset) {
sdma_v3_0_print_status(adev);
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
sdma_v3_0_print_status(adev);
}
return 0;
}
static int sdma_v3_0_set_trap_irq_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
enum amdgpu_interrupt_state state)
{
u32 sdma_cntl;
switch (type) {
case AMDGPU_SDMA_IRQ_TRAP0:
switch (state) {
case AMDGPU_IRQ_STATE_DISABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 0);
WREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET, sdma_cntl);
break;
case AMDGPU_IRQ_STATE_ENABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 1);
WREG32(mmSDMA0_CNTL + SDMA0_REGISTER_OFFSET, sdma_cntl);
break;
default:
break;
}
break;
case AMDGPU_SDMA_IRQ_TRAP1:
switch (state) {
case AMDGPU_IRQ_STATE_DISABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 0);
WREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET, sdma_cntl);
break;
case AMDGPU_IRQ_STATE_ENABLE:
sdma_cntl = RREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET);
sdma_cntl = REG_SET_FIELD(sdma_cntl, SDMA0_CNTL, TRAP_ENABLE, 1);
WREG32(mmSDMA0_CNTL + SDMA1_REGISTER_OFFSET, sdma_cntl);
break;
default:
break;
}
break;
default:
break;
}
return 0;
}
static int sdma_v3_0_process_trap_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
u8 instance_id, queue_id;
instance_id = (entry->ring_id & 0x3) >> 0;
queue_id = (entry->ring_id & 0xc) >> 2;
DRM_DEBUG("IH: SDMA trap\n");
switch (instance_id) {
case 0:
switch (queue_id) {
case 0:
amdgpu_fence_process(&adev->sdma[0].ring);
break;
case 1:
/* XXX compute */
break;
case 2:
/* XXX compute */
break;
}
break;
case 1:
switch (queue_id) {
case 0:
amdgpu_fence_process(&adev->sdma[1].ring);
break;
case 1:
/* XXX compute */
break;
case 2:
/* XXX compute */
break;
}
break;
}
return 0;
}
static int sdma_v3_0_process_illegal_inst_irq(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
DRM_ERROR("Illegal instruction in SDMA command stream\n");
schedule_work(&adev->reset_work);
return 0;
}
static int sdma_v3_0_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
/* XXX handled via the smc on VI */
return 0;
}
static int sdma_v3_0_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs sdma_v3_0_ip_funcs = {
.early_init = sdma_v3_0_early_init,
.late_init = NULL,
.sw_init = sdma_v3_0_sw_init,
.sw_fini = sdma_v3_0_sw_fini,
.hw_init = sdma_v3_0_hw_init,
.hw_fini = sdma_v3_0_hw_fini,
.suspend = sdma_v3_0_suspend,
.resume = sdma_v3_0_resume,
.is_idle = sdma_v3_0_is_idle,
.wait_for_idle = sdma_v3_0_wait_for_idle,
.soft_reset = sdma_v3_0_soft_reset,
.print_status = sdma_v3_0_print_status,
.set_clockgating_state = sdma_v3_0_set_clockgating_state,
.set_powergating_state = sdma_v3_0_set_powergating_state,
};
/**
* sdma_v3_0_ring_is_lockup - Check if the DMA engine is locked up
*
* @ring: amdgpu_ring structure holding ring information
*
* Check if the async DMA engine is locked up (VI).
* Returns true if the engine appears to be locked up, false if not.
*/
static bool sdma_v3_0_ring_is_lockup(struct amdgpu_ring *ring)
{
if (sdma_v3_0_is_idle(ring->adev)) {
amdgpu_ring_lockup_update(ring);
return false;
}
return amdgpu_ring_test_lockup(ring);
}
static const struct amdgpu_ring_funcs sdma_v3_0_ring_funcs = {
.get_rptr = sdma_v3_0_ring_get_rptr,
.get_wptr = sdma_v3_0_ring_get_wptr,
.set_wptr = sdma_v3_0_ring_set_wptr,
.parse_cs = NULL,
.emit_ib = sdma_v3_0_ring_emit_ib,
.emit_fence = sdma_v3_0_ring_emit_fence,
.emit_semaphore = sdma_v3_0_ring_emit_semaphore,
.emit_vm_flush = sdma_v3_0_ring_emit_vm_flush,
.test_ring = sdma_v3_0_ring_test_ring,
.test_ib = sdma_v3_0_ring_test_ib,
.is_lockup = sdma_v3_0_ring_is_lockup,
};
static void sdma_v3_0_set_ring_funcs(struct amdgpu_device *adev)
{
adev->sdma[0].ring.funcs = &sdma_v3_0_ring_funcs;
adev->sdma[1].ring.funcs = &sdma_v3_0_ring_funcs;
}
static const struct amdgpu_irq_src_funcs sdma_v3_0_trap_irq_funcs = {
.set = sdma_v3_0_set_trap_irq_state,
.process = sdma_v3_0_process_trap_irq,
};
static const struct amdgpu_irq_src_funcs sdma_v3_0_illegal_inst_irq_funcs = {
.process = sdma_v3_0_process_illegal_inst_irq,
};
static void sdma_v3_0_set_irq_funcs(struct amdgpu_device *adev)
{
adev->sdma_trap_irq.num_types = AMDGPU_SDMA_IRQ_LAST;
adev->sdma_trap_irq.funcs = &sdma_v3_0_trap_irq_funcs;
adev->sdma_illegal_inst_irq.funcs = &sdma_v3_0_illegal_inst_irq_funcs;
}
/**
* sdma_v3_0_emit_copy_buffer - copy buffer using the sDMA engine
*
* @ring: amdgpu_ring structure holding ring information
* @src_offset: src GPU address
* @dst_offset: dst GPU address
* @byte_count: number of bytes to xfer
*
* Copy GPU buffers using the DMA engine (VI).
* Used by the amdgpu ttm implementation to move pages if
* registered as the asic copy callback.
*/
static void sdma_v3_0_emit_copy_buffer(struct amdgpu_ring *ring,
uint64_t src_offset,
uint64_t dst_offset,
uint32_t byte_count)
{
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_COPY) |
SDMA_PKT_HEADER_SUB_OP(SDMA_SUBOP_COPY_LINEAR));
amdgpu_ring_write(ring, byte_count);
amdgpu_ring_write(ring, 0); /* src/dst endian swap */
amdgpu_ring_write(ring, lower_32_bits(src_offset));
amdgpu_ring_write(ring, upper_32_bits(src_offset));
amdgpu_ring_write(ring, lower_32_bits(dst_offset));
amdgpu_ring_write(ring, upper_32_bits(dst_offset));
}
/**
* sdma_v3_0_emit_fill_buffer - fill buffer using the sDMA engine
*
* @ring: amdgpu_ring structure holding ring information
* @src_data: value to write to buffer
* @dst_offset: dst GPU address
* @byte_count: number of bytes to xfer
*
* Fill GPU buffers using the DMA engine (VI).
*/
static void sdma_v3_0_emit_fill_buffer(struct amdgpu_ring *ring,
uint32_t src_data,
uint64_t dst_offset,
uint32_t byte_count)
{
amdgpu_ring_write(ring, SDMA_PKT_HEADER_OP(SDMA_OP_CONST_FILL));
amdgpu_ring_write(ring, lower_32_bits(dst_offset));
amdgpu_ring_write(ring, upper_32_bits(dst_offset));
amdgpu_ring_write(ring, src_data);
amdgpu_ring_write(ring, byte_count);
}
static const struct amdgpu_buffer_funcs sdma_v3_0_buffer_funcs = {
.copy_max_bytes = 0x1fffff,
.copy_num_dw = 7,
.emit_copy_buffer = sdma_v3_0_emit_copy_buffer,
.fill_max_bytes = 0x1fffff,
.fill_num_dw = 5,
.emit_fill_buffer = sdma_v3_0_emit_fill_buffer,
};
static void sdma_v3_0_set_buffer_funcs(struct amdgpu_device *adev)
{
if (adev->mman.buffer_funcs == NULL) {
adev->mman.buffer_funcs = &sdma_v3_0_buffer_funcs;
adev->mman.buffer_funcs_ring = &adev->sdma[0].ring;
}
}
static const struct amdgpu_vm_pte_funcs sdma_v3_0_vm_pte_funcs = {
.copy_pte = sdma_v3_0_vm_copy_pte,
.write_pte = sdma_v3_0_vm_write_pte,
.set_pte_pde = sdma_v3_0_vm_set_pte_pde,
.pad_ib = sdma_v3_0_vm_pad_ib,
};
static void sdma_v3_0_set_vm_pte_funcs(struct amdgpu_device *adev)
{
if (adev->vm_manager.vm_pte_funcs == NULL) {
adev->vm_manager.vm_pte_funcs = &sdma_v3_0_vm_pte_funcs;
adev->vm_manager.vm_pte_funcs_ring = &adev->sdma[0].ring;
}
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __SDMA_V3_0_H__
#define __SDMA_V3_0_H__
extern const struct amdgpu_ip_funcs sdma_v3_0_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef SMU8_H
#define SMU8_H
#pragma pack(push, 1)
#define ENABLE_DEBUG_FEATURES
struct SMU8_Firmware_Header {
uint32_t Version;
uint32_t ImageSize;
uint32_t CodeSize;
uint32_t HeaderSize;
uint32_t EntryPoint;
uint32_t Rtos;
uint32_t UcodeLoadStatus;
uint32_t DpmTable;
uint32_t FanTable;
uint32_t PmFuseTable;
uint32_t Globals;
uint32_t Reserved[20];
uint32_t Signature;
};
struct SMU8_MultimediaPowerLogData {
uint32_t avgTotalPower;
uint32_t avgGpuPower;
uint32_t avgUvdPower;
uint32_t avgVcePower;
uint32_t avgSclk;
uint32_t avgDclk;
uint32_t avgVclk;
uint32_t avgEclk;
uint32_t startTimeHi;
uint32_t startTimeLo;
uint32_t endTimeHi;
uint32_t endTimeLo;
};
#define SMU8_FIRMWARE_HEADER_LOCATION 0x1FF80
#define SMU8_UNBCSR_START_ADDR 0xC0100000
#define SMN_MP1_SRAM_START_ADDR 0x10000000
#pragma pack(pop)
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef SMU8_FUSION_H
#define SMU8_FUSION_H
#include "smu8.h"
#pragma pack(push, 1)
#define SMU8_MAX_CUS 2
#define SMU8_PSMS_PER_CU 4
#define SMU8_CACS_PER_CU 4
struct SMU8_GfxCuPgScoreboard {
uint8_t Enabled;
uint8_t spare[3];
};
struct SMU8_Port80MonitorTable {
uint32_t MmioAddress;
uint32_t MemoryBaseHi;
uint32_t MemoryBaseLo;
uint16_t MemoryBufferSize;
uint16_t MemoryPosition;
uint16_t PollingInterval;
uint8_t EnableCsrShadow;
uint8_t EnableDramShadow;
};
/* Clock Table Definitions */
#define NUM_SCLK_LEVELS 8
#define NUM_LCLK_LEVELS 8
#define NUM_UVD_LEVELS 8
#define NUM_ECLK_LEVELS 8
#define NUM_ACLK_LEVELS 8
struct SMU8_Fusion_ClkLevel {
uint8_t GnbVid;
uint8_t GfxVid;
uint8_t DfsDid;
uint8_t DeepSleepDid;
uint32_t DfsBypass;
uint32_t Frequency;
};
struct SMU8_Fusion_SclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_SCLK_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
/* SMU8_Fusion_ClkLevel PwrOffLevel; */
uint32_t SclkValidMask;
uint32_t MaxSclkIndex;
};
struct SMU8_Fusion_LclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_LCLK_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
/* SMU8_Fusion_ClkLevel PwrOffLevel; */
uint32_t LclkValidMask;
uint32_t MaxLclkIndex;
};
struct SMU8_Fusion_EclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_ECLK_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
struct SMU8_Fusion_ClkLevel PwrOffLevel;
uint32_t EclkValidMask;
uint32_t MaxEclkIndex;
};
struct SMU8_Fusion_VclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_UVD_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
struct SMU8_Fusion_ClkLevel PwrOffLevel;
uint32_t VclkValidMask;
uint32_t MaxVclkIndex;
};
struct SMU8_Fusion_DclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_UVD_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
struct SMU8_Fusion_ClkLevel PwrOffLevel;
uint32_t DclkValidMask;
uint32_t MaxDclkIndex;
};
struct SMU8_Fusion_AclkBreakdownTable {
struct SMU8_Fusion_ClkLevel ClkLevel[NUM_ACLK_LEVELS];
struct SMU8_Fusion_ClkLevel DpmOffLevel;
struct SMU8_Fusion_ClkLevel PwrOffLevel;
uint32_t AclkValidMask;
uint32_t MaxAclkIndex;
};
struct SMU8_Fusion_ClkTable {
struct SMU8_Fusion_SclkBreakdownTable SclkBreakdownTable;
struct SMU8_Fusion_LclkBreakdownTable LclkBreakdownTable;
struct SMU8_Fusion_EclkBreakdownTable EclkBreakdownTable;
struct SMU8_Fusion_VclkBreakdownTable VclkBreakdownTable;
struct SMU8_Fusion_DclkBreakdownTable DclkBreakdownTable;
struct SMU8_Fusion_AclkBreakdownTable AclkBreakdownTable;
};
#pragma pack(pop)
#endif
// CZ Ucode Loading Definitions
#ifndef SMU_UCODE_XFER_CZ_H
#define SMU_UCODE_XFER_CZ_H
#define NUM_JOBLIST_ENTRIES 32
#define TASK_TYPE_NO_ACTION 0
#define TASK_TYPE_UCODE_LOAD 1
#define TASK_TYPE_UCODE_SAVE 2
#define TASK_TYPE_REG_LOAD 3
#define TASK_TYPE_REG_SAVE 4
#define TASK_TYPE_INITIALIZE 5
#define TASK_ARG_REG_SMCIND 0
#define TASK_ARG_REG_MMIO 1
#define TASK_ARG_REG_FCH 2
#define TASK_ARG_REG_UNB 3
#define TASK_ARG_INIT_MM_PWR_LOG 0
#define TASK_ARG_INIT_CLK_TABLE 1
#define JOB_GFX_SAVE 0
#define JOB_GFX_RESTORE 1
#define JOB_FCH_SAVE 2
#define JOB_FCH_RESTORE 3
#define JOB_UNB_SAVE 4
#define JOB_UNB_RESTORE 5
#define JOB_GMC_SAVE 6
#define JOB_GMC_RESTORE 7
#define JOB_GNB_SAVE 8
#define JOB_GNB_RESTORE 9
#define IGNORE_JOB 0xff
#define END_OF_TASK_LIST (uint16_t)0xffff
// Size of DRAM regions (in bytes) requested by SMU:
#define SMU_DRAM_REQ_MM_PWR_LOG 48
#define UCODE_ID_SDMA0 0
#define UCODE_ID_SDMA1 1
#define UCODE_ID_CP_CE 2
#define UCODE_ID_CP_PFP 3
#define UCODE_ID_CP_ME 4
#define UCODE_ID_CP_MEC_JT1 5
#define UCODE_ID_CP_MEC_JT2 6
#define UCODE_ID_GMCON_RENG 7
#define UCODE_ID_RLC_G 8
#define UCODE_ID_RLC_SCRATCH 9
#define UCODE_ID_RLC_SRM_ARAM 10
#define UCODE_ID_RLC_SRM_DRAM 11
#define UCODE_ID_DMCU_ERAM 12
#define UCODE_ID_DMCU_IRAM 13
#define UCODE_ID_SDMA0_MASK 0x00000001
#define UCODE_ID_SDMA1_MASK 0x00000002
#define UCODE_ID_CP_CE_MASK 0x00000004
#define UCODE_ID_CP_PFP_MASK 0x00000008
#define UCODE_ID_CP_ME_MASK 0x00000010
#define UCODE_ID_CP_MEC_JT1_MASK 0x00000020
#define UCODE_ID_CP_MEC_JT2_MASK 0x00000040
#define UCODE_ID_GMCON_RENG_MASK 0x00000080
#define UCODE_ID_RLC_G_MASK 0x00000100
#define UCODE_ID_RLC_SCRATCH_MASK 0x00000200
#define UCODE_ID_RLC_SRM_ARAM_MASK 0x00000400
#define UCODE_ID_RLC_SRM_DRAM_MASK 0x00000800
#define UCODE_ID_DMCU_ERAM_MASK 0x00001000
#define UCODE_ID_DMCU_IRAM_MASK 0x00002000
#define UCODE_ID_SDMA0_SIZE_BYTE 10368
#define UCODE_ID_SDMA1_SIZE_BYTE 10368
#define UCODE_ID_CP_CE_SIZE_BYTE 8576
#define UCODE_ID_CP_PFP_SIZE_BYTE 16768
#define UCODE_ID_CP_ME_SIZE_BYTE 16768
#define UCODE_ID_CP_MEC_JT1_SIZE_BYTE 384
#define UCODE_ID_CP_MEC_JT2_SIZE_BYTE 384
#define UCODE_ID_GMCON_RENG_SIZE_BYTE 4096
#define UCODE_ID_RLC_G_SIZE_BYTE 2048
#define UCODE_ID_RLC_SCRATCH_SIZE_BYTE 132
#define UCODE_ID_RLC_SRM_ARAM_SIZE_BYTE 8192
#define UCODE_ID_RLC_SRM_DRAM_SIZE_BYTE 4096
#define UCODE_ID_DMCU_ERAM_SIZE_BYTE 24576
#define UCODE_ID_DMCU_IRAM_SIZE_BYTE 1024
#define NUM_UCODES 14
typedef struct {
uint32_t high;
uint32_t low;
} data_64_t;
struct SMU_Task {
uint8_t type;
uint8_t arg;
uint16_t next;
data_64_t addr;
uint32_t size_bytes;
};
typedef struct SMU_Task SMU_Task;
struct TOC {
uint8_t JobList[NUM_JOBLIST_ENTRIES];
SMU_Task tasks[1];
};
// META DATA COMMAND Definitions
#define METADATA_CMD_MODE0 0x00000103
#define METADATA_CMD_MODE1 0x00000113
#define METADATA_CMD_MODE2 0x00000123
#define METADATA_CMD_MODE3 0x00000133
#define METADATA_CMD_DELAY 0x00000203
#define METADATA_CMD_CHNG_REGSPACE 0x00000303
#define METADATA_PERFORM_ON_SAVE 0x00001000
#define METADATA_PERFORM_ON_LOAD 0x00002000
#define METADATA_CMD_ARG_MASK 0xFFFF0000
#define METADATA_CMD_ARG_SHIFT 16
// Simple register addr/data fields
struct SMU_MetaData_Mode0 {
uint32_t register_address;
uint32_t register_data;
};
typedef struct SMU_MetaData_Mode0 SMU_MetaData_Mode0;
// Register addr/data with mask
struct SMU_MetaData_Mode1 {
uint32_t register_address;
uint32_t register_mask;
uint32_t register_data;
};
typedef struct SMU_MetaData_Mode1 SMU_MetaData_Mode1;
struct SMU_MetaData_Mode2 {
uint32_t register_address;
uint32_t register_mask;
uint32_t target_value;
};
typedef struct SMU_MetaData_Mode2 SMU_MetaData_Mode2;
// Always write data (even on a save operation)
struct SMU_MetaData_Mode3 {
uint32_t register_address;
uint32_t register_mask;
uint32_t register_data;
};
typedef struct SMU_MetaData_Mode3 SMU_MetaData_Mode3;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef SMU_UCODE_XFER_VI_H
#define SMU_UCODE_XFER_VI_H
#define SMU_DRAMData_TOC_VERSION 1
#define MAX_IH_REGISTER_COUNT 65535
#define SMU_DIGEST_SIZE_BYTES 20
#define SMU_FB_SIZE_BYTES 1048576
#define SMU_MAX_ENTRIES 12
#define UCODE_ID_SMU 0
#define UCODE_ID_SDMA0 1
#define UCODE_ID_SDMA1 2
#define UCODE_ID_CP_CE 3
#define UCODE_ID_CP_PFP 4
#define UCODE_ID_CP_ME 5
#define UCODE_ID_CP_MEC 6
#define UCODE_ID_CP_MEC_JT1 7
#define UCODE_ID_CP_MEC_JT2 8
#define UCODE_ID_GMCON_RENG 9
#define UCODE_ID_RLC_G 10
#define UCODE_ID_IH_REG_RESTORE 11
#define UCODE_ID_VBIOS 12
#define UCODE_ID_MISC_METADATA 13
#define UCODE_ID_RLC_SCRATCH 32
#define UCODE_ID_RLC_SRM_ARAM 33
#define UCODE_ID_RLC_SRM_DRAM 34
#define UCODE_ID_MEC_STORAGE 35
#define UCODE_ID_VBIOS_PARAMETERS 36
#define UCODE_META_DATA 0xFF
#define UCODE_ID_SMU_MASK 0x00000001
#define UCODE_ID_SDMA0_MASK 0x00000002
#define UCODE_ID_SDMA1_MASK 0x00000004
#define UCODE_ID_CP_CE_MASK 0x00000008
#define UCODE_ID_CP_PFP_MASK 0x00000010
#define UCODE_ID_CP_ME_MASK 0x00000020
#define UCODE_ID_CP_MEC_MASK 0x00000040
#define UCODE_ID_CP_MEC_JT1_MASK 0x00000080
#define UCODE_ID_CP_MEC_JT2_MASK 0x00000100
#define UCODE_ID_GMCON_RENG_MASK 0x00000200
#define UCODE_ID_RLC_G_MASK 0x00000400
#define UCODE_ID_IH_REG_RESTORE_MASK 0x00000800
#define UCODE_ID_VBIOS_MASK 0x00001000
#define UCODE_FLAG_UNHALT_MASK 0x1
struct SMU_Entry {
#ifndef __BIG_ENDIAN
uint16_t id;
uint16_t version;
uint32_t image_addr_high;
uint32_t image_addr_low;
uint32_t meta_data_addr_high;
uint32_t meta_data_addr_low;
uint32_t data_size_byte;
uint16_t flags;
uint16_t num_register_entries;
#else
uint16_t version;
uint16_t id;
uint32_t image_addr_high;
uint32_t image_addr_low;
uint32_t meta_data_addr_high;
uint32_t meta_data_addr_low;
uint32_t data_size_byte;
uint16_t num_register_entries;
uint16_t flags;
#endif
};
struct SMU_DRAMData_TOC {
uint32_t structure_version;
uint32_t num_entries;
struct SMU_Entry entry[SMU_MAX_ENTRIES];
};
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "tonga_smumgr.h"
MODULE_FIRMWARE("radeon/tonga_smc.bin");
static void tonga_dpm_set_funcs(struct amdgpu_device *adev);
static int tonga_dpm_early_init(struct amdgpu_device *adev)
{
tonga_dpm_set_funcs(adev);
return 0;
}
static int tonga_dpm_init_microcode(struct amdgpu_device *adev)
{
char fw_name[30] = "radeon/tonga_smc.bin";
int err;
err = request_firmware(&adev->pm.fw, fw_name, adev->dev);
if (err)
goto out;
err = amdgpu_ucode_validate(adev->pm.fw);
out:
if (err) {
DRM_ERROR("Failed to load firmware \"%s\"", fw_name);
release_firmware(adev->pm.fw);
adev->pm.fw = NULL;
}
return err;
}
static int tonga_dpm_sw_init(struct amdgpu_device *adev)
{
int ret;
ret = tonga_dpm_init_microcode(adev);
if (ret)
return ret;
return 0;
}
static int tonga_dpm_sw_fini(struct amdgpu_device *adev)
{
return 0;
}
static int tonga_dpm_hw_init(struct amdgpu_device *adev)
{
int ret;
mutex_lock(&adev->pm.mutex);
ret = tonga_smu_init(adev);
if (ret) {
DRM_ERROR("SMU initialization failed\n");
goto fail;
}
ret = tonga_smu_start(adev);
if (ret) {
DRM_ERROR("SMU start failed\n");
goto fail;
}
mutex_unlock(&adev->pm.mutex);
return 0;
fail:
adev->firmware.smu_load = false;
mutex_unlock(&adev->pm.mutex);
return -EINVAL;
}
static int tonga_dpm_hw_fini(struct amdgpu_device *adev)
{
mutex_lock(&adev->pm.mutex);
tonga_smu_fini(adev);
mutex_unlock(&adev->pm.mutex);
return 0;
}
static int tonga_dpm_suspend(struct amdgpu_device *adev)
{
tonga_dpm_hw_fini(adev);
return 0;
}
static int tonga_dpm_resume(struct amdgpu_device *adev)
{
tonga_dpm_hw_init(adev);
return 0;
}
static int tonga_dpm_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
return 0;
}
static int tonga_dpm_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs tonga_dpm_ip_funcs = {
.early_init = tonga_dpm_early_init,
.late_init = NULL,
.sw_init = tonga_dpm_sw_init,
.sw_fini = tonga_dpm_sw_fini,
.hw_init = tonga_dpm_hw_init,
.hw_fini = tonga_dpm_hw_fini,
.suspend = tonga_dpm_suspend,
.resume = tonga_dpm_resume,
.is_idle = NULL,
.wait_for_idle = NULL,
.soft_reset = NULL,
.print_status = NULL,
.set_clockgating_state = tonga_dpm_set_clockgating_state,
.set_powergating_state = tonga_dpm_set_powergating_state,
};
static const struct amdgpu_dpm_funcs tonga_dpm_funcs = {
.get_temperature = NULL,
.pre_set_power_state = NULL,
.set_power_state = NULL,
.post_set_power_state = NULL,
.display_configuration_changed = NULL,
.get_sclk = NULL,
.get_mclk = NULL,
.print_power_state = NULL,
.debugfs_print_current_performance_level = NULL,
.force_performance_level = NULL,
.vblank_too_short = NULL,
.powergate_uvd = NULL,
};
static void tonga_dpm_set_funcs(struct amdgpu_device *adev)
{
if (NULL == adev->pm.funcs)
adev->pm.funcs = &tonga_dpm_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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 "drmP.h"
#include "amdgpu.h"
#include "amdgpu_ih.h"
#include "vid.h"
#include "oss/oss_3_0_d.h"
#include "oss/oss_3_0_sh_mask.h"
#include "bif/bif_5_1_d.h"
#include "bif/bif_5_1_sh_mask.h"
/*
* Interrupts
* Starting with r6xx, interrupts are handled via a ring buffer.
* Ring buffers are areas of GPU accessible memory that the GPU
* writes interrupt vectors into and the host reads vectors out of.
* There is a rptr (read pointer) that determines where the
* host is currently reading, and a wptr (write pointer)
* which determines where the GPU has written. When the
* pointers are equal, the ring is idle. When the GPU
* writes vectors to the ring buffer, it increments the
* wptr. When there is an interrupt, the host then starts
* fetching commands and processing them until the pointers are
* equal again at which point it updates the rptr.
*/
static void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev);
/**
* tonga_ih_enable_interrupts - Enable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Enable the interrupt ring buffer (VI).
*/
static void tonga_ih_enable_interrupts(struct amdgpu_device *adev)
{
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 1);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
adev->irq.ih.enabled = true;
}
/**
* tonga_ih_disable_interrupts - Disable the interrupt ring buffer
*
* @adev: amdgpu_device pointer
*
* Disable the interrupt ring buffer (VI).
*/
static void tonga_ih_disable_interrupts(struct amdgpu_device *adev)
{
u32 ih_rb_cntl = RREG32(mmIH_RB_CNTL);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_ENABLE, 0);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, ENABLE_INTR, 0);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
adev->irq.ih.enabled = false;
adev->irq.ih.rptr = 0;
}
/**
* tonga_ih_irq_init - init and enable the interrupt ring
*
* @adev: amdgpu_device pointer
*
* Allocate a ring buffer for the interrupt controller,
* enable the RLC, disable interrupts, enable the IH
* ring buffer and enable it (VI).
* Called at device load and reume.
* Returns 0 for success, errors for failure.
*/
static int tonga_ih_irq_init(struct amdgpu_device *adev)
{
int ret = 0;
int rb_bufsz;
u32 interrupt_cntl, ih_rb_cntl, ih_doorbell_rtpr;
u64 wptr_off;
/* disable irqs */
tonga_ih_disable_interrupts(adev);
/* setup interrupt control */
WREG32(mmINTERRUPT_CNTL2, adev->dummy_page.addr >> 8);
interrupt_cntl = RREG32(mmINTERRUPT_CNTL);
/* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi
* INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN
*/
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_DUMMY_RD_OVERRIDE, 0);
/* INTERRUPT_CNTL__IH_REQ_NONSNOOP_EN_MASK=1 if ring is in non-cacheable memory, e.g., vram */
interrupt_cntl = REG_SET_FIELD(interrupt_cntl, INTERRUPT_CNTL, IH_REQ_NONSNOOP_EN, 0);
WREG32(mmINTERRUPT_CNTL, interrupt_cntl);
/* Ring Buffer base. [39:8] of 40-bit address of the beginning of the ring buffer*/
if (adev->irq.ih.use_bus_addr)
WREG32(mmIH_RB_BASE, adev->irq.ih.rb_dma_addr >> 8);
else
WREG32(mmIH_RB_BASE, adev->irq.ih.gpu_addr >> 8);
rb_bufsz = order_base_2(adev->irq.ih.ring_size / 4);
ih_rb_cntl = REG_SET_FIELD(0, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RB_SIZE, rb_bufsz);
/* Ring Buffer write pointer writeback. If enabled, IH_RB_WPTR register value is written to memory */
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, WPTR_WRITEBACK_ENABLE, 1);
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, MC_VMID, 0);
if (adev->irq.msi_enabled)
ih_rb_cntl = REG_SET_FIELD(ih_rb_cntl, IH_RB_CNTL, RPTR_REARM, 1);
WREG32(mmIH_RB_CNTL, ih_rb_cntl);
/* set the writeback address whether it's enabled or not */
if (adev->irq.ih.use_bus_addr)
wptr_off = adev->irq.ih.rb_dma_addr + (adev->irq.ih.wptr_offs * 4);
else
wptr_off = adev->wb.gpu_addr + (adev->irq.ih.wptr_offs * 4);
WREG32(mmIH_RB_WPTR_ADDR_LO, lower_32_bits(wptr_off));
WREG32(mmIH_RB_WPTR_ADDR_HI, upper_32_bits(wptr_off) & 0xFF);
/* set rptr, wptr to 0 */
WREG32(mmIH_RB_RPTR, 0);
WREG32(mmIH_RB_WPTR, 0);
ih_doorbell_rtpr = RREG32(mmIH_DOORBELL_RPTR);
if (adev->irq.ih.use_doorbell) {
ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
OFFSET, adev->irq.ih.doorbell_index);
ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
ENABLE, 1);
} else {
ih_doorbell_rtpr = REG_SET_FIELD(ih_doorbell_rtpr, IH_DOORBELL_RPTR,
ENABLE, 0);
}
WREG32(mmIH_DOORBELL_RPTR, ih_doorbell_rtpr);
pci_set_master(adev->pdev);
/* enable interrupts */
tonga_ih_enable_interrupts(adev);
return ret;
}
/**
* tonga_ih_irq_disable - disable interrupts
*
* @adev: amdgpu_device pointer
*
* Disable interrupts on the hw (VI).
*/
static void tonga_ih_irq_disable(struct amdgpu_device *adev)
{
tonga_ih_disable_interrupts(adev);
/* Wait and acknowledge irq */
mdelay(1);
}
/**
* tonga_ih_get_wptr - get the IH ring buffer wptr
*
* @adev: amdgpu_device pointer
*
* Get the IH ring buffer wptr from either the register
* or the writeback memory buffer (VI). Also check for
* ring buffer overflow and deal with it.
* Used by cz_irq_process(VI).
* Returns the value of the wptr.
*/
static u32 tonga_ih_get_wptr(struct amdgpu_device *adev)
{
u32 wptr, tmp;
if (adev->irq.ih.use_bus_addr)
wptr = le32_to_cpu(adev->irq.ih.ring[adev->irq.ih.wptr_offs]);
else
wptr = le32_to_cpu(adev->wb.wb[adev->irq.ih.wptr_offs]);
if (REG_GET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW)) {
wptr = REG_SET_FIELD(wptr, IH_RB_WPTR, RB_OVERFLOW, 0);
/* When a ring buffer overflow happen start parsing interrupt
* from the last not overwritten vector (wptr + 16). Hopefully
* this should allow us to catchup.
*/
dev_warn(adev->dev, "IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X)\n",
wptr, adev->irq.ih.rptr, (wptr + 16) & adev->irq.ih.ptr_mask);
adev->irq.ih.rptr = (wptr + 16) & adev->irq.ih.ptr_mask;
tmp = RREG32(mmIH_RB_CNTL);
tmp = REG_SET_FIELD(tmp, IH_RB_CNTL, WPTR_OVERFLOW_CLEAR, 1);
WREG32(mmIH_RB_CNTL, tmp);
}
return (wptr & adev->irq.ih.ptr_mask);
}
/**
* tonga_ih_decode_iv - decode an interrupt vector
*
* @adev: amdgpu_device pointer
*
* Decodes the interrupt vector at the current rptr
* position and also advance the position.
*/
static void tonga_ih_decode_iv(struct amdgpu_device *adev,
struct amdgpu_iv_entry *entry)
{
/* wptr/rptr are in bytes! */
u32 ring_index = adev->irq.ih.rptr >> 2;
uint32_t dw[4];
dw[0] = le32_to_cpu(adev->irq.ih.ring[ring_index + 0]);
dw[1] = le32_to_cpu(adev->irq.ih.ring[ring_index + 1]);
dw[2] = le32_to_cpu(adev->irq.ih.ring[ring_index + 2]);
dw[3] = le32_to_cpu(adev->irq.ih.ring[ring_index + 3]);
entry->src_id = dw[0] & 0xff;
entry->src_data = dw[1] & 0xfffffff;
entry->ring_id = dw[2] & 0xff;
entry->vm_id = (dw[2] >> 8) & 0xff;
entry->pas_id = (dw[2] >> 16) & 0xffff;
/* wptr/rptr are in bytes! */
adev->irq.ih.rptr += 16;
}
/**
* tonga_ih_set_rptr - set the IH ring buffer rptr
*
* @adev: amdgpu_device pointer
*
* Set the IH ring buffer rptr.
*/
static void tonga_ih_set_rptr(struct amdgpu_device *adev)
{
if (adev->irq.ih.use_doorbell) {
/* XXX check if swapping is necessary on BE */
if (adev->irq.ih.use_bus_addr)
adev->irq.ih.ring[adev->irq.ih.rptr_offs] = adev->irq.ih.rptr;
else
adev->wb.wb[adev->irq.ih.rptr_offs] = adev->irq.ih.rptr;
WDOORBELL32(adev->irq.ih.doorbell_index, adev->irq.ih.rptr);
} else {
WREG32(mmIH_RB_RPTR, adev->irq.ih.rptr);
}
}
static int tonga_ih_early_init(struct amdgpu_device *adev)
{
tonga_ih_set_interrupt_funcs(adev);
return 0;
}
static int tonga_ih_sw_init(struct amdgpu_device *adev)
{
int r;
r = amdgpu_ih_ring_init(adev, 4 * 1024, true);
if (r)
return r;
adev->irq.ih.use_doorbell = true;
adev->irq.ih.doorbell_index = AMDGPU_DOORBELL_IH;
r = amdgpu_irq_init(adev);
return r;
}
static int tonga_ih_sw_fini(struct amdgpu_device *adev)
{
amdgpu_irq_fini(adev);
amdgpu_ih_ring_fini(adev);
return 0;
}
static int tonga_ih_hw_init(struct amdgpu_device *adev)
{
int r;
r = tonga_ih_irq_init(adev);
if (r)
return r;
return 0;
}
static int tonga_ih_hw_fini(struct amdgpu_device *adev)
{
tonga_ih_irq_disable(adev);
return 0;
}
static int tonga_ih_suspend(struct amdgpu_device *adev)
{
return tonga_ih_hw_fini(adev);
}
static int tonga_ih_resume(struct amdgpu_device *adev)
{
return tonga_ih_hw_init(adev);
}
static bool tonga_ih_is_idle(struct amdgpu_device *adev)
{
u32 tmp = RREG32(mmSRBM_STATUS);
if (REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return false;
return true;
}
static int tonga_ih_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
u32 tmp;
for (i = 0; i < adev->usec_timeout; i++) {
/* read MC_STATUS */
tmp = RREG32(mmSRBM_STATUS);
if (!REG_GET_FIELD(tmp, SRBM_STATUS, IH_BUSY))
return 0;
udelay(1);
}
return -ETIMEDOUT;
}
static void tonga_ih_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "TONGA IH registers\n");
dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
RREG32(mmSRBM_STATUS));
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
dev_info(adev->dev, " INTERRUPT_CNTL=0x%08X\n",
RREG32(mmINTERRUPT_CNTL));
dev_info(adev->dev, " INTERRUPT_CNTL2=0x%08X\n",
RREG32(mmINTERRUPT_CNTL2));
dev_info(adev->dev, " IH_CNTL=0x%08X\n",
RREG32(mmIH_CNTL));
dev_info(adev->dev, " IH_RB_CNTL=0x%08X\n",
RREG32(mmIH_RB_CNTL));
dev_info(adev->dev, " IH_RB_BASE=0x%08X\n",
RREG32(mmIH_RB_BASE));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_LO=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_LO));
dev_info(adev->dev, " IH_RB_WPTR_ADDR_HI=0x%08X\n",
RREG32(mmIH_RB_WPTR_ADDR_HI));
dev_info(adev->dev, " IH_RB_RPTR=0x%08X\n",
RREG32(mmIH_RB_RPTR));
dev_info(adev->dev, " IH_RB_WPTR=0x%08X\n",
RREG32(mmIH_RB_WPTR));
}
static int tonga_ih_soft_reset(struct amdgpu_device *adev)
{
u32 srbm_soft_reset = 0;
u32 tmp = RREG32(mmSRBM_STATUS);
if (tmp & SRBM_STATUS__IH_BUSY_MASK)
srbm_soft_reset = REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET,
SOFT_RESET_IH, 1);
if (srbm_soft_reset) {
tonga_ih_print_status(adev);
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
/* Wait a little for things to settle down */
udelay(50);
tonga_ih_print_status(adev);
}
return 0;
}
static int tonga_ih_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
// TODO
return 0;
}
static int tonga_ih_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
// TODO
return 0;
}
const struct amdgpu_ip_funcs tonga_ih_ip_funcs = {
.early_init = tonga_ih_early_init,
.late_init = NULL,
.sw_init = tonga_ih_sw_init,
.sw_fini = tonga_ih_sw_fini,
.hw_init = tonga_ih_hw_init,
.hw_fini = tonga_ih_hw_fini,
.suspend = tonga_ih_suspend,
.resume = tonga_ih_resume,
.is_idle = tonga_ih_is_idle,
.wait_for_idle = tonga_ih_wait_for_idle,
.soft_reset = tonga_ih_soft_reset,
.print_status = tonga_ih_print_status,
.set_clockgating_state = tonga_ih_set_clockgating_state,
.set_powergating_state = tonga_ih_set_powergating_state,
};
static const struct amdgpu_ih_funcs tonga_ih_funcs = {
.get_wptr = tonga_ih_get_wptr,
.decode_iv = tonga_ih_decode_iv,
.set_rptr = tonga_ih_set_rptr
};
static void tonga_ih_set_interrupt_funcs(struct amdgpu_device *adev)
{
if (adev->irq.ih_funcs == NULL)
adev->irq.ih_funcs = &tonga_ih_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __TONGA_IH_H__
#define __TONGA_IH_H__
extern const struct amdgpu_ip_funcs tonga_ih_ip_funcs;
#endif /* __CZ_IH_H__ */
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef TONGA_PP_SMC_H
#define TONGA_PP_SMC_H
#pragma pack(push, 1)
#define PPSMC_SWSTATE_FLAG_DC 0x01
#define PPSMC_SWSTATE_FLAG_UVD 0x02
#define PPSMC_SWSTATE_FLAG_VCE 0x04
#define PPSMC_SWSTATE_FLAG_PCIE_X1 0x08
#define PPSMC_THERMAL_PROTECT_TYPE_INTERNAL 0x00
#define PPSMC_THERMAL_PROTECT_TYPE_EXTERNAL 0x01
#define PPSMC_THERMAL_PROTECT_TYPE_NONE 0xff
#define PPSMC_SYSTEMFLAG_GPIO_DC 0x01
#define PPSMC_SYSTEMFLAG_STEPVDDC 0x02
#define PPSMC_SYSTEMFLAG_GDDR5 0x04
#define PPSMC_SYSTEMFLAG_DISABLE_BABYSTEP 0x08
#define PPSMC_SYSTEMFLAG_REGULATOR_HOT 0x10
#define PPSMC_SYSTEMFLAG_REGULATOR_HOT_ANALOG 0x20
#define PPSMC_SYSTEMFLAG_12CHANNEL 0x40
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_MASK 0x07
#define PPSMC_EXTRAFLAGS_AC2DC_DONT_WAIT_FOR_VBLANK 0x08
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTODPMLOWSTATE 0x00
#define PPSMC_EXTRAFLAGS_AC2DC_ACTION_GOTOINITIALSTATE 0x01
#define PPSMC_EXTRAFLAGS_AC2DC_GPIO5_POLARITY_HIGH 0x10
#define PPSMC_EXTRAFLAGS_DRIVER_TO_GPIO17 0x20
#define PPSMC_EXTRAFLAGS_PCC_TO_GPIO17 0x40
#define PPSMC_DPM2FLAGS_TDPCLMP 0x01
#define PPSMC_DPM2FLAGS_PWRSHFT 0x02
#define PPSMC_DPM2FLAGS_OCP 0x04
#define PPSMC_DISPLAY_WATERMARK_LOW 0
#define PPSMC_DISPLAY_WATERMARK_HIGH 1
#define PPSMC_STATEFLAG_AUTO_PULSE_SKIP 0x01
#define PPSMC_STATEFLAG_POWERBOOST 0x02
#define PPSMC_STATEFLAG_PSKIP_ON_TDP_FAULT 0x04
#define PPSMC_STATEFLAG_POWERSHIFT 0x08
#define PPSMC_STATEFLAG_SLOW_READ_MARGIN 0x10
#define PPSMC_STATEFLAG_DEEPSLEEP_THROTTLE 0x20
#define PPSMC_STATEFLAG_DEEPSLEEP_BYPASS 0x40
#define FDO_MODE_HARDWARE 0
#define FDO_MODE_PIECE_WISE_LINEAR 1
enum FAN_CONTROL {
FAN_CONTROL_FUZZY,
FAN_CONTROL_TABLE
};
#define PPSMC_Result_OK ((uint16_t)0x01)
#define PPSMC_Result_NoMore ((uint16_t)0x02)
#define PPSMC_Result_NotNow ((uint16_t)0x03)
#define PPSMC_Result_Failed ((uint16_t)0xFF)
#define PPSMC_Result_UnknownCmd ((uint16_t)0xFE)
#define PPSMC_Result_UnknownVT ((uint16_t)0xFD)
typedef uint16_t PPSMC_Result;
#define PPSMC_isERROR(x) ((uint16_t)0x80 & (x))
#define PPSMC_MSG_Halt ((uint16_t)0x10)
#define PPSMC_MSG_Resume ((uint16_t)0x11)
#define PPSMC_MSG_EnableDPMLevel ((uint16_t)0x12)
#define PPSMC_MSG_ZeroLevelsDisabled ((uint16_t)0x13)
#define PPSMC_MSG_OneLevelsDisabled ((uint16_t)0x14)
#define PPSMC_MSG_TwoLevelsDisabled ((uint16_t)0x15)
#define PPSMC_MSG_EnableThermalInterrupt ((uint16_t)0x16)
#define PPSMC_MSG_RunningOnAC ((uint16_t)0x17)
#define PPSMC_MSG_LevelUp ((uint16_t)0x18)
#define PPSMC_MSG_LevelDown ((uint16_t)0x19)
#define PPSMC_MSG_ResetDPMCounters ((uint16_t)0x1a)
#define PPSMC_MSG_SwitchToSwState ((uint16_t)0x20)
#define PPSMC_MSG_SwitchToSwStateLast ((uint16_t)0x3f)
#define PPSMC_MSG_SwitchToInitialState ((uint16_t)0x40)
#define PPSMC_MSG_NoForcedLevel ((uint16_t)0x41)
#define PPSMC_MSG_ForceHigh ((uint16_t)0x42)
#define PPSMC_MSG_ForceMediumOrHigh ((uint16_t)0x43)
#define PPSMC_MSG_SwitchToMinimumPower ((uint16_t)0x51)
#define PPSMC_MSG_ResumeFromMinimumPower ((uint16_t)0x52)
#define PPSMC_MSG_EnableCac ((uint16_t)0x53)
#define PPSMC_MSG_DisableCac ((uint16_t)0x54)
#define PPSMC_DPMStateHistoryStart ((uint16_t)0x55)
#define PPSMC_DPMStateHistoryStop ((uint16_t)0x56)
#define PPSMC_CACHistoryStart ((uint16_t)0x57)
#define PPSMC_CACHistoryStop ((uint16_t)0x58)
#define PPSMC_TDPClampingActive ((uint16_t)0x59)
#define PPSMC_TDPClampingInactive ((uint16_t)0x5A)
#define PPSMC_StartFanControl ((uint16_t)0x5B)
#define PPSMC_StopFanControl ((uint16_t)0x5C)
#define PPSMC_NoDisplay ((uint16_t)0x5D)
#define PPSMC_HasDisplay ((uint16_t)0x5E)
#define PPSMC_MSG_UVDPowerOFF ((uint16_t)0x60)
#define PPSMC_MSG_UVDPowerON ((uint16_t)0x61)
#define PPSMC_MSG_EnableULV ((uint16_t)0x62)
#define PPSMC_MSG_DisableULV ((uint16_t)0x63)
#define PPSMC_MSG_EnterULV ((uint16_t)0x64)
#define PPSMC_MSG_ExitULV ((uint16_t)0x65)
#define PPSMC_PowerShiftActive ((uint16_t)0x6A)
#define PPSMC_PowerShiftInactive ((uint16_t)0x6B)
#define PPSMC_OCPActive ((uint16_t)0x6C)
#define PPSMC_OCPInactive ((uint16_t)0x6D)
#define PPSMC_CACLongTermAvgEnable ((uint16_t)0x6E)
#define PPSMC_CACLongTermAvgDisable ((uint16_t)0x6F)
#define PPSMC_MSG_InferredStateSweep_Start ((uint16_t)0x70)
#define PPSMC_MSG_InferredStateSweep_Stop ((uint16_t)0x71)
#define PPSMC_MSG_SwitchToLowestInfState ((uint16_t)0x72)
#define PPSMC_MSG_SwitchToNonInfState ((uint16_t)0x73)
#define PPSMC_MSG_AllStateSweep_Start ((uint16_t)0x74)
#define PPSMC_MSG_AllStateSweep_Stop ((uint16_t)0x75)
#define PPSMC_MSG_SwitchNextLowerInfState ((uint16_t)0x76)
#define PPSMC_MSG_SwitchNextHigherInfState ((uint16_t)0x77)
#define PPSMC_MSG_MclkRetrainingTest ((uint16_t)0x78)
#define PPSMC_MSG_ForceTDPClamping ((uint16_t)0x79)
#define PPSMC_MSG_CollectCAC_PowerCorreln ((uint16_t)0x7A)
#define PPSMC_MSG_CollectCAC_WeightCalib ((uint16_t)0x7B)
#define PPSMC_MSG_CollectCAC_SQonly ((uint16_t)0x7C)
#define PPSMC_MSG_CollectCAC_TemperaturePwr ((uint16_t)0x7D)
#define PPSMC_MSG_ExtremitiesTest_Start ((uint16_t)0x7E)
#define PPSMC_MSG_ExtremitiesTest_Stop ((uint16_t)0x7F)
#define PPSMC_FlushDataCache ((uint16_t)0x80)
#define PPSMC_FlushInstrCache ((uint16_t)0x81)
#define PPSMC_MSG_SetEnabledLevels ((uint16_t)0x82)
#define PPSMC_MSG_SetForcedLevels ((uint16_t)0x83)
#define PPSMC_MSG_ResetToDefaults ((uint16_t)0x84)
#define PPSMC_MSG_SetForcedLevelsAndJump ((uint16_t)0x85)
#define PPSMC_MSG_SetCACHistoryMode ((uint16_t)0x86)
#define PPSMC_MSG_EnableDTE ((uint16_t)0x87)
#define PPSMC_MSG_DisableDTE ((uint16_t)0x88)
#define PPSMC_MSG_SmcSpaceSetAddress ((uint16_t)0x89)
#define PPSMC_MSG_SmcSpaceWriteDWordInc ((uint16_t)0x8A)
#define PPSMC_MSG_SmcSpaceWriteWordInc ((uint16_t)0x8B)
#define PPSMC_MSG_SmcSpaceWriteByteInc ((uint16_t)0x8C)
#define PPSMC_MSG_ChangeNearTDPLimit ((uint16_t)0x90)
#define PPSMC_MSG_ChangeSafePowerLimit ((uint16_t)0x91)
#define PPSMC_MSG_DPMStateSweepStart ((uint16_t)0x92)
#define PPSMC_MSG_DPMStateSweepStop ((uint16_t)0x93)
#define PPSMC_MSG_OVRDDisableSCLKDS ((uint16_t)0x94)
#define PPSMC_MSG_CancelDisableOVRDSCLKDS ((uint16_t)0x95)
#define PPSMC_MSG_ThrottleOVRDSCLKDS ((uint16_t)0x96)
#define PPSMC_MSG_CancelThrottleOVRDSCLKDS ((uint16_t)0x97)
#define PPSMC_MSG_GPIO17 ((uint16_t)0x98)
#define PPSMC_MSG_API_SetSvi2Volt_Vddc ((uint16_t)0x99)
#define PPSMC_MSG_API_SetSvi2Volt_Vddci ((uint16_t)0x9A)
#define PPSMC_MSG_API_SetSvi2Volt_Mvdd ((uint16_t)0x9B)
#define PPSMC_MSG_API_GetSvi2Volt_Vddc ((uint16_t)0x9C)
#define PPSMC_MSG_API_GetSvi2Volt_Vddci ((uint16_t)0x9D)
#define PPSMC_MSG_API_GetSvi2Volt_Mvdd ((uint16_t)0x9E)
#define PPSMC_MSG_BREAK ((uint16_t)0xF8)
#define PPSMC_MSG_Test ((uint16_t)0x100)
#define PPSMC_MSG_DRV_DRAM_ADDR_HI ((uint16_t)0x250)
#define PPSMC_MSG_DRV_DRAM_ADDR_LO ((uint16_t)0x251)
#define PPSMC_MSG_SMU_DRAM_ADDR_HI ((uint16_t)0x252)
#define PPSMC_MSG_SMU_DRAM_ADDR_LO ((uint16_t)0x253)
#define PPSMC_MSG_LoadUcodes ((uint16_t)0x254)
typedef uint16_t PPSMC_Msg;
#define PPSMC_EVENT_STATUS_THERMAL 0x00000001
#define PPSMC_EVENT_STATUS_REGULATORHOT 0x00000002
#define PPSMC_EVENT_STATUS_DC 0x00000004
#define PPSMC_EVENT_STATUS_GPIO17 0x00000008
#pragma pack(pop)
#endif
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include "drmP.h"
#include "amdgpu.h"
#include "tonga_ppsmc.h"
#include "tonga_smumgr.h"
#include "smu_ucode_xfer_vi.h"
#include "amdgpu_ucode.h"
#include "smu/smu_7_1_2_d.h"
#include "smu/smu_7_1_2_sh_mask.h"
#define TONGA_SMC_SIZE 0x20000
static int tonga_set_smc_sram_address(struct amdgpu_device *adev, uint32_t smc_address, uint32_t limit)
{
uint32_t val;
if (smc_address & 3)
return -EINVAL;
if ((smc_address + 3) > limit)
return -EINVAL;
WREG32(mmSMC_IND_INDEX_0, smc_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
return 0;
}
static int tonga_copy_bytes_to_smc(struct amdgpu_device *adev, uint32_t smc_start_address, const uint8_t *src, uint32_t byte_count, uint32_t limit)
{
uint32_t addr;
uint32_t data, orig_data;
int result = 0;
uint32_t extra_shift;
unsigned long flags;
if (smc_start_address & 3)
return -EINVAL;
if ((smc_start_address + byte_count) > limit)
return -EINVAL;
addr = smc_start_address;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
while (byte_count >= 4) {
/* Bytes are written into the SMC addres space with the MSB first */
data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3];
result = tonga_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
src += 4;
byte_count -= 4;
addr += 4;
}
if (0 != byte_count) {
/* Now write odd bytes left, do a read modify write cycle */
data = 0;
result = tonga_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
orig_data = RREG32(mmSMC_IND_DATA_0);
extra_shift = 8 * (4 - byte_count);
while (byte_count > 0) {
data = (data << 8) + *src++;
byte_count--;
}
data <<= extra_shift;
data |= (orig_data & ~((~0UL) << extra_shift));
result = tonga_set_smc_sram_address(adev, addr, limit);
if (result)
goto out;
WREG32(mmSMC_IND_DATA_0, data);
}
out:
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int tonga_program_jump_on_start(struct amdgpu_device *adev)
{
static unsigned char data[] = {0xE0, 0x00, 0x80, 0x40};
tonga_copy_bytes_to_smc(adev, 0x0, data, 4, sizeof(data)+1);
return 0;
}
static bool tonga_is_smc_ram_running(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable);
return ((0 == val) && (0x20100 <= RREG32_SMC(ixSMC_PC_C)));
}
static int wait_smu_response(struct amdgpu_device *adev)
{
int i;
uint32_t val;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32(mmSMC_RESP_0);
if (REG_GET_FIELD(val, SMC_RESP_0, SMC_RESP))
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
static int tonga_send_msg_to_smc_offset(struct amdgpu_device *adev)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, 0x20000);
WREG32(mmSMC_MESSAGE_0, PPSMC_MSG_Test);
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send message\n");
return -EINVAL;
}
return 0;
}
static int tonga_send_msg_to_smc(struct amdgpu_device *adev, PPSMC_Msg msg)
{
if (!tonga_is_smc_ram_running(adev))
{
return -EINVAL;;
}
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send message\n");
return -EINVAL;
}
return 0;
}
static int tonga_send_msg_to_smc_without_waiting(struct amdgpu_device *adev,
PPSMC_Msg msg)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MESSAGE_0, msg);
return 0;
}
static int tonga_send_msg_to_smc_with_parameter(struct amdgpu_device *adev,
PPSMC_Msg msg,
uint32_t parameter)
{
if (!tonga_is_smc_ram_running(adev))
return -EINVAL;
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, parameter);
return tonga_send_msg_to_smc(adev, msg);
}
static int tonga_send_msg_to_smc_with_parameter_without_waiting(
struct amdgpu_device *adev,
PPSMC_Msg msg, uint32_t parameter)
{
if (wait_smu_response(adev)) {
DRM_ERROR("Failed to send previous message\n");
return -EINVAL;
}
WREG32(mmSMC_MSG_ARG_0, parameter);
return tonga_send_msg_to_smc_without_waiting(adev, msg);
}
#if 0 /* not used yet */
static int tonga_wait_for_smc_inactive(struct amdgpu_device *adev)
{
int i;
uint32_t val;
if (!tonga_is_smc_ram_running(adev))
return -EINVAL;
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
if (REG_GET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, cken) == 0)
break;
udelay(1);
}
if (i == adev->usec_timeout)
return -EINVAL;
return 0;
}
#endif
static int tonga_smu_upload_firmware_image(struct amdgpu_device *adev)
{
const struct smc_firmware_header_v1_0 *hdr;
uint32_t ucode_size;
uint32_t ucode_start_address;
const uint8_t *src;
uint32_t val;
uint32_t byte_count;
uint32_t *data;
unsigned long flags;
if (!adev->pm.fw)
return -EINVAL;
hdr = (const struct smc_firmware_header_v1_0 *)adev->pm.fw->data;
amdgpu_ucode_print_smc_hdr(&hdr->header);
adev->pm.fw_version = le32_to_cpu(hdr->header.ucode_version);
ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes);
ucode_start_address = le32_to_cpu(hdr->ucode_start_addr);
src = (const uint8_t *)
(adev->pm.fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes));
if (ucode_size & 3) {
DRM_ERROR("SMC ucode is not 4 bytes aligned\n");
return -EINVAL;
}
if (ucode_size > TONGA_SMC_SIZE) {
DRM_ERROR("SMC address is beyond the SMC RAM area\n");
return -EINVAL;
}
spin_lock_irqsave(&adev->smc_idx_lock, flags);
WREG32(mmSMC_IND_INDEX_0, ucode_start_address);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 1);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
byte_count = ucode_size;
data = (uint32_t *)src;
for (; byte_count >= 4; data++, byte_count -= 4)
WREG32(mmSMC_IND_DATA_0, data[0]);
val = RREG32(mmSMC_IND_ACCESS_CNTL);
val = REG_SET_FIELD(val, SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, 0);
WREG32(mmSMC_IND_ACCESS_CNTL, val);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return 0;
}
#if 0 /* not used yet */
static int tonga_read_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t *value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = tonga_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
*value = RREG32(mmSMC_IND_DATA_0);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int tonga_write_smc_sram_dword(struct amdgpu_device *adev,
uint32_t smc_address,
uint32_t value,
uint32_t limit)
{
int result;
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
result = tonga_set_smc_sram_address(adev, smc_address, limit);
if (result == 0)
WREG32(mmSMC_IND_DATA_0, value);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return result;
}
static int tonga_smu_stop_smc(struct amdgpu_device *adev)
{
uint32_t val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 1);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
return 0;
}
#endif
static enum AMDGPU_UCODE_ID tonga_convert_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case UCODE_ID_SDMA0:
return AMDGPU_UCODE_ID_SDMA0;
case UCODE_ID_SDMA1:
return AMDGPU_UCODE_ID_SDMA1;
case UCODE_ID_CP_CE:
return AMDGPU_UCODE_ID_CP_CE;
case UCODE_ID_CP_PFP:
return AMDGPU_UCODE_ID_CP_PFP;
case UCODE_ID_CP_ME:
return AMDGPU_UCODE_ID_CP_ME;
case UCODE_ID_CP_MEC:
case UCODE_ID_CP_MEC_JT1:
return AMDGPU_UCODE_ID_CP_MEC1;
case UCODE_ID_CP_MEC_JT2:
return AMDGPU_UCODE_ID_CP_MEC2;
case UCODE_ID_RLC_G:
return AMDGPU_UCODE_ID_RLC_G;
default:
DRM_ERROR("ucode type is out of range!\n");
return AMDGPU_UCODE_ID_MAXIMUM;
}
}
static int tonga_smu_populate_single_firmware_entry(struct amdgpu_device *adev,
uint32_t fw_type,
struct SMU_Entry *entry)
{
enum AMDGPU_UCODE_ID id = tonga_convert_fw_type(fw_type);
struct amdgpu_firmware_info *ucode = &adev->firmware.ucode[id];
const struct gfx_firmware_header_v1_0 *header = NULL;
uint64_t gpu_addr;
uint32_t data_size;
if (ucode->fw == NULL)
return -EINVAL;
gpu_addr = ucode->mc_addr;
header = (const struct gfx_firmware_header_v1_0 *)ucode->fw->data;
data_size = le32_to_cpu(header->header.ucode_size_bytes);
if ((fw_type == UCODE_ID_CP_MEC_JT1) ||
(fw_type == UCODE_ID_CP_MEC_JT2)) {
gpu_addr += le32_to_cpu(header->jt_offset) << 2;
data_size = le32_to_cpu(header->jt_size) << 2;
}
entry->version = (uint16_t)le32_to_cpu(header->header.ucode_version);
entry->id = (uint16_t)fw_type;
entry->image_addr_high = upper_32_bits(gpu_addr);
entry->image_addr_low = lower_32_bits(gpu_addr);
entry->meta_data_addr_high = 0;
entry->meta_data_addr_low = 0;
entry->data_size_byte = data_size;
entry->num_register_entries = 0;
if (fw_type == UCODE_ID_RLC_G)
entry->flags = 1;
else
entry->flags = 0;
return 0;
}
static int tonga_smu_request_load_fw(struct amdgpu_device *adev)
{
struct tonga_smu_private_data *private = (struct tonga_smu_private_data *)adev->smu.priv;
struct SMU_DRAMData_TOC *toc;
uint32_t fw_to_load;
WREG32_SMC(ixSOFT_REGISTERS_TABLE_28, 0);
tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_HI, private->smu_buffer_addr_high);
tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_SMU_DRAM_ADDR_LO, private->smu_buffer_addr_low);
toc = (struct SMU_DRAMData_TOC *)private->header;
toc->num_entries = 0;
toc->structure_version = 1;
if (!adev->firmware.smu_load)
return 0;
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_RLC_G,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for RLC\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_CE,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for CE\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_PFP,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for PFP\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_ME,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for ME\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT1\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_CP_MEC_JT2,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for MEC_JT2\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA0,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA0\n");
return -EINVAL;
}
if (tonga_smu_populate_single_firmware_entry(adev, UCODE_ID_SDMA1,
&toc->entry[toc->num_entries++])) {
DRM_ERROR("Failed to get firmware entry for SDMA1\n");
return -EINVAL;
}
tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_HI, private->header_addr_high);
tonga_send_msg_to_smc_with_parameter(adev, PPSMC_MSG_DRV_DRAM_ADDR_LO, private->header_addr_low);
fw_to_load = UCODE_ID_RLC_G_MASK |
UCODE_ID_SDMA0_MASK |
UCODE_ID_SDMA1_MASK |
UCODE_ID_CP_CE_MASK |
UCODE_ID_CP_ME_MASK |
UCODE_ID_CP_PFP_MASK |
UCODE_ID_CP_MEC_MASK;
if (tonga_send_msg_to_smc_with_parameter_without_waiting(adev, PPSMC_MSG_LoadUcodes, fw_to_load)) {
DRM_ERROR("Fail to request SMU load ucode\n");
return -EINVAL;
}
return 0;
}
static uint32_t tonga_smu_get_mask_for_fw_type(uint32_t fw_type)
{
switch (fw_type) {
case AMDGPU_UCODE_ID_SDMA0:
return UCODE_ID_SDMA0_MASK;
case AMDGPU_UCODE_ID_SDMA1:
return UCODE_ID_SDMA1_MASK;
case AMDGPU_UCODE_ID_CP_CE:
return UCODE_ID_CP_CE_MASK;
case AMDGPU_UCODE_ID_CP_PFP:
return UCODE_ID_CP_PFP_MASK;
case AMDGPU_UCODE_ID_CP_ME:
return UCODE_ID_CP_ME_MASK;
case AMDGPU_UCODE_ID_CP_MEC1:
return UCODE_ID_CP_MEC_MASK;
case AMDGPU_UCODE_ID_CP_MEC2:
return UCODE_ID_CP_MEC_MASK;
case AMDGPU_UCODE_ID_RLC_G:
return UCODE_ID_RLC_G_MASK;
default:
DRM_ERROR("ucode type is out of range!\n");
return 0;
}
}
static int tonga_smu_check_fw_load_finish(struct amdgpu_device *adev,
uint32_t fw_type)
{
uint32_t fw_mask = tonga_smu_get_mask_for_fw_type(fw_type);
int i;
for (i = 0; i < adev->usec_timeout; i++) {
if (fw_mask == (RREG32_SMC(ixSOFT_REGISTERS_TABLE_28) & fw_mask))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("check firmware loading failed\n");
return -EINVAL;
}
return 0;
}
static int tonga_smu_start_in_protection_mode(struct amdgpu_device *adev)
{
int result;
uint32_t val;
int i;
/* Assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
result = tonga_smu_upload_firmware_image(adev);
if (result)
return result;
/* Clear status */
WREG32_SMC(ixSMU_STATUS, 0);
/* Enable clock */
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
/* De-assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
/* Set SMU Auto Start */
val = RREG32_SMC(ixSMU_INPUT_DATA);
val = REG_SET_FIELD(val, SMU_INPUT_DATA, AUTO_START, 1);
WREG32_SMC(ixSMU_INPUT_DATA, val);
/* Clear firmware interrupt enable flag */
WREG32_SMC(ixFIRMWARE_FLAGS, 0);
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixRCU_UC_EVENTS);
if (REG_GET_FIELD(val, RCU_UC_EVENTS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Interrupt is not enabled by firmware\n");
return -EINVAL;
}
/* Call Test SMU message with 0x20000 offset
* to trigger SMU start
*/
tonga_send_msg_to_smc_offset(adev);
/* Wait for done bit to be set */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixSMU_STATUS);
if (REG_GET_FIELD(val, SMU_STATUS, SMU_DONE))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Timeout for SMU start\n");
return -EINVAL;
}
/* Check pass/failed indicator */
val = RREG32_SMC(ixSMU_STATUS);
if (!REG_GET_FIELD(val, SMU_STATUS, SMU_PASS)) {
DRM_ERROR("SMU Firmware start failed\n");
return -EINVAL;
}
/* Wait for firmware to initialize */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixFIRMWARE_FLAGS);
if(REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("SMU firmware initialization failed\n");
return -EINVAL;
}
return 0;
}
static int tonga_smu_start_in_non_protection_mode(struct amdgpu_device *adev)
{
int i, result;
uint32_t val;
/* wait for smc boot up */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixRCU_UC_EVENTS);
val = REG_GET_FIELD(val, RCU_UC_EVENTS, boot_seq_done);
if (val)
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("SMC boot sequence is not completed\n");
return -EINVAL;
}
/* Clear firmware interrupt enable flag */
WREG32_SMC(ixFIRMWARE_FLAGS, 0);
/* Assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 1);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
result = tonga_smu_upload_firmware_image(adev);
if (result)
return result;
/* Set smc instruct start point at 0x0 */
tonga_program_jump_on_start(adev);
/* Enable clock */
val = RREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0);
val = REG_SET_FIELD(val, SMC_SYSCON_CLOCK_CNTL_0, ck_disable, 0);
WREG32_SMC(ixSMC_SYSCON_CLOCK_CNTL_0, val);
/* De-assert reset */
val = RREG32_SMC(ixSMC_SYSCON_RESET_CNTL);
val = REG_SET_FIELD(val, SMC_SYSCON_RESET_CNTL, rst_reg, 0);
WREG32_SMC(ixSMC_SYSCON_RESET_CNTL, val);
/* Wait for firmware to initialize */
for (i = 0; i < adev->usec_timeout; i++) {
val = RREG32_SMC(ixFIRMWARE_FLAGS);
if (REG_GET_FIELD(val, FIRMWARE_FLAGS, INTERRUPTS_ENABLED))
break;
udelay(1);
}
if (i == adev->usec_timeout) {
DRM_ERROR("Timeout for SMC firmware initialization\n");
return -EINVAL;
}
return 0;
}
int tonga_smu_start(struct amdgpu_device *adev)
{
int result;
uint32_t val;
if (!tonga_is_smc_ram_running(adev)) {
val = RREG32_SMC(ixSMU_FIRMWARE);
if (!REG_GET_FIELD(val, SMU_FIRMWARE, SMU_MODE)) {
result = tonga_smu_start_in_non_protection_mode(adev);
if (result)
return result;
} else {
result = tonga_smu_start_in_protection_mode(adev);
if (result)
return result;
}
}
return tonga_smu_request_load_fw(adev);
}
static const struct amdgpu_smumgr_funcs tonga_smumgr_funcs = {
.check_fw_load_finish = tonga_smu_check_fw_load_finish,
.request_smu_load_fw = NULL,
.request_smu_specific_fw = NULL,
};
int tonga_smu_init(struct amdgpu_device *adev)
{
struct tonga_smu_private_data *private;
uint32_t image_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096;
uint32_t smu_internal_buffer_size = 200*4096;
struct amdgpu_bo **toc_buf = &adev->smu.toc_buf;
struct amdgpu_bo **smu_buf = &adev->smu.smu_buf;
uint64_t mc_addr;
void *toc_buf_ptr;
void *smu_buf_ptr;
int ret;
private = kzalloc(sizeof(struct tonga_smu_private_data), GFP_KERNEL);
if (NULL == private)
return -ENOMEM;
/* allocate firmware buffers */
if (adev->firmware.smu_load)
amdgpu_ucode_init_bo(adev);
adev->smu.priv = private;
adev->smu.fw_flags = 0;
/* Allocate FW image data structure and header buffer */
ret = amdgpu_bo_create(adev, image_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, toc_buf);
if (ret) {
DRM_ERROR("Failed to allocate memory for TOC buffer\n");
return -ENOMEM;
}
/* Allocate buffer for SMU internal buffer */
ret = amdgpu_bo_create(adev, smu_internal_buffer_size, PAGE_SIZE,
true, AMDGPU_GEM_DOMAIN_VRAM, 0, NULL, smu_buf);
if (ret) {
DRM_ERROR("Failed to allocate memory for SMU internal buffer\n");
return -ENOMEM;
}
/* Retrieve GPU address for header buffer and internal buffer */
ret = amdgpu_bo_reserve(adev->smu.toc_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to reserve the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_pin(adev->smu.toc_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to pin the TOC buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_kmap(*toc_buf, &toc_buf_ptr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to map the TOC buffer\n");
return -EINVAL;
}
amdgpu_bo_unreserve(adev->smu.toc_buf);
private->header_addr_low = lower_32_bits(mc_addr);
private->header_addr_high = upper_32_bits(mc_addr);
private->header = toc_buf_ptr;
ret = amdgpu_bo_reserve(adev->smu.smu_buf, false);
if (ret) {
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to reserve the SMU internal buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_pin(adev->smu.smu_buf, AMDGPU_GEM_DOMAIN_VRAM, &mc_addr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to pin the SMU internal buffer\n");
return -EINVAL;
}
ret = amdgpu_bo_kmap(*smu_buf, &smu_buf_ptr);
if (ret) {
amdgpu_bo_unreserve(adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
amdgpu_bo_unref(&adev->smu.toc_buf);
DRM_ERROR("Failed to map the SMU internal buffer\n");
return -EINVAL;
}
amdgpu_bo_unreserve(adev->smu.smu_buf);
private->smu_buffer_addr_low = lower_32_bits(mc_addr);
private->smu_buffer_addr_high = upper_32_bits(mc_addr);
adev->smu.smumgr_funcs = &tonga_smumgr_funcs;
return 0;
}
int tonga_smu_fini(struct amdgpu_device *adev)
{
amdgpu_bo_unref(&adev->smu.toc_buf);
amdgpu_bo_unref(&adev->smu.smu_buf);
kfree(adev->smu.priv);
adev->smu.priv = NULL;
if (adev->firmware.fw_buf)
amdgpu_ucode_fini_bo(adev);
return 0;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef TONGA_SMUMGR_H
#define TONGA_SMUMGR_H
#include "tonga_ppsmc.h"
int tonga_smu_init(struct amdgpu_device *adev);
int tonga_smu_fini(struct amdgpu_device *adev);
int tonga_smu_start(struct amdgpu_device *adev);
struct tonga_smu_private_data
{
uint8_t *header;
uint32_t smu_buffer_addr_high;
uint32_t smu_buffer_addr_low;
uint32_t header_addr_high;
uint32_t header_addr_low;
};
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
* Authors: Christian König <christian.koenig@amd.com>
*/
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "amdgpu.h"
#include "amdgpu_uvd.h"
#include "vid.h"
#include "uvd/uvd_5_0_d.h"
#include "uvd/uvd_5_0_sh_mask.h"
#include "oss/oss_2_0_d.h"
#include "oss/oss_2_0_sh_mask.h"
static void uvd_v5_0_set_ring_funcs(struct amdgpu_device *adev);
static void uvd_v5_0_set_irq_funcs(struct amdgpu_device *adev);
static int uvd_v5_0_start(struct amdgpu_device *adev);
static void uvd_v5_0_stop(struct amdgpu_device *adev);
/**
* uvd_v5_0_ring_get_rptr - get read pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware read pointer
*/
static uint32_t uvd_v5_0_ring_get_rptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
return RREG32(mmUVD_RBC_RB_RPTR);
}
/**
* uvd_v5_0_ring_get_wptr - get write pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware write pointer
*/
static uint32_t uvd_v5_0_ring_get_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
return RREG32(mmUVD_RBC_RB_WPTR);
}
/**
* uvd_v5_0_ring_set_wptr - set write pointer
*
* @ring: amdgpu_ring pointer
*
* Commits the write pointer to the hardware
*/
static void uvd_v5_0_ring_set_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
WREG32(mmUVD_RBC_RB_WPTR, ring->wptr);
}
static int uvd_v5_0_early_init(struct amdgpu_device *adev)
{
uvd_v5_0_set_ring_funcs(adev);
uvd_v5_0_set_irq_funcs(adev);
return 0;
}
static int uvd_v5_0_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
/* UVD TRAP */
r = amdgpu_irq_add_id(adev, 124, &adev->uvd.irq);
if (r)
return r;
r = amdgpu_uvd_sw_init(adev);
if (r)
return r;
r = amdgpu_uvd_resume(adev);
if (r)
return r;
ring = &adev->uvd.ring;
sprintf(ring->name, "uvd");
r = amdgpu_ring_init(adev, ring, 4096, CP_PACKET2, 0xf,
&adev->uvd.irq, 0, AMDGPU_RING_TYPE_UVD);
return r;
}
static int uvd_v5_0_sw_fini(struct amdgpu_device *adev)
{
int r;
r = amdgpu_uvd_suspend(adev);
if (r)
return r;
r = amdgpu_uvd_sw_fini(adev);
if (r)
return r;
return r;
}
/**
* uvd_v5_0_hw_init - start and test UVD block
*
* @adev: amdgpu_device pointer
*
* Initialize the hardware, boot up the VCPU and do some testing
*/
static int uvd_v5_0_hw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uint32_t tmp;
int r;
/* raise clocks while booting up the VCPU */
amdgpu_asic_set_uvd_clocks(adev, 53300, 40000);
r = uvd_v5_0_start(adev);
if (r)
goto done;
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
goto done;
}
r = amdgpu_ring_lock(ring, 10);
if (r) {
DRM_ERROR("amdgpu: ring failed to lock UVD ring (%d).\n", r);
goto done;
}
tmp = PACKET0(mmUVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
tmp = PACKET0(mmUVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
tmp = PACKET0(mmUVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
/* Clear timeout status bits */
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_TIMEOUT_STATUS, 0));
amdgpu_ring_write(ring, 0x8);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_CNTL, 0));
amdgpu_ring_write(ring, 3);
amdgpu_ring_unlock_commit(ring);
done:
/* lower clocks again */
amdgpu_asic_set_uvd_clocks(adev, 0, 0);
if (!r)
DRM_INFO("UVD initialized successfully.\n");
return r;
}
/**
* uvd_v5_0_hw_fini - stop the hardware block
*
* @adev: amdgpu_device pointer
*
* Stop the UVD block, mark ring as not ready any more
*/
static int uvd_v5_0_hw_fini(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uvd_v5_0_stop(adev);
ring->ready = false;
return 0;
}
static int uvd_v5_0_suspend(struct amdgpu_device *adev)
{
int r;
r = uvd_v5_0_hw_fini(adev);
if (r)
return r;
r = amdgpu_uvd_suspend(adev);
if (r)
return r;
return r;
}
static int uvd_v5_0_resume(struct amdgpu_device *adev)
{
int r;
r = amdgpu_uvd_resume(adev);
if (r)
return r;
r = uvd_v5_0_hw_init(adev);
if (r)
return r;
return r;
}
/**
* uvd_v5_0_mc_resume - memory controller programming
*
* @adev: amdgpu_device pointer
*
* Let the UVD memory controller know it's offsets
*/
static void uvd_v5_0_mc_resume(struct amdgpu_device *adev)
{
uint64_t offset;
uint32_t size;
/* programm memory controller bits 0-27 */
WREG32(mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
lower_32_bits(adev->uvd.gpu_addr));
WREG32(mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
upper_32_bits(adev->uvd.gpu_addr));
offset = AMDGPU_UVD_FIRMWARE_OFFSET;
size = AMDGPU_GPU_PAGE_ALIGN(adev->uvd.fw->size + 4);
WREG32(mmUVD_VCPU_CACHE_OFFSET0, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE0, size);
offset += size;
size = AMDGPU_UVD_STACK_SIZE;
WREG32(mmUVD_VCPU_CACHE_OFFSET1, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE1, size);
offset += size;
size = AMDGPU_UVD_HEAP_SIZE;
WREG32(mmUVD_VCPU_CACHE_OFFSET2, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE2, size);
}
/**
* uvd_v5_0_start - start UVD block
*
* @adev: amdgpu_device pointer
*
* Setup and start the UVD block
*/
static int uvd_v5_0_start(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uint32_t rb_bufsz, tmp;
uint32_t lmi_swap_cntl;
uint32_t mp_swap_cntl;
int i, j, r;
/*disable DPG */
WREG32_P(mmUVD_POWER_STATUS, 0, ~(1 << 2));
/* disable byte swapping */
lmi_swap_cntl = 0;
mp_swap_cntl = 0;
uvd_v5_0_mc_resume(adev);
/* disable clock gating */
WREG32(mmUVD_CGC_GATE, 0);
/* disable interupt */
WREG32_P(mmUVD_MASTINT_EN, 0, ~(1 << 1));
/* stall UMC and register bus before resetting VCPU */
WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8));
mdelay(1);
/* put LMI, VCPU, RBC etc... into reset */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__LMI_SOFT_RESET_MASK |
UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK | UVD_SOFT_RESET__LBSI_SOFT_RESET_MASK |
UVD_SOFT_RESET__RBC_SOFT_RESET_MASK | UVD_SOFT_RESET__CSM_SOFT_RESET_MASK |
UVD_SOFT_RESET__CXW_SOFT_RESET_MASK | UVD_SOFT_RESET__TAP_SOFT_RESET_MASK |
UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK);
mdelay(5);
/* take UVD block out of reset */
WREG32_P(mmSRBM_SOFT_RESET, 0, ~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
mdelay(5);
/* initialize UVD memory controller */
WREG32(mmUVD_LMI_CTRL, 0x40 | (1 << 8) | (1 << 13) |
(1 << 21) | (1 << 9) | (1 << 20));
#ifdef __BIG_ENDIAN
/* swap (8 in 32) RB and IB */
lmi_swap_cntl = 0xa;
mp_swap_cntl = 0;
#endif
WREG32(mmUVD_LMI_SWAP_CNTL, lmi_swap_cntl);
WREG32(mmUVD_MP_SWAP_CNTL, mp_swap_cntl);
WREG32(mmUVD_MPC_SET_MUXA0, 0x40c2040);
WREG32(mmUVD_MPC_SET_MUXA1, 0x0);
WREG32(mmUVD_MPC_SET_MUXB0, 0x40c2040);
WREG32(mmUVD_MPC_SET_MUXB1, 0x0);
WREG32(mmUVD_MPC_SET_ALU, 0);
WREG32(mmUVD_MPC_SET_MUX, 0x88);
/* take all subblocks out of reset, except VCPU */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(5);
/* enable VCPU clock */
WREG32(mmUVD_VCPU_CNTL, 1 << 9);
/* enable UMC */
WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8));
/* boot up the VCPU */
WREG32(mmUVD_SOFT_RESET, 0);
mdelay(10);
for (i = 0; i < 10; ++i) {
uint32_t status;
for (j = 0; j < 100; ++j) {
status = RREG32(mmUVD_STATUS);
if (status & 2)
break;
mdelay(10);
}
r = 0;
if (status & 2)
break;
DRM_ERROR("UVD not responding, trying to reset the VCPU!!!\n");
WREG32_P(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK,
~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(10);
WREG32_P(mmUVD_SOFT_RESET, 0, ~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(10);
r = -1;
}
if (r) {
DRM_ERROR("UVD not responding, giving up!!!\n");
return r;
}
/* enable master interrupt */
WREG32_P(mmUVD_MASTINT_EN, 3 << 1, ~(3 << 1));
/* clear the bit 4 of UVD_STATUS */
WREG32_P(mmUVD_STATUS, 0, ~(2 << 1));
rb_bufsz = order_base_2(ring->ring_size);
tmp = 0;
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BLKSZ, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_FETCH, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_WPTR_POLL_EN, 0);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_UPDATE, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
/* force RBC into idle state */
WREG32(mmUVD_RBC_RB_CNTL, tmp);
/* set the write pointer delay */
WREG32(mmUVD_RBC_RB_WPTR_CNTL, 0);
/* set the wb address */
WREG32(mmUVD_RBC_RB_RPTR_ADDR, (upper_32_bits(ring->gpu_addr) >> 2));
/* programm the RB_BASE for ring buffer */
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_LOW,
lower_32_bits(ring->gpu_addr));
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH,
upper_32_bits(ring->gpu_addr));
/* Initialize the ring buffer's read and write pointers */
WREG32(mmUVD_RBC_RB_RPTR, 0);
ring->wptr = RREG32(mmUVD_RBC_RB_RPTR);
WREG32(mmUVD_RBC_RB_WPTR, ring->wptr);
WREG32_P(mmUVD_RBC_RB_CNTL, 0, ~UVD_RBC_RB_CNTL__RB_NO_FETCH_MASK);
return 0;
}
/**
* uvd_v5_0_stop - stop UVD block
*
* @adev: amdgpu_device pointer
*
* stop the UVD block
*/
static void uvd_v5_0_stop(struct amdgpu_device *adev)
{
/* force RBC into idle state */
WREG32(mmUVD_RBC_RB_CNTL, 0x11010101);
/* Stall UMC and register bus before resetting VCPU */
WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8));
mdelay(1);
/* put VCPU into reset */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(5);
/* disable VCPU clock */
WREG32(mmUVD_VCPU_CNTL, 0x0);
/* Unstall UMC and register bus */
WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8));
}
/**
* uvd_v5_0_ring_emit_fence - emit an fence & trap command
*
* @ring: amdgpu_ring pointer
* @fence: fence to emit
*
* Write a fence and a trap command to the ring.
*/
static void uvd_v5_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
bool write64bit)
{
WARN_ON(write64bit);
amdgpu_ring_write(ring, PACKET0(mmUVD_CONTEXT_ID, 0));
amdgpu_ring_write(ring, seq);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0));
amdgpu_ring_write(ring, addr & 0xffffffff);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0));
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xff);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD, 0));
amdgpu_ring_write(ring, 2);
}
/**
* uvd_v5_0_ring_emit_semaphore - emit semaphore command
*
* @ring: amdgpu_ring pointer
* @semaphore: semaphore to emit commands for
* @emit_wait: true if we should emit a wait command
*
* Emit a semaphore command (either wait or signal) to the UVD ring.
*/
static bool uvd_v5_0_ring_emit_semaphore(struct amdgpu_ring *ring,
struct amdgpu_semaphore *semaphore,
bool emit_wait)
{
uint64_t addr = semaphore->gpu_addr;
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_ADDR_LOW, 0));
amdgpu_ring_write(ring, (addr >> 3) & 0x000FFFFF);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_ADDR_HIGH, 0));
amdgpu_ring_write(ring, (addr >> 23) & 0x000FFFFF);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_CMD, 0));
amdgpu_ring_write(ring, 0x80 | (emit_wait ? 1 : 0));
return true;
}
/**
* uvd_v5_0_ring_test_ring - register write test
*
* @ring: amdgpu_ring pointer
*
* Test if we can successfully write to the context register
*/
static int uvd_v5_0_ring_test_ring(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
uint32_t tmp = 0;
unsigned i;
int r;
WREG32(mmUVD_CONTEXT_ID, 0xCAFEDEAD);
r = amdgpu_ring_lock(ring, 3);
if (r) {
DRM_ERROR("amdgpu: cp failed to lock ring %d (%d).\n",
ring->idx, r);
return r;
}
amdgpu_ring_write(ring, PACKET0(mmUVD_CONTEXT_ID, 0));
amdgpu_ring_write(ring, 0xDEADBEEF);
amdgpu_ring_unlock_commit(ring);
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(mmUVD_CONTEXT_ID);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ring test on %d succeeded in %d usecs\n",
ring->idx, i);
} else {
DRM_ERROR("amdgpu: ring %d test failed (0x%08X)\n",
ring->idx, tmp);
r = -EINVAL;
}
return r;
}
/**
* uvd_v5_0_ring_emit_ib - execute indirect buffer
*
* @ring: amdgpu_ring pointer
* @ib: indirect buffer to execute
*
* Write ring commands to execute the indirect buffer
*/
static void uvd_v5_0_ring_emit_ib(struct amdgpu_ring *ring,
struct amdgpu_ib *ib)
{
amdgpu_ring_write(ring, PACKET0(mmUVD_LMI_RBC_IB_64BIT_BAR_LOW, 0));
amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, PACKET0(mmUVD_LMI_RBC_IB_64BIT_BAR_HIGH, 0));
amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, PACKET0(mmUVD_RBC_IB_SIZE, 0));
amdgpu_ring_write(ring, ib->length_dw);
}
/**
* uvd_v5_0_ring_test_ib - test ib execution
*
* @ring: amdgpu_ring pointer
*
* Test if we can successfully execute an IB
*/
static int uvd_v5_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
struct amdgpu_fence *fence = NULL;
int r;
r = amdgpu_asic_set_uvd_clocks(adev, 53300, 40000);
if (r) {
DRM_ERROR("amdgpu: failed to raise UVD clocks (%d).\n", r);
return r;
}
r = amdgpu_uvd_get_create_msg(ring, 1, NULL);
if (r) {
DRM_ERROR("amdgpu: failed to get create msg (%d).\n", r);
goto error;
}
r = amdgpu_uvd_get_destroy_msg(ring, 1, &fence);
if (r) {
DRM_ERROR("amdgpu: failed to get destroy ib (%d).\n", r);
goto error;
}
r = amdgpu_fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
goto error;
}
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
error:
amdgpu_fence_unref(&fence);
amdgpu_asic_set_uvd_clocks(adev, 0, 0);
return r;
}
static bool uvd_v5_0_is_idle(struct amdgpu_device *adev)
{
return !(RREG32(mmSRBM_STATUS) & SRBM_STATUS__UVD_BUSY_MASK);
}
static int uvd_v5_0_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
for (i = 0; i < adev->usec_timeout; i++) {
if (!(RREG32(mmSRBM_STATUS) & SRBM_STATUS__UVD_BUSY_MASK))
return 0;
}
return -ETIMEDOUT;
}
static int uvd_v5_0_soft_reset(struct amdgpu_device *adev)
{
uvd_v5_0_stop(adev);
WREG32_P(mmSRBM_SOFT_RESET, SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK,
~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
mdelay(5);
return uvd_v5_0_start(adev);
}
static void uvd_v5_0_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "UVD 5.0 registers\n");
dev_info(adev->dev, " UVD_SEMA_ADDR_LOW=0x%08X\n",
RREG32(mmUVD_SEMA_ADDR_LOW));
dev_info(adev->dev, " UVD_SEMA_ADDR_HIGH=0x%08X\n",
RREG32(mmUVD_SEMA_ADDR_HIGH));
dev_info(adev->dev, " UVD_SEMA_CMD=0x%08X\n",
RREG32(mmUVD_SEMA_CMD));
dev_info(adev->dev, " UVD_GPCOM_VCPU_CMD=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_CMD));
dev_info(adev->dev, " UVD_GPCOM_VCPU_DATA0=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_DATA0));
dev_info(adev->dev, " UVD_GPCOM_VCPU_DATA1=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_DATA1));
dev_info(adev->dev, " UVD_ENGINE_CNTL=0x%08X\n",
RREG32(mmUVD_ENGINE_CNTL));
dev_info(adev->dev, " UVD_UDEC_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_ADDR_CONFIG));
dev_info(adev->dev, " UVD_UDEC_DB_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_DB_ADDR_CONFIG));
dev_info(adev->dev, " UVD_UDEC_DBW_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_DBW_ADDR_CONFIG));
dev_info(adev->dev, " UVD_SEMA_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_CNTL));
dev_info(adev->dev, " UVD_LMI_EXT40_ADDR=0x%08X\n",
RREG32(mmUVD_LMI_EXT40_ADDR));
dev_info(adev->dev, " UVD_CTX_INDEX=0x%08X\n",
RREG32(mmUVD_CTX_INDEX));
dev_info(adev->dev, " UVD_CTX_DATA=0x%08X\n",
RREG32(mmUVD_CTX_DATA));
dev_info(adev->dev, " UVD_CGC_GATE=0x%08X\n",
RREG32(mmUVD_CGC_GATE));
dev_info(adev->dev, " UVD_CGC_CTRL=0x%08X\n",
RREG32(mmUVD_CGC_CTRL));
dev_info(adev->dev, " UVD_LMI_CTRL2=0x%08X\n",
RREG32(mmUVD_LMI_CTRL2));
dev_info(adev->dev, " UVD_MASTINT_EN=0x%08X\n",
RREG32(mmUVD_MASTINT_EN));
dev_info(adev->dev, " UVD_LMI_ADDR_EXT=0x%08X\n",
RREG32(mmUVD_LMI_ADDR_EXT));
dev_info(adev->dev, " UVD_LMI_CTRL=0x%08X\n",
RREG32(mmUVD_LMI_CTRL));
dev_info(adev->dev, " UVD_LMI_SWAP_CNTL=0x%08X\n",
RREG32(mmUVD_LMI_SWAP_CNTL));
dev_info(adev->dev, " UVD_MP_SWAP_CNTL=0x%08X\n",
RREG32(mmUVD_MP_SWAP_CNTL));
dev_info(adev->dev, " UVD_MPC_SET_MUXA0=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXA0));
dev_info(adev->dev, " UVD_MPC_SET_MUXA1=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXA1));
dev_info(adev->dev, " UVD_MPC_SET_MUXB0=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXB0));
dev_info(adev->dev, " UVD_MPC_SET_MUXB1=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXB1));
dev_info(adev->dev, " UVD_MPC_SET_MUX=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUX));
dev_info(adev->dev, " UVD_MPC_SET_ALU=0x%08X\n",
RREG32(mmUVD_MPC_SET_ALU));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET0=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET0));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE0=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE0));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET1=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET1));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE1=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE1));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET2=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET2));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE2=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE2));
dev_info(adev->dev, " UVD_VCPU_CNTL=0x%08X\n",
RREG32(mmUVD_VCPU_CNTL));
dev_info(adev->dev, " UVD_SOFT_RESET=0x%08X\n",
RREG32(mmUVD_SOFT_RESET));
dev_info(adev->dev, " UVD_LMI_RBC_IB_64BIT_BAR_LOW=0x%08X\n",
RREG32(mmUVD_LMI_RBC_IB_64BIT_BAR_LOW));
dev_info(adev->dev, " UVD_LMI_RBC_IB_64BIT_BAR_HIGH=0x%08X\n",
RREG32(mmUVD_LMI_RBC_IB_64BIT_BAR_HIGH));
dev_info(adev->dev, " UVD_RBC_IB_SIZE=0x%08X\n",
RREG32(mmUVD_RBC_IB_SIZE));
dev_info(adev->dev, " UVD_LMI_RBC_RB_64BIT_BAR_LOW=0x%08X\n",
RREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_LOW));
dev_info(adev->dev, " UVD_LMI_RBC_RB_64BIT_BAR_HIGH=0x%08X\n",
RREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH));
dev_info(adev->dev, " UVD_RBC_RB_RPTR=0x%08X\n",
RREG32(mmUVD_RBC_RB_RPTR));
dev_info(adev->dev, " UVD_RBC_RB_WPTR=0x%08X\n",
RREG32(mmUVD_RBC_RB_WPTR));
dev_info(adev->dev, " UVD_RBC_RB_WPTR_CNTL=0x%08X\n",
RREG32(mmUVD_RBC_RB_WPTR_CNTL));
dev_info(adev->dev, " UVD_RBC_RB_CNTL=0x%08X\n",
RREG32(mmUVD_RBC_RB_CNTL));
dev_info(adev->dev, " UVD_STATUS=0x%08X\n",
RREG32(mmUVD_STATUS));
dev_info(adev->dev, " UVD_SEMA_TIMEOUT_STATUS=0x%08X\n",
RREG32(mmUVD_SEMA_TIMEOUT_STATUS));
dev_info(adev->dev, " UVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_CONTEXT_ID=0x%08X\n",
RREG32(mmUVD_CONTEXT_ID));
}
static int uvd_v5_0_set_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
enum amdgpu_interrupt_state state)
{
// TODO
return 0;
}
static int uvd_v5_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
DRM_DEBUG("IH: UVD TRAP\n");
amdgpu_fence_process(&adev->uvd.ring);
return 0;
}
static int uvd_v5_0_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
//TODO
return 0;
}
static int uvd_v5_0_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
/* This doesn't actually powergate the UVD block.
* That's done in the dpm code via the SMC. This
* just re-inits the block as necessary. The actual
* gating still happens in the dpm code. We should
* revisit this when there is a cleaner line between
* the smc and the hw blocks
*/
if (state == AMDGPU_PG_STATE_GATE) {
uvd_v5_0_stop(adev);
return 0;
} else {
return uvd_v5_0_start(adev);
}
}
const struct amdgpu_ip_funcs uvd_v5_0_ip_funcs = {
.early_init = uvd_v5_0_early_init,
.late_init = NULL,
.sw_init = uvd_v5_0_sw_init,
.sw_fini = uvd_v5_0_sw_fini,
.hw_init = uvd_v5_0_hw_init,
.hw_fini = uvd_v5_0_hw_fini,
.suspend = uvd_v5_0_suspend,
.resume = uvd_v5_0_resume,
.is_idle = uvd_v5_0_is_idle,
.wait_for_idle = uvd_v5_0_wait_for_idle,
.soft_reset = uvd_v5_0_soft_reset,
.print_status = uvd_v5_0_print_status,
.set_clockgating_state = uvd_v5_0_set_clockgating_state,
.set_powergating_state = uvd_v5_0_set_powergating_state,
};
static const struct amdgpu_ring_funcs uvd_v5_0_ring_funcs = {
.get_rptr = uvd_v5_0_ring_get_rptr,
.get_wptr = uvd_v5_0_ring_get_wptr,
.set_wptr = uvd_v5_0_ring_set_wptr,
.parse_cs = amdgpu_uvd_ring_parse_cs,
.emit_ib = uvd_v5_0_ring_emit_ib,
.emit_fence = uvd_v5_0_ring_emit_fence,
.emit_semaphore = uvd_v5_0_ring_emit_semaphore,
.test_ring = uvd_v5_0_ring_test_ring,
.test_ib = uvd_v5_0_ring_test_ib,
.is_lockup = amdgpu_ring_test_lockup,
};
static void uvd_v5_0_set_ring_funcs(struct amdgpu_device *adev)
{
adev->uvd.ring.funcs = &uvd_v5_0_ring_funcs;
}
static const struct amdgpu_irq_src_funcs uvd_v5_0_irq_funcs = {
.set = uvd_v5_0_set_interrupt_state,
.process = uvd_v5_0_process_interrupt,
};
static void uvd_v5_0_set_irq_funcs(struct amdgpu_device *adev)
{
adev->uvd.irq.num_types = 1;
adev->uvd.irq.funcs = &uvd_v5_0_irq_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __UVD_V5_0_H__
#define __UVD_V5_0_H__
extern const struct amdgpu_ip_funcs uvd_v5_0_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
* Authors: Christian König <christian.koenig@amd.com>
*/
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "amdgpu.h"
#include "amdgpu_uvd.h"
#include "vid.h"
#include "uvd/uvd_6_0_d.h"
#include "uvd/uvd_6_0_sh_mask.h"
#include "oss/oss_2_0_d.h"
#include "oss/oss_2_0_sh_mask.h"
static void uvd_v6_0_set_ring_funcs(struct amdgpu_device *adev);
static void uvd_v6_0_set_irq_funcs(struct amdgpu_device *adev);
static int uvd_v6_0_start(struct amdgpu_device *adev);
static void uvd_v6_0_stop(struct amdgpu_device *adev);
/**
* uvd_v6_0_ring_get_rptr - get read pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware read pointer
*/
static uint32_t uvd_v6_0_ring_get_rptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
return RREG32(mmUVD_RBC_RB_RPTR);
}
/**
* uvd_v6_0_ring_get_wptr - get write pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware write pointer
*/
static uint32_t uvd_v6_0_ring_get_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
return RREG32(mmUVD_RBC_RB_WPTR);
}
/**
* uvd_v6_0_ring_set_wptr - set write pointer
*
* @ring: amdgpu_ring pointer
*
* Commits the write pointer to the hardware
*/
static void uvd_v6_0_ring_set_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
WREG32(mmUVD_RBC_RB_WPTR, ring->wptr);
}
static int uvd_v6_0_early_init(struct amdgpu_device *adev)
{
uvd_v6_0_set_ring_funcs(adev);
uvd_v6_0_set_irq_funcs(adev);
return 0;
}
static int uvd_v6_0_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
/* UVD TRAP */
r = amdgpu_irq_add_id(adev, 124, &adev->uvd.irq);
if (r)
return r;
r = amdgpu_uvd_sw_init(adev);
if (r)
return r;
r = amdgpu_uvd_resume(adev);
if (r)
return r;
ring = &adev->uvd.ring;
sprintf(ring->name, "uvd");
r = amdgpu_ring_init(adev, ring, 4096, CP_PACKET2, 0xf,
&adev->uvd.irq, 0, AMDGPU_RING_TYPE_UVD);
return r;
}
static int uvd_v6_0_sw_fini(struct amdgpu_device *adev)
{
int r;
r = amdgpu_uvd_suspend(adev);
if (r)
return r;
r = amdgpu_uvd_sw_fini(adev);
if (r)
return r;
return r;
}
/**
* uvd_v6_0_hw_init - start and test UVD block
*
* @adev: amdgpu_device pointer
*
* Initialize the hardware, boot up the VCPU and do some testing
*/
static int uvd_v6_0_hw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uint32_t tmp;
int r;
r = uvd_v6_0_start(adev);
if (r)
goto done;
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
goto done;
}
r = amdgpu_ring_lock(ring, 10);
if (r) {
DRM_ERROR("amdgpu: ring failed to lock UVD ring (%d).\n", r);
goto done;
}
tmp = PACKET0(mmUVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
tmp = PACKET0(mmUVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
tmp = PACKET0(mmUVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL, 0);
amdgpu_ring_write(ring, tmp);
amdgpu_ring_write(ring, 0xFFFFF);
/* Clear timeout status bits */
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_TIMEOUT_STATUS, 0));
amdgpu_ring_write(ring, 0x8);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_CNTL, 0));
amdgpu_ring_write(ring, 3);
amdgpu_ring_unlock_commit(ring);
done:
if (!r)
DRM_INFO("UVD initialized successfully.\n");
return r;
}
/**
* uvd_v6_0_hw_fini - stop the hardware block
*
* @adev: amdgpu_device pointer
*
* Stop the UVD block, mark ring as not ready any more
*/
static int uvd_v6_0_hw_fini(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uvd_v6_0_stop(adev);
ring->ready = false;
return 0;
}
static int uvd_v6_0_suspend(struct amdgpu_device *adev)
{
int r;
r = uvd_v6_0_hw_fini(adev);
if (r)
return r;
r = amdgpu_uvd_suspend(adev);
if (r)
return r;
return r;
}
static int uvd_v6_0_resume(struct amdgpu_device *adev)
{
int r;
r = amdgpu_uvd_resume(adev);
if (r)
return r;
r = uvd_v6_0_hw_init(adev);
if (r)
return r;
return r;
}
/**
* uvd_v6_0_mc_resume - memory controller programming
*
* @adev: amdgpu_device pointer
*
* Let the UVD memory controller know it's offsets
*/
static void uvd_v6_0_mc_resume(struct amdgpu_device *adev)
{
uint64_t offset;
uint32_t size;
/* programm memory controller bits 0-27 */
WREG32(mmUVD_LMI_VCPU_CACHE_64BIT_BAR_LOW,
lower_32_bits(adev->uvd.gpu_addr));
WREG32(mmUVD_LMI_VCPU_CACHE_64BIT_BAR_HIGH,
upper_32_bits(adev->uvd.gpu_addr));
offset = AMDGPU_UVD_FIRMWARE_OFFSET;
size = AMDGPU_GPU_PAGE_ALIGN(adev->uvd.fw->size + 4);
WREG32(mmUVD_VCPU_CACHE_OFFSET0, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE0, size);
offset += size;
size = AMDGPU_UVD_STACK_SIZE;
WREG32(mmUVD_VCPU_CACHE_OFFSET1, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE1, size);
offset += size;
size = AMDGPU_UVD_HEAP_SIZE;
WREG32(mmUVD_VCPU_CACHE_OFFSET2, offset >> 3);
WREG32(mmUVD_VCPU_CACHE_SIZE2, size);
}
/**
* uvd_v6_0_start - start UVD block
*
* @adev: amdgpu_device pointer
*
* Setup and start the UVD block
*/
static int uvd_v6_0_start(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring = &adev->uvd.ring;
uint32_t rb_bufsz, tmp;
uint32_t lmi_swap_cntl;
uint32_t mp_swap_cntl;
int i, j, r;
/*disable DPG */
WREG32_P(mmUVD_POWER_STATUS, 0, ~(1 << 2));
/* disable byte swapping */
lmi_swap_cntl = 0;
mp_swap_cntl = 0;
uvd_v6_0_mc_resume(adev);
/* disable clock gating */
WREG32(mmUVD_CGC_GATE, 0);
/* disable interupt */
WREG32_P(mmUVD_MASTINT_EN, 0, ~(1 << 1));
/* stall UMC and register bus before resetting VCPU */
WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8));
mdelay(1);
/* put LMI, VCPU, RBC etc... into reset */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__LMI_SOFT_RESET_MASK |
UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK | UVD_SOFT_RESET__LBSI_SOFT_RESET_MASK |
UVD_SOFT_RESET__RBC_SOFT_RESET_MASK | UVD_SOFT_RESET__CSM_SOFT_RESET_MASK |
UVD_SOFT_RESET__CXW_SOFT_RESET_MASK | UVD_SOFT_RESET__TAP_SOFT_RESET_MASK |
UVD_SOFT_RESET__LMI_UMC_SOFT_RESET_MASK);
mdelay(5);
/* take UVD block out of reset */
WREG32_P(mmSRBM_SOFT_RESET, 0, ~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
mdelay(5);
/* initialize UVD memory controller */
WREG32(mmUVD_LMI_CTRL, 0x40 | (1 << 8) | (1 << 13) |
(1 << 21) | (1 << 9) | (1 << 20));
#ifdef __BIG_ENDIAN
/* swap (8 in 32) RB and IB */
lmi_swap_cntl = 0xa;
mp_swap_cntl = 0;
#endif
WREG32(mmUVD_LMI_SWAP_CNTL, lmi_swap_cntl);
WREG32(mmUVD_MP_SWAP_CNTL, mp_swap_cntl);
WREG32(mmUVD_MPC_SET_MUXA0, 0x40c2040);
WREG32(mmUVD_MPC_SET_MUXA1, 0x0);
WREG32(mmUVD_MPC_SET_MUXB0, 0x40c2040);
WREG32(mmUVD_MPC_SET_MUXB1, 0x0);
WREG32(mmUVD_MPC_SET_ALU, 0);
WREG32(mmUVD_MPC_SET_MUX, 0x88);
/* take all subblocks out of reset, except VCPU */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(5);
/* enable VCPU clock */
WREG32(mmUVD_VCPU_CNTL, 1 << 9);
/* enable UMC */
WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8));
/* boot up the VCPU */
WREG32(mmUVD_SOFT_RESET, 0);
mdelay(10);
for (i = 0; i < 10; ++i) {
uint32_t status;
for (j = 0; j < 100; ++j) {
status = RREG32(mmUVD_STATUS);
if (status & 2)
break;
mdelay(10);
}
r = 0;
if (status & 2)
break;
DRM_ERROR("UVD not responding, trying to reset the VCPU!!!\n");
WREG32_P(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK,
~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(10);
WREG32_P(mmUVD_SOFT_RESET, 0,
~UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(10);
r = -1;
}
if (r) {
DRM_ERROR("UVD not responding, giving up!!!\n");
return r;
}
/* enable master interrupt */
WREG32_P(mmUVD_MASTINT_EN, 3 << 1, ~(3 << 1));
/* clear the bit 4 of UVD_STATUS */
WREG32_P(mmUVD_STATUS, 0, ~(2 << 1));
rb_bufsz = order_base_2(ring->ring_size);
tmp = 0;
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BUFSZ, rb_bufsz);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_BLKSZ, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_FETCH, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_WPTR_POLL_EN, 0);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_NO_UPDATE, 1);
tmp = REG_SET_FIELD(tmp, UVD_RBC_RB_CNTL, RB_RPTR_WR_EN, 1);
/* force RBC into idle state */
WREG32(mmUVD_RBC_RB_CNTL, tmp);
/* set the write pointer delay */
WREG32(mmUVD_RBC_RB_WPTR_CNTL, 0);
/* set the wb address */
WREG32(mmUVD_RBC_RB_RPTR_ADDR, (upper_32_bits(ring->gpu_addr) >> 2));
/* programm the RB_BASE for ring buffer */
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_LOW,
lower_32_bits(ring->gpu_addr));
WREG32(mmUVD_LMI_RBC_RB_64BIT_BAR_HIGH,
upper_32_bits(ring->gpu_addr));
/* Initialize the ring buffer's read and write pointers */
WREG32(mmUVD_RBC_RB_RPTR, 0);
ring->wptr = RREG32(mmUVD_RBC_RB_RPTR);
WREG32(mmUVD_RBC_RB_WPTR, ring->wptr);
WREG32_P(mmUVD_RBC_RB_CNTL, 0, ~UVD_RBC_RB_CNTL__RB_NO_FETCH_MASK);
return 0;
}
/**
* uvd_v6_0_stop - stop UVD block
*
* @adev: amdgpu_device pointer
*
* stop the UVD block
*/
static void uvd_v6_0_stop(struct amdgpu_device *adev)
{
/* force RBC into idle state */
WREG32(mmUVD_RBC_RB_CNTL, 0x11010101);
/* Stall UMC and register bus before resetting VCPU */
WREG32_P(mmUVD_LMI_CTRL2, 1 << 8, ~(1 << 8));
mdelay(1);
/* put VCPU into reset */
WREG32(mmUVD_SOFT_RESET, UVD_SOFT_RESET__VCPU_SOFT_RESET_MASK);
mdelay(5);
/* disable VCPU clock */
WREG32(mmUVD_VCPU_CNTL, 0x0);
/* Unstall UMC and register bus */
WREG32_P(mmUVD_LMI_CTRL2, 0, ~(1 << 8));
}
/**
* uvd_v6_0_ring_emit_fence - emit an fence & trap command
*
* @ring: amdgpu_ring pointer
* @fence: fence to emit
*
* Write a fence and a trap command to the ring.
*/
static void uvd_v6_0_ring_emit_fence(struct amdgpu_ring *ring, u64 addr, u64 seq,
bool write64bit)
{
WARN_ON(write64bit);
amdgpu_ring_write(ring, PACKET0(mmUVD_CONTEXT_ID, 0));
amdgpu_ring_write(ring, seq);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0));
amdgpu_ring_write(ring, addr & 0xffffffff);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0));
amdgpu_ring_write(ring, upper_32_bits(addr) & 0xff);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA0, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_DATA1, 0));
amdgpu_ring_write(ring, 0);
amdgpu_ring_write(ring, PACKET0(mmUVD_GPCOM_VCPU_CMD, 0));
amdgpu_ring_write(ring, 2);
}
/**
* uvd_v6_0_ring_emit_semaphore - emit semaphore command
*
* @ring: amdgpu_ring pointer
* @semaphore: semaphore to emit commands for
* @emit_wait: true if we should emit a wait command
*
* Emit a semaphore command (either wait or signal) to the UVD ring.
*/
static bool uvd_v6_0_ring_emit_semaphore(struct amdgpu_ring *ring,
struct amdgpu_semaphore *semaphore,
bool emit_wait)
{
uint64_t addr = semaphore->gpu_addr;
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_ADDR_LOW, 0));
amdgpu_ring_write(ring, (addr >> 3) & 0x000FFFFF);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_ADDR_HIGH, 0));
amdgpu_ring_write(ring, (addr >> 23) & 0x000FFFFF);
amdgpu_ring_write(ring, PACKET0(mmUVD_SEMA_CMD, 0));
amdgpu_ring_write(ring, 0x80 | (emit_wait ? 1 : 0));
return true;
}
/**
* uvd_v6_0_ring_test_ring - register write test
*
* @ring: amdgpu_ring pointer
*
* Test if we can successfully write to the context register
*/
static int uvd_v6_0_ring_test_ring(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
uint32_t tmp = 0;
unsigned i;
int r;
WREG32(mmUVD_CONTEXT_ID, 0xCAFEDEAD);
r = amdgpu_ring_lock(ring, 3);
if (r) {
DRM_ERROR("amdgpu: cp failed to lock ring %d (%d).\n",
ring->idx, r);
return r;
}
amdgpu_ring_write(ring, PACKET0(mmUVD_CONTEXT_ID, 0));
amdgpu_ring_write(ring, 0xDEADBEEF);
amdgpu_ring_unlock_commit(ring);
for (i = 0; i < adev->usec_timeout; i++) {
tmp = RREG32(mmUVD_CONTEXT_ID);
if (tmp == 0xDEADBEEF)
break;
DRM_UDELAY(1);
}
if (i < adev->usec_timeout) {
DRM_INFO("ring test on %d succeeded in %d usecs\n",
ring->idx, i);
} else {
DRM_ERROR("amdgpu: ring %d test failed (0x%08X)\n",
ring->idx, tmp);
r = -EINVAL;
}
return r;
}
/**
* uvd_v6_0_ring_emit_ib - execute indirect buffer
*
* @ring: amdgpu_ring pointer
* @ib: indirect buffer to execute
*
* Write ring commands to execute the indirect buffer
*/
static void uvd_v6_0_ring_emit_ib(struct amdgpu_ring *ring,
struct amdgpu_ib *ib)
{
amdgpu_ring_write(ring, PACKET0(mmUVD_LMI_RBC_IB_64BIT_BAR_LOW, 0));
amdgpu_ring_write(ring, lower_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, PACKET0(mmUVD_LMI_RBC_IB_64BIT_BAR_HIGH, 0));
amdgpu_ring_write(ring, upper_32_bits(ib->gpu_addr));
amdgpu_ring_write(ring, PACKET0(mmUVD_RBC_IB_SIZE, 0));
amdgpu_ring_write(ring, ib->length_dw);
}
/**
* uvd_v6_0_ring_test_ib - test ib execution
*
* @ring: amdgpu_ring pointer
*
* Test if we can successfully execute an IB
*/
static int uvd_v6_0_ring_test_ib(struct amdgpu_ring *ring)
{
struct amdgpu_fence *fence = NULL;
int r;
r = amdgpu_uvd_get_create_msg(ring, 1, NULL);
if (r) {
DRM_ERROR("amdgpu: failed to get create msg (%d).\n", r);
goto error;
}
r = amdgpu_uvd_get_destroy_msg(ring, 1, &fence);
if (r) {
DRM_ERROR("amdgpu: failed to get destroy ib (%d).\n", r);
goto error;
}
r = amdgpu_fence_wait(fence, false);
if (r) {
DRM_ERROR("amdgpu: fence wait failed (%d).\n", r);
goto error;
}
DRM_INFO("ib test on ring %d succeeded\n", ring->idx);
error:
amdgpu_fence_unref(&fence);
return r;
}
static bool uvd_v6_0_is_idle(struct amdgpu_device *adev)
{
return !(RREG32(mmSRBM_STATUS) & SRBM_STATUS__UVD_BUSY_MASK);
}
static int uvd_v6_0_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
for (i = 0; i < adev->usec_timeout; i++) {
if (!(RREG32(mmSRBM_STATUS) & SRBM_STATUS__UVD_BUSY_MASK))
return 0;
}
return -ETIMEDOUT;
}
static int uvd_v6_0_soft_reset(struct amdgpu_device *adev)
{
uvd_v6_0_stop(adev);
WREG32_P(mmSRBM_SOFT_RESET, SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK,
~SRBM_SOFT_RESET__SOFT_RESET_UVD_MASK);
mdelay(5);
return uvd_v6_0_start(adev);
}
static void uvd_v6_0_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "UVD 6.0 registers\n");
dev_info(adev->dev, " UVD_SEMA_ADDR_LOW=0x%08X\n",
RREG32(mmUVD_SEMA_ADDR_LOW));
dev_info(adev->dev, " UVD_SEMA_ADDR_HIGH=0x%08X\n",
RREG32(mmUVD_SEMA_ADDR_HIGH));
dev_info(adev->dev, " UVD_SEMA_CMD=0x%08X\n",
RREG32(mmUVD_SEMA_CMD));
dev_info(adev->dev, " UVD_GPCOM_VCPU_CMD=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_CMD));
dev_info(adev->dev, " UVD_GPCOM_VCPU_DATA0=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_DATA0));
dev_info(adev->dev, " UVD_GPCOM_VCPU_DATA1=0x%08X\n",
RREG32(mmUVD_GPCOM_VCPU_DATA1));
dev_info(adev->dev, " UVD_ENGINE_CNTL=0x%08X\n",
RREG32(mmUVD_ENGINE_CNTL));
dev_info(adev->dev, " UVD_UDEC_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_ADDR_CONFIG));
dev_info(adev->dev, " UVD_UDEC_DB_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_DB_ADDR_CONFIG));
dev_info(adev->dev, " UVD_UDEC_DBW_ADDR_CONFIG=0x%08X\n",
RREG32(mmUVD_UDEC_DBW_ADDR_CONFIG));
dev_info(adev->dev, " UVD_SEMA_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_CNTL));
dev_info(adev->dev, " UVD_LMI_EXT40_ADDR=0x%08X\n",
RREG32(mmUVD_LMI_EXT40_ADDR));
dev_info(adev->dev, " UVD_CTX_INDEX=0x%08X\n",
RREG32(mmUVD_CTX_INDEX));
dev_info(adev->dev, " UVD_CTX_DATA=0x%08X\n",
RREG32(mmUVD_CTX_DATA));
dev_info(adev->dev, " UVD_CGC_GATE=0x%08X\n",
RREG32(mmUVD_CGC_GATE));
dev_info(adev->dev, " UVD_CGC_CTRL=0x%08X\n",
RREG32(mmUVD_CGC_CTRL));
dev_info(adev->dev, " UVD_LMI_CTRL2=0x%08X\n",
RREG32(mmUVD_LMI_CTRL2));
dev_info(adev->dev, " UVD_MASTINT_EN=0x%08X\n",
RREG32(mmUVD_MASTINT_EN));
dev_info(adev->dev, " UVD_LMI_ADDR_EXT=0x%08X\n",
RREG32(mmUVD_LMI_ADDR_EXT));
dev_info(adev->dev, " UVD_LMI_CTRL=0x%08X\n",
RREG32(mmUVD_LMI_CTRL));
dev_info(adev->dev, " UVD_LMI_SWAP_CNTL=0x%08X\n",
RREG32(mmUVD_LMI_SWAP_CNTL));
dev_info(adev->dev, " UVD_MP_SWAP_CNTL=0x%08X\n",
RREG32(mmUVD_MP_SWAP_CNTL));
dev_info(adev->dev, " UVD_MPC_SET_MUXA0=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXA0));
dev_info(adev->dev, " UVD_MPC_SET_MUXA1=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXA1));
dev_info(adev->dev, " UVD_MPC_SET_MUXB0=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXB0));
dev_info(adev->dev, " UVD_MPC_SET_MUXB1=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUXB1));
dev_info(adev->dev, " UVD_MPC_SET_MUX=0x%08X\n",
RREG32(mmUVD_MPC_SET_MUX));
dev_info(adev->dev, " UVD_MPC_SET_ALU=0x%08X\n",
RREG32(mmUVD_MPC_SET_ALU));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET0=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET0));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE0=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE0));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET1=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET1));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE1=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE1));
dev_info(adev->dev, " UVD_VCPU_CACHE_OFFSET2=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_OFFSET2));
dev_info(adev->dev, " UVD_VCPU_CACHE_SIZE2=0x%08X\n",
RREG32(mmUVD_VCPU_CACHE_SIZE2));
dev_info(adev->dev, " UVD_VCPU_CNTL=0x%08X\n",
RREG32(mmUVD_VCPU_CNTL));
dev_info(adev->dev, " UVD_SOFT_RESET=0x%08X\n",
RREG32(mmUVD_SOFT_RESET));
dev_info(adev->dev, " UVD_RBC_IB_SIZE=0x%08X\n",
RREG32(mmUVD_RBC_IB_SIZE));
dev_info(adev->dev, " UVD_RBC_RB_RPTR=0x%08X\n",
RREG32(mmUVD_RBC_RB_RPTR));
dev_info(adev->dev, " UVD_RBC_RB_WPTR=0x%08X\n",
RREG32(mmUVD_RBC_RB_WPTR));
dev_info(adev->dev, " UVD_RBC_RB_WPTR_CNTL=0x%08X\n",
RREG32(mmUVD_RBC_RB_WPTR_CNTL));
dev_info(adev->dev, " UVD_RBC_RB_CNTL=0x%08X\n",
RREG32(mmUVD_RBC_RB_CNTL));
dev_info(adev->dev, " UVD_STATUS=0x%08X\n",
RREG32(mmUVD_STATUS));
dev_info(adev->dev, " UVD_SEMA_TIMEOUT_STATUS=0x%08X\n",
RREG32(mmUVD_SEMA_TIMEOUT_STATUS));
dev_info(adev->dev, " UVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_WAIT_INCOMPLETE_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_WAIT_FAULT_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL=0x%08X\n",
RREG32(mmUVD_SEMA_SIGNAL_INCOMPLETE_TIMEOUT_CNTL));
dev_info(adev->dev, " UVD_CONTEXT_ID=0x%08X\n",
RREG32(mmUVD_CONTEXT_ID));
}
static int uvd_v6_0_set_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
enum amdgpu_interrupt_state state)
{
// TODO
return 0;
}
static int uvd_v6_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
DRM_DEBUG("IH: UVD TRAP\n");
amdgpu_fence_process(&adev->uvd.ring);
return 0;
}
static int uvd_v6_0_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
//TODO
return 0;
}
static int uvd_v6_0_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
/* This doesn't actually powergate the UVD block.
* That's done in the dpm code via the SMC. This
* just re-inits the block as necessary. The actual
* gating still happens in the dpm code. We should
* revisit this when there is a cleaner line between
* the smc and the hw blocks
*/
if (state == AMDGPU_PG_STATE_GATE) {
uvd_v6_0_stop(adev);
return 0;
} else {
return uvd_v6_0_start(adev);
}
}
const struct amdgpu_ip_funcs uvd_v6_0_ip_funcs = {
.early_init = uvd_v6_0_early_init,
.late_init = NULL,
.sw_init = uvd_v6_0_sw_init,
.sw_fini = uvd_v6_0_sw_fini,
.hw_init = uvd_v6_0_hw_init,
.hw_fini = uvd_v6_0_hw_fini,
.suspend = uvd_v6_0_suspend,
.resume = uvd_v6_0_resume,
.is_idle = uvd_v6_0_is_idle,
.wait_for_idle = uvd_v6_0_wait_for_idle,
.soft_reset = uvd_v6_0_soft_reset,
.print_status = uvd_v6_0_print_status,
.set_clockgating_state = uvd_v6_0_set_clockgating_state,
.set_powergating_state = uvd_v6_0_set_powergating_state,
};
static const struct amdgpu_ring_funcs uvd_v6_0_ring_funcs = {
.get_rptr = uvd_v6_0_ring_get_rptr,
.get_wptr = uvd_v6_0_ring_get_wptr,
.set_wptr = uvd_v6_0_ring_set_wptr,
.parse_cs = amdgpu_uvd_ring_parse_cs,
.emit_ib = uvd_v6_0_ring_emit_ib,
.emit_fence = uvd_v6_0_ring_emit_fence,
.emit_semaphore = uvd_v6_0_ring_emit_semaphore,
.test_ring = uvd_v6_0_ring_test_ring,
.test_ib = uvd_v6_0_ring_test_ib,
.is_lockup = amdgpu_ring_test_lockup,
};
static void uvd_v6_0_set_ring_funcs(struct amdgpu_device *adev)
{
adev->uvd.ring.funcs = &uvd_v6_0_ring_funcs;
}
static const struct amdgpu_irq_src_funcs uvd_v6_0_irq_funcs = {
.set = uvd_v6_0_set_interrupt_state,
.process = uvd_v6_0_process_interrupt,
};
static void uvd_v6_0_set_irq_funcs(struct amdgpu_device *adev)
{
adev->uvd.irq.num_types = 1;
adev->uvd.irq.funcs = &uvd_v6_0_irq_funcs;
}
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __UVD_V6_0_H__
#define __UVD_V6_0_H__
extern const struct amdgpu_ip_funcs uvd_v6_0_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
* All Rights Reserved.
*
* 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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.
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* Authors: Christian König <christian.koenig@amd.com>
*/
#include <linux/firmware.h>
#include <drm/drmP.h>
#include "amdgpu.h"
#include "amdgpu_vce.h"
#include "vid.h"
#include "vce/vce_3_0_d.h"
#include "vce/vce_3_0_sh_mask.h"
#include "oss/oss_2_0_d.h"
#include "oss/oss_2_0_sh_mask.h"
static void vce_v3_0_mc_resume(struct amdgpu_device *adev);
static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev);
static void vce_v3_0_set_irq_funcs(struct amdgpu_device *adev);
/**
* vce_v3_0_ring_get_rptr - get read pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware read pointer
*/
static uint32_t vce_v3_0_ring_get_rptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
if (ring == &adev->vce.ring[0])
return RREG32(mmVCE_RB_RPTR);
else
return RREG32(mmVCE_RB_RPTR2);
}
/**
* vce_v3_0_ring_get_wptr - get write pointer
*
* @ring: amdgpu_ring pointer
*
* Returns the current hardware write pointer
*/
static uint32_t vce_v3_0_ring_get_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
if (ring == &adev->vce.ring[0])
return RREG32(mmVCE_RB_WPTR);
else
return RREG32(mmVCE_RB_WPTR2);
}
/**
* vce_v3_0_ring_set_wptr - set write pointer
*
* @ring: amdgpu_ring pointer
*
* Commits the write pointer to the hardware
*/
static void vce_v3_0_ring_set_wptr(struct amdgpu_ring *ring)
{
struct amdgpu_device *adev = ring->adev;
if (ring == &adev->vce.ring[0])
WREG32(mmVCE_RB_WPTR, ring->wptr);
else
WREG32(mmVCE_RB_WPTR2, ring->wptr);
}
/**
* vce_v3_0_start - start VCE block
*
* @adev: amdgpu_device pointer
*
* Setup and start the VCE block
*/
static int vce_v3_0_start(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int i, j, r;
vce_v3_0_mc_resume(adev);
/* set BUSY flag */
WREG32_P(mmVCE_STATUS, 1, ~1);
ring = &adev->vce.ring[0];
WREG32(mmVCE_RB_RPTR, ring->wptr);
WREG32(mmVCE_RB_WPTR, ring->wptr);
WREG32(mmVCE_RB_BASE_LO, ring->gpu_addr);
WREG32(mmVCE_RB_BASE_HI, upper_32_bits(ring->gpu_addr));
WREG32(mmVCE_RB_SIZE, ring->ring_size / 4);
ring = &adev->vce.ring[1];
WREG32(mmVCE_RB_RPTR2, ring->wptr);
WREG32(mmVCE_RB_WPTR2, ring->wptr);
WREG32(mmVCE_RB_BASE_LO2, ring->gpu_addr);
WREG32(mmVCE_RB_BASE_HI2, upper_32_bits(ring->gpu_addr));
WREG32(mmVCE_RB_SIZE2, ring->ring_size / 4);
WREG32_P(mmVCE_VCPU_CNTL, VCE_VCPU_CNTL__CLK_EN_MASK, ~VCE_VCPU_CNTL__CLK_EN_MASK);
WREG32_P(mmVCE_SOFT_RESET,
VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
mdelay(100);
WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
for (i = 0; i < 10; ++i) {
uint32_t status;
for (j = 0; j < 100; ++j) {
status = RREG32(mmVCE_STATUS);
if (status & 2)
break;
mdelay(10);
}
r = 0;
if (status & 2)
break;
DRM_ERROR("VCE not responding, trying to reset the ECPU!!!\n");
WREG32_P(mmVCE_SOFT_RESET, VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK,
~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
mdelay(10);
WREG32_P(mmVCE_SOFT_RESET, 0, ~VCE_SOFT_RESET__ECPU_SOFT_RESET_MASK);
mdelay(10);
r = -1;
}
/* clear BUSY flag */
WREG32_P(mmVCE_STATUS, 0, ~1);
if (r) {
DRM_ERROR("VCE not responding, giving up!!!\n");
return r;
}
return 0;
}
static int vce_v3_0_early_init(struct amdgpu_device *adev)
{
vce_v3_0_set_ring_funcs(adev);
vce_v3_0_set_irq_funcs(adev);
return 0;
}
static int vce_v3_0_sw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
/* VCE */
r = amdgpu_irq_add_id(adev, 167, &adev->vce.irq);
if (r)
return r;
r = amdgpu_vce_sw_init(adev);
if (r)
return r;
r = amdgpu_vce_resume(adev);
if (r)
return r;
ring = &adev->vce.ring[0];
sprintf(ring->name, "vce0");
r = amdgpu_ring_init(adev, ring, 4096, VCE_CMD_NO_OP, 0xf,
&adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
if (r)
return r;
ring = &adev->vce.ring[1];
sprintf(ring->name, "vce1");
r = amdgpu_ring_init(adev, ring, 4096, VCE_CMD_NO_OP, 0xf,
&adev->vce.irq, 0, AMDGPU_RING_TYPE_VCE);
if (r)
return r;
return r;
}
static int vce_v3_0_sw_fini(struct amdgpu_device *adev)
{
int r;
r = amdgpu_vce_suspend(adev);
if (r)
return r;
r = amdgpu_vce_sw_fini(adev);
if (r)
return r;
return r;
}
static int vce_v3_0_hw_init(struct amdgpu_device *adev)
{
struct amdgpu_ring *ring;
int r;
r = vce_v3_0_start(adev);
if (r)
return r;
ring = &adev->vce.ring[0];
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
return r;
}
ring = &adev->vce.ring[1];
ring->ready = true;
r = amdgpu_ring_test_ring(ring);
if (r) {
ring->ready = false;
return r;
}
DRM_INFO("VCE initialized successfully.\n");
return 0;
}
static int vce_v3_0_hw_fini(struct amdgpu_device *adev)
{
// TODO
return 0;
}
static int vce_v3_0_suspend(struct amdgpu_device *adev)
{
int r;
r = vce_v3_0_hw_fini(adev);
if (r)
return r;
r = amdgpu_vce_suspend(adev);
if (r)
return r;
return r;
}
static int vce_v3_0_resume(struct amdgpu_device *adev)
{
int r;
r = amdgpu_vce_resume(adev);
if (r)
return r;
r = vce_v3_0_hw_init(adev);
if (r)
return r;
return r;
}
static void vce_v3_0_mc_resume(struct amdgpu_device *adev)
{
uint32_t offset, size;
WREG32_P(mmVCE_CLOCK_GATING_A, 0, ~(1 << 16));
WREG32_P(mmVCE_UENC_CLOCK_GATING, 0x1FF000, ~0xFF9FF000);
WREG32_P(mmVCE_UENC_REG_CLOCK_GATING, 0x3F, ~0x3F);
WREG32(mmVCE_CLOCK_GATING_B, 0xf7);
WREG32(mmVCE_LMI_CTRL, 0x00398000);
WREG32_P(mmVCE_LMI_CACHE_CTRL, 0x0, ~0x1);
WREG32(mmVCE_LMI_SWAP_CNTL, 0);
WREG32(mmVCE_LMI_SWAP_CNTL1, 0);
WREG32(mmVCE_LMI_VM_CTRL, 0);
WREG32(mmVCE_LMI_VCPU_CACHE_40BIT_BAR, (adev->vce.gpu_addr >> 8));
offset = AMDGPU_VCE_FIRMWARE_OFFSET;
size = AMDGPU_GPU_PAGE_ALIGN(adev->vce.fw->size);
WREG32(mmVCE_VCPU_CACHE_OFFSET0, offset & 0x7fffffff);
WREG32(mmVCE_VCPU_CACHE_SIZE0, size);
offset += size;
size = AMDGPU_VCE_STACK_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET1, offset & 0x7fffffff);
WREG32(mmVCE_VCPU_CACHE_SIZE1, size);
offset += size;
size = AMDGPU_VCE_HEAP_SIZE;
WREG32(mmVCE_VCPU_CACHE_OFFSET2, offset & 0x7fffffff);
WREG32(mmVCE_VCPU_CACHE_SIZE2, size);
WREG32_P(mmVCE_LMI_CTRL2, 0x0, ~0x100);
WREG32_P(mmVCE_SYS_INT_EN, VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK,
~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK);
}
static bool vce_v3_0_is_idle(struct amdgpu_device *adev)
{
return !(RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK);
}
static int vce_v3_0_wait_for_idle(struct amdgpu_device *adev)
{
unsigned i;
for (i = 0; i < adev->usec_timeout; i++) {
if (!(RREG32(mmSRBM_STATUS2) & SRBM_STATUS2__VCE_BUSY_MASK))
return 0;
}
return -ETIMEDOUT;
}
static int vce_v3_0_soft_reset(struct amdgpu_device *adev)
{
WREG32_P(mmSRBM_SOFT_RESET, SRBM_SOFT_RESET__SOFT_RESET_VCE_MASK,
~SRBM_SOFT_RESET__SOFT_RESET_VCE_MASK);
mdelay(5);
return vce_v3_0_start(adev);
}
static void vce_v3_0_print_status(struct amdgpu_device *adev)
{
dev_info(adev->dev, "VCE 3.0 registers\n");
dev_info(adev->dev, " VCE_STATUS=0x%08X\n",
RREG32(mmVCE_STATUS));
dev_info(adev->dev, " VCE_VCPU_CNTL=0x%08X\n",
RREG32(mmVCE_VCPU_CNTL));
dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET0=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_OFFSET0));
dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE0=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_SIZE0));
dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET1=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_OFFSET1));
dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE1=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_SIZE1));
dev_info(adev->dev, " VCE_VCPU_CACHE_OFFSET2=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_OFFSET2));
dev_info(adev->dev, " VCE_VCPU_CACHE_SIZE2=0x%08X\n",
RREG32(mmVCE_VCPU_CACHE_SIZE2));
dev_info(adev->dev, " VCE_SOFT_RESET=0x%08X\n",
RREG32(mmVCE_SOFT_RESET));
dev_info(adev->dev, " VCE_RB_BASE_LO2=0x%08X\n",
RREG32(mmVCE_RB_BASE_LO2));
dev_info(adev->dev, " VCE_RB_BASE_HI2=0x%08X\n",
RREG32(mmVCE_RB_BASE_HI2));
dev_info(adev->dev, " VCE_RB_SIZE2=0x%08X\n",
RREG32(mmVCE_RB_SIZE2));
dev_info(adev->dev, " VCE_RB_RPTR2=0x%08X\n",
RREG32(mmVCE_RB_RPTR2));
dev_info(adev->dev, " VCE_RB_WPTR2=0x%08X\n",
RREG32(mmVCE_RB_WPTR2));
dev_info(adev->dev, " VCE_RB_BASE_LO=0x%08X\n",
RREG32(mmVCE_RB_BASE_LO));
dev_info(adev->dev, " VCE_RB_BASE_HI=0x%08X\n",
RREG32(mmVCE_RB_BASE_HI));
dev_info(adev->dev, " VCE_RB_SIZE=0x%08X\n",
RREG32(mmVCE_RB_SIZE));
dev_info(adev->dev, " VCE_RB_RPTR=0x%08X\n",
RREG32(mmVCE_RB_RPTR));
dev_info(adev->dev, " VCE_RB_WPTR=0x%08X\n",
RREG32(mmVCE_RB_WPTR));
dev_info(adev->dev, " VCE_CLOCK_GATING_A=0x%08X\n",
RREG32(mmVCE_CLOCK_GATING_A));
dev_info(adev->dev, " VCE_CLOCK_GATING_B=0x%08X\n",
RREG32(mmVCE_CLOCK_GATING_B));
dev_info(adev->dev, " VCE_UENC_CLOCK_GATING=0x%08X\n",
RREG32(mmVCE_UENC_CLOCK_GATING));
dev_info(adev->dev, " VCE_UENC_REG_CLOCK_GATING=0x%08X\n",
RREG32(mmVCE_UENC_REG_CLOCK_GATING));
dev_info(adev->dev, " VCE_SYS_INT_EN=0x%08X\n",
RREG32(mmVCE_SYS_INT_EN));
dev_info(adev->dev, " VCE_LMI_CTRL2=0x%08X\n",
RREG32(mmVCE_LMI_CTRL2));
dev_info(adev->dev, " VCE_LMI_CTRL=0x%08X\n",
RREG32(mmVCE_LMI_CTRL));
dev_info(adev->dev, " VCE_LMI_VM_CTRL=0x%08X\n",
RREG32(mmVCE_LMI_VM_CTRL));
dev_info(adev->dev, " VCE_LMI_SWAP_CNTL=0x%08X\n",
RREG32(mmVCE_LMI_SWAP_CNTL));
dev_info(adev->dev, " VCE_LMI_SWAP_CNTL1=0x%08X\n",
RREG32(mmVCE_LMI_SWAP_CNTL1));
dev_info(adev->dev, " VCE_LMI_CACHE_CTRL=0x%08X\n",
RREG32(mmVCE_LMI_CACHE_CTRL));
}
static int vce_v3_0_set_interrupt_state(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
unsigned type,
enum amdgpu_interrupt_state state)
{
uint32_t val = 0;
if (state == AMDGPU_IRQ_STATE_ENABLE)
val |= VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK;
WREG32_P(mmVCE_SYS_INT_EN, val, ~VCE_SYS_INT_EN__VCE_SYS_INT_TRAP_INTERRUPT_EN_MASK);
return 0;
}
static int vce_v3_0_process_interrupt(struct amdgpu_device *adev,
struct amdgpu_irq_src *source,
struct amdgpu_iv_entry *entry)
{
DRM_DEBUG("IH: VCE\n");
switch (entry->src_data) {
case 0:
amdgpu_fence_process(&adev->vce.ring[0]);
break;
case 1:
amdgpu_fence_process(&adev->vce.ring[1]);
break;
default:
DRM_ERROR("Unhandled interrupt: %d %d\n",
entry->src_id, entry->src_data);
break;
}
return 0;
}
static int vce_v3_0_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
//TODO
return 0;
}
static int vce_v3_0_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
/* This doesn't actually powergate the VCE block.
* That's done in the dpm code via the SMC. This
* just re-inits the block as necessary. The actual
* gating still happens in the dpm code. We should
* revisit this when there is a cleaner line between
* the smc and the hw blocks
*/
if (state == AMDGPU_PG_STATE_GATE)
/* XXX do we need a vce_v3_0_stop()? */
return 0;
else
return vce_v3_0_start(adev);
}
const struct amdgpu_ip_funcs vce_v3_0_ip_funcs = {
.early_init = vce_v3_0_early_init,
.late_init = NULL,
.sw_init = vce_v3_0_sw_init,
.sw_fini = vce_v3_0_sw_fini,
.hw_init = vce_v3_0_hw_init,
.hw_fini = vce_v3_0_hw_fini,
.suspend = vce_v3_0_suspend,
.resume = vce_v3_0_resume,
.is_idle = vce_v3_0_is_idle,
.wait_for_idle = vce_v3_0_wait_for_idle,
.soft_reset = vce_v3_0_soft_reset,
.print_status = vce_v3_0_print_status,
.set_clockgating_state = vce_v3_0_set_clockgating_state,
.set_powergating_state = vce_v3_0_set_powergating_state,
};
static const struct amdgpu_ring_funcs vce_v3_0_ring_funcs = {
.get_rptr = vce_v3_0_ring_get_rptr,
.get_wptr = vce_v3_0_ring_get_wptr,
.set_wptr = vce_v3_0_ring_set_wptr,
.parse_cs = amdgpu_vce_ring_parse_cs,
.emit_ib = amdgpu_vce_ring_emit_ib,
.emit_fence = amdgpu_vce_ring_emit_fence,
.emit_semaphore = amdgpu_vce_ring_emit_semaphore,
.test_ring = amdgpu_vce_ring_test_ring,
.test_ib = amdgpu_vce_ring_test_ib,
.is_lockup = amdgpu_ring_test_lockup,
};
static void vce_v3_0_set_ring_funcs(struct amdgpu_device *adev)
{
adev->vce.ring[0].funcs = &vce_v3_0_ring_funcs;
adev->vce.ring[1].funcs = &vce_v3_0_ring_funcs;
}
static const struct amdgpu_irq_src_funcs vce_v3_0_irq_funcs = {
.set = vce_v3_0_set_interrupt_state,
.process = vce_v3_0_process_interrupt,
};
static void vce_v3_0_set_irq_funcs(struct amdgpu_device *adev)
{
adev->vce.irq.num_types = 1;
adev->vce.irq.funcs = &vce_v3_0_irq_funcs;
};
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __VCE_V3_0_H__
#define __VCE_V3_0_H__
extern const struct amdgpu_ip_funcs vce_v3_0_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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/firmware.h>
#include <linux/slab.h>
#include <linux/module.h>
#include "drmP.h"
#include "amdgpu.h"
#include "amdgpu_atombios.h"
#include "amdgpu_ih.h"
#include "amdgpu_uvd.h"
#include "amdgpu_vce.h"
#include "amdgpu_ucode.h"
#include "atom.h"
#include "gmc/gmc_8_1_d.h"
#include "gmc/gmc_8_1_sh_mask.h"
#include "oss/oss_3_0_d.h"
#include "oss/oss_3_0_sh_mask.h"
#include "bif/bif_5_0_d.h"
#include "bif/bif_5_0_sh_mask.h"
#include "gca/gfx_8_0_d.h"
#include "gca/gfx_8_0_sh_mask.h"
#include "smu/smu_7_1_1_d.h"
#include "smu/smu_7_1_1_sh_mask.h"
#include "uvd/uvd_5_0_d.h"
#include "uvd/uvd_5_0_sh_mask.h"
#include "vce/vce_3_0_d.h"
#include "vce/vce_3_0_sh_mask.h"
#include "dce/dce_10_0_d.h"
#include "dce/dce_10_0_sh_mask.h"
#include "vid.h"
#include "vi.h"
#include "vi_dpm.h"
#include "gmc_v8_0.h"
#include "gfx_v8_0.h"
#include "sdma_v2_4.h"
#include "sdma_v3_0.h"
#include "dce_v10_0.h"
#include "dce_v11_0.h"
#include "iceland_ih.h"
#include "tonga_ih.h"
#include "cz_ih.h"
#include "uvd_v5_0.h"
#include "uvd_v6_0.h"
#include "vce_v3_0.h"
/*
* Indirect registers accessor
*/
static u32 vi_pcie_rreg(struct amdgpu_device *adev, u32 reg)
{
unsigned long flags;
u32 r;
spin_lock_irqsave(&adev->pcie_idx_lock, flags);
WREG32(mmPCIE_INDEX, reg);
(void)RREG32(mmPCIE_INDEX);
r = RREG32(mmPCIE_DATA);
spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
return r;
}
static void vi_pcie_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
{
unsigned long flags;
spin_lock_irqsave(&adev->pcie_idx_lock, flags);
WREG32(mmPCIE_INDEX, reg);
(void)RREG32(mmPCIE_INDEX);
WREG32(mmPCIE_DATA, v);
(void)RREG32(mmPCIE_DATA);
spin_unlock_irqrestore(&adev->pcie_idx_lock, flags);
}
static u32 vi_smc_rreg(struct amdgpu_device *adev, u32 reg)
{
unsigned long flags;
u32 r;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
WREG32(mmSMC_IND_INDEX_0, (reg));
r = RREG32(mmSMC_IND_DATA_0);
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
return r;
}
static void vi_smc_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
{
unsigned long flags;
spin_lock_irqsave(&adev->smc_idx_lock, flags);
WREG32(mmSMC_IND_INDEX_0, (reg));
WREG32(mmSMC_IND_DATA_0, (v));
spin_unlock_irqrestore(&adev->smc_idx_lock, flags);
}
static u32 vi_uvd_ctx_rreg(struct amdgpu_device *adev, u32 reg)
{
unsigned long flags;
u32 r;
spin_lock_irqsave(&adev->uvd_ctx_idx_lock, flags);
WREG32(mmUVD_CTX_INDEX, ((reg) & 0x1ff));
r = RREG32(mmUVD_CTX_DATA);
spin_unlock_irqrestore(&adev->uvd_ctx_idx_lock, flags);
return r;
}
static void vi_uvd_ctx_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
{
unsigned long flags;
spin_lock_irqsave(&adev->uvd_ctx_idx_lock, flags);
WREG32(mmUVD_CTX_INDEX, ((reg) & 0x1ff));
WREG32(mmUVD_CTX_DATA, (v));
spin_unlock_irqrestore(&adev->uvd_ctx_idx_lock, flags);
}
static u32 vi_didt_rreg(struct amdgpu_device *adev, u32 reg)
{
unsigned long flags;
u32 r;
spin_lock_irqsave(&adev->didt_idx_lock, flags);
WREG32(mmDIDT_IND_INDEX, (reg));
r = RREG32(mmDIDT_IND_DATA);
spin_unlock_irqrestore(&adev->didt_idx_lock, flags);
return r;
}
static void vi_didt_wreg(struct amdgpu_device *adev, u32 reg, u32 v)
{
unsigned long flags;
spin_lock_irqsave(&adev->didt_idx_lock, flags);
WREG32(mmDIDT_IND_INDEX, (reg));
WREG32(mmDIDT_IND_DATA, (v));
spin_unlock_irqrestore(&adev->didt_idx_lock, flags);
}
static const u32 tonga_mgcg_cgcg_init[] =
{
mmCGTT_DRM_CLK_CTRL0, 0xffffffff, 0x00600100,
mmPCIE_INDEX, 0xffffffff, 0x0140001c,
mmPCIE_DATA, 0x000f0000, 0x00000000,
mmSMC_IND_INDEX_4, 0xffffffff, 0xC060000C,
mmSMC_IND_DATA_4, 0xc0000fff, 0x00000100,
mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100,
mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000,
mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100,
mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104,
};
static const u32 iceland_mgcg_cgcg_init[] =
{
mmPCIE_INDEX, 0xffffffff, ixPCIE_CNTL2,
mmPCIE_DATA, 0x000f0000, 0x00000000,
mmSMC_IND_INDEX_4, 0xffffffff, ixCGTT_ROM_CLK_CTRL0,
mmSMC_IND_DATA_4, 0xc0000fff, 0x00000100,
mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104,
};
static const u32 cz_mgcg_cgcg_init[] =
{
mmCGTT_DRM_CLK_CTRL0, 0xffffffff, 0x00600100,
mmPCIE_INDEX, 0xffffffff, 0x0140001c,
mmPCIE_DATA, 0x000f0000, 0x00000000,
mmXDMA_CLOCK_GATING_CNTL, 0xffffffff, 0x00000100,
mmXDMA_MEM_POWER_CNTL, 0x00000101, 0x00000000,
mmCGTT_DRM_CLK_CTRL0, 0xff000fff, 0x00000100,
mmHDP_XDP_CGTT_BLK_CTRL, 0xc0000fff, 0x00000104,
};
static void vi_init_golden_registers(struct amdgpu_device *adev)
{
/* Some of the registers might be dependent on GRBM_GFX_INDEX */
mutex_lock(&adev->grbm_idx_mutex);
switch (adev->asic_type) {
case CHIP_TOPAZ:
amdgpu_program_register_sequence(adev,
iceland_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(iceland_mgcg_cgcg_init));
break;
case CHIP_TONGA:
amdgpu_program_register_sequence(adev,
tonga_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(tonga_mgcg_cgcg_init));
break;
case CHIP_CARRIZO:
amdgpu_program_register_sequence(adev,
cz_mgcg_cgcg_init,
(const u32)ARRAY_SIZE(cz_mgcg_cgcg_init));
break;
default:
break;
}
mutex_unlock(&adev->grbm_idx_mutex);
}
/**
* vi_get_xclk - get the xclk
*
* @adev: amdgpu_device pointer
*
* Returns the reference clock used by the gfx engine
* (VI).
*/
static u32 vi_get_xclk(struct amdgpu_device *adev)
{
u32 reference_clock = adev->clock.spll.reference_freq;
u32 tmp;
if (adev->flags & AMDGPU_IS_APU)
return reference_clock;
tmp = RREG32_SMC(ixCG_CLKPIN_CNTL_2);
if (REG_GET_FIELD(tmp, CG_CLKPIN_CNTL_2, MUX_TCLK_TO_XCLK))
return 1000;
tmp = RREG32_SMC(ixCG_CLKPIN_CNTL);
if (REG_GET_FIELD(tmp, CG_CLKPIN_CNTL, XTALIN_DIVIDE))
return reference_clock / 4;
return reference_clock;
}
/**
* vi_srbm_select - select specific register instances
*
* @adev: amdgpu_device pointer
* @me: selected ME (micro engine)
* @pipe: pipe
* @queue: queue
* @vmid: VMID
*
* Switches the currently active registers instances. Some
* registers are instanced per VMID, others are instanced per
* me/pipe/queue combination.
*/
void vi_srbm_select(struct amdgpu_device *adev,
u32 me, u32 pipe, u32 queue, u32 vmid)
{
u32 srbm_gfx_cntl = 0;
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, PIPEID, pipe);
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, MEID, me);
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, VMID, vmid);
srbm_gfx_cntl = REG_SET_FIELD(srbm_gfx_cntl, SRBM_GFX_CNTL, QUEUEID, queue);
WREG32(mmSRBM_GFX_CNTL, srbm_gfx_cntl);
}
static void vi_vga_set_state(struct amdgpu_device *adev, bool state)
{
/* todo */
}
static bool vi_read_disabled_bios(struct amdgpu_device *adev)
{
u32 bus_cntl;
u32 d1vga_control = 0;
u32 d2vga_control = 0;
u32 vga_render_control = 0;
u32 rom_cntl;
bool r;
bus_cntl = RREG32(mmBUS_CNTL);
if (adev->mode_info.num_crtc) {
d1vga_control = RREG32(mmD1VGA_CONTROL);
d2vga_control = RREG32(mmD2VGA_CONTROL);
vga_render_control = RREG32(mmVGA_RENDER_CONTROL);
}
rom_cntl = RREG32_SMC(ixROM_CNTL);
/* enable the rom */
WREG32(mmBUS_CNTL, (bus_cntl & ~BUS_CNTL__BIOS_ROM_DIS_MASK));
if (adev->mode_info.num_crtc) {
/* Disable VGA mode */
WREG32(mmD1VGA_CONTROL,
(d1vga_control & ~(D1VGA_CONTROL__D1VGA_MODE_ENABLE_MASK |
D1VGA_CONTROL__D1VGA_TIMING_SELECT_MASK)));
WREG32(mmD2VGA_CONTROL,
(d2vga_control & ~(D2VGA_CONTROL__D2VGA_MODE_ENABLE_MASK |
D2VGA_CONTROL__D2VGA_TIMING_SELECT_MASK)));
WREG32(mmVGA_RENDER_CONTROL,
(vga_render_control & ~VGA_RENDER_CONTROL__VGA_VSTATUS_CNTL_MASK));
}
WREG32_SMC(ixROM_CNTL, rom_cntl | ROM_CNTL__SCK_OVERWRITE_MASK);
r = amdgpu_read_bios(adev);
/* restore regs */
WREG32(mmBUS_CNTL, bus_cntl);
if (adev->mode_info.num_crtc) {
WREG32(mmD1VGA_CONTROL, d1vga_control);
WREG32(mmD2VGA_CONTROL, d2vga_control);
WREG32(mmVGA_RENDER_CONTROL, vga_render_control);
}
WREG32_SMC(ixROM_CNTL, rom_cntl);
return r;
}
static struct amdgpu_allowed_register_entry tonga_allowed_read_registers[] = {
{mmGB_MACROTILE_MODE7, true},
};
static struct amdgpu_allowed_register_entry cz_allowed_read_registers[] = {
{mmGB_TILE_MODE7, true},
{mmGB_TILE_MODE12, true},
{mmGB_TILE_MODE17, true},
{mmGB_TILE_MODE23, true},
{mmGB_MACROTILE_MODE7, true},
};
static struct amdgpu_allowed_register_entry vi_allowed_read_registers[] = {
{mmGRBM_STATUS, false},
{mmGB_ADDR_CONFIG, false},
{mmMC_ARB_RAMCFG, false},
{mmGB_TILE_MODE0, false},
{mmGB_TILE_MODE1, false},
{mmGB_TILE_MODE2, false},
{mmGB_TILE_MODE3, false},
{mmGB_TILE_MODE4, false},
{mmGB_TILE_MODE5, false},
{mmGB_TILE_MODE6, false},
{mmGB_TILE_MODE7, false},
{mmGB_TILE_MODE8, false},
{mmGB_TILE_MODE9, false},
{mmGB_TILE_MODE10, false},
{mmGB_TILE_MODE11, false},
{mmGB_TILE_MODE12, false},
{mmGB_TILE_MODE13, false},
{mmGB_TILE_MODE14, false},
{mmGB_TILE_MODE15, false},
{mmGB_TILE_MODE16, false},
{mmGB_TILE_MODE17, false},
{mmGB_TILE_MODE18, false},
{mmGB_TILE_MODE19, false},
{mmGB_TILE_MODE20, false},
{mmGB_TILE_MODE21, false},
{mmGB_TILE_MODE22, false},
{mmGB_TILE_MODE23, false},
{mmGB_TILE_MODE24, false},
{mmGB_TILE_MODE25, false},
{mmGB_TILE_MODE26, false},
{mmGB_TILE_MODE27, false},
{mmGB_TILE_MODE28, false},
{mmGB_TILE_MODE29, false},
{mmGB_TILE_MODE30, false},
{mmGB_TILE_MODE31, false},
{mmGB_MACROTILE_MODE0, false},
{mmGB_MACROTILE_MODE1, false},
{mmGB_MACROTILE_MODE2, false},
{mmGB_MACROTILE_MODE3, false},
{mmGB_MACROTILE_MODE4, false},
{mmGB_MACROTILE_MODE5, false},
{mmGB_MACROTILE_MODE6, false},
{mmGB_MACROTILE_MODE7, false},
{mmGB_MACROTILE_MODE8, false},
{mmGB_MACROTILE_MODE9, false},
{mmGB_MACROTILE_MODE10, false},
{mmGB_MACROTILE_MODE11, false},
{mmGB_MACROTILE_MODE12, false},
{mmGB_MACROTILE_MODE13, false},
{mmGB_MACROTILE_MODE14, false},
{mmGB_MACROTILE_MODE15, false},
{mmCC_RB_BACKEND_DISABLE, false, true},
{mmGC_USER_RB_BACKEND_DISABLE, false, true},
{mmGB_BACKEND_MAP, false, false},
{mmPA_SC_RASTER_CONFIG, false, true},
{mmPA_SC_RASTER_CONFIG_1, false, true},
};
static uint32_t vi_read_indexed_register(struct amdgpu_device *adev, u32 se_num,
u32 sh_num, u32 reg_offset)
{
uint32_t val;
mutex_lock(&adev->grbm_idx_mutex);
if (se_num != 0xffffffff || sh_num != 0xffffffff)
gfx_v8_0_select_se_sh(adev, se_num, sh_num);
val = RREG32(reg_offset);
if (se_num != 0xffffffff || sh_num != 0xffffffff)
gfx_v8_0_select_se_sh(adev, 0xffffffff, 0xffffffff);
mutex_unlock(&adev->grbm_idx_mutex);
return val;
}
static int vi_read_register(struct amdgpu_device *adev, u32 se_num,
u32 sh_num, u32 reg_offset, u32 *value)
{
struct amdgpu_allowed_register_entry *asic_register_table = NULL;
struct amdgpu_allowed_register_entry *asic_register_entry;
uint32_t size, i;
*value = 0;
switch (adev->asic_type) {
case CHIP_TOPAZ:
asic_register_table = tonga_allowed_read_registers;
size = ARRAY_SIZE(tonga_allowed_read_registers);
break;
case CHIP_TONGA:
case CHIP_CARRIZO:
asic_register_table = cz_allowed_read_registers;
size = ARRAY_SIZE(cz_allowed_read_registers);
break;
default:
return -EINVAL;
}
if (asic_register_table) {
for (i = 0; i < size; i++) {
asic_register_entry = asic_register_table + i;
if (reg_offset != asic_register_entry->reg_offset)
continue;
if (!asic_register_entry->untouched)
*value = asic_register_entry->grbm_indexed ?
vi_read_indexed_register(adev, se_num,
sh_num, reg_offset) :
RREG32(reg_offset);
return 0;
}
}
for (i = 0; i < ARRAY_SIZE(vi_allowed_read_registers); i++) {
if (reg_offset != vi_allowed_read_registers[i].reg_offset)
continue;
if (!vi_allowed_read_registers[i].untouched)
*value = vi_allowed_read_registers[i].grbm_indexed ?
vi_read_indexed_register(adev, se_num,
sh_num, reg_offset) :
RREG32(reg_offset);
return 0;
}
return -EINVAL;
}
static void vi_print_gpu_status_regs(struct amdgpu_device *adev)
{
dev_info(adev->dev, " GRBM_STATUS=0x%08X\n",
RREG32(mmGRBM_STATUS));
dev_info(adev->dev, " GRBM_STATUS2=0x%08X\n",
RREG32(mmGRBM_STATUS2));
dev_info(adev->dev, " GRBM_STATUS_SE0=0x%08X\n",
RREG32(mmGRBM_STATUS_SE0));
dev_info(adev->dev, " GRBM_STATUS_SE1=0x%08X\n",
RREG32(mmGRBM_STATUS_SE1));
dev_info(adev->dev, " GRBM_STATUS_SE2=0x%08X\n",
RREG32(mmGRBM_STATUS_SE2));
dev_info(adev->dev, " GRBM_STATUS_SE3=0x%08X\n",
RREG32(mmGRBM_STATUS_SE3));
dev_info(adev->dev, " SRBM_STATUS=0x%08X\n",
RREG32(mmSRBM_STATUS));
dev_info(adev->dev, " SRBM_STATUS2=0x%08X\n",
RREG32(mmSRBM_STATUS2));
dev_info(adev->dev, " SDMA0_STATUS_REG = 0x%08X\n",
RREG32(mmSDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET));
dev_info(adev->dev, " SDMA1_STATUS_REG = 0x%08X\n",
RREG32(mmSDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET));
dev_info(adev->dev, " CP_STAT = 0x%08x\n", RREG32(mmCP_STAT));
dev_info(adev->dev, " CP_STALLED_STAT1 = 0x%08x\n",
RREG32(mmCP_STALLED_STAT1));
dev_info(adev->dev, " CP_STALLED_STAT2 = 0x%08x\n",
RREG32(mmCP_STALLED_STAT2));
dev_info(adev->dev, " CP_STALLED_STAT3 = 0x%08x\n",
RREG32(mmCP_STALLED_STAT3));
dev_info(adev->dev, " CP_CPF_BUSY_STAT = 0x%08x\n",
RREG32(mmCP_CPF_BUSY_STAT));
dev_info(adev->dev, " CP_CPF_STALLED_STAT1 = 0x%08x\n",
RREG32(mmCP_CPF_STALLED_STAT1));
dev_info(adev->dev, " CP_CPF_STATUS = 0x%08x\n", RREG32(mmCP_CPF_STATUS));
dev_info(adev->dev, " CP_CPC_BUSY_STAT = 0x%08x\n", RREG32(mmCP_CPC_BUSY_STAT));
dev_info(adev->dev, " CP_CPC_STALLED_STAT1 = 0x%08x\n",
RREG32(mmCP_CPC_STALLED_STAT1));
dev_info(adev->dev, " CP_CPC_STATUS = 0x%08x\n", RREG32(mmCP_CPC_STATUS));
}
/**
* vi_gpu_check_soft_reset - check which blocks are busy
*
* @adev: amdgpu_device pointer
*
* Check which blocks are busy and return the relevant reset
* mask to be used by vi_gpu_soft_reset().
* Returns a mask of the blocks to be reset.
*/
u32 vi_gpu_check_soft_reset(struct amdgpu_device *adev)
{
u32 reset_mask = 0;
u32 tmp;
/* GRBM_STATUS */
tmp = RREG32(mmGRBM_STATUS);
if (tmp & (GRBM_STATUS__PA_BUSY_MASK | GRBM_STATUS__SC_BUSY_MASK |
GRBM_STATUS__BCI_BUSY_MASK | GRBM_STATUS__SX_BUSY_MASK |
GRBM_STATUS__TA_BUSY_MASK | GRBM_STATUS__VGT_BUSY_MASK |
GRBM_STATUS__DB_BUSY_MASK | GRBM_STATUS__CB_BUSY_MASK |
GRBM_STATUS__GDS_BUSY_MASK | GRBM_STATUS__SPI_BUSY_MASK |
GRBM_STATUS__IA_BUSY_MASK | GRBM_STATUS__IA_BUSY_NO_DMA_MASK))
reset_mask |= AMDGPU_RESET_GFX;
if (tmp & (GRBM_STATUS__CP_BUSY_MASK | GRBM_STATUS__CP_COHERENCY_BUSY_MASK))
reset_mask |= AMDGPU_RESET_CP;
/* GRBM_STATUS2 */
tmp = RREG32(mmGRBM_STATUS2);
if (tmp & GRBM_STATUS2__RLC_BUSY_MASK)
reset_mask |= AMDGPU_RESET_RLC;
if (tmp & (GRBM_STATUS2__CPF_BUSY_MASK |
GRBM_STATUS2__CPC_BUSY_MASK |
GRBM_STATUS2__CPG_BUSY_MASK))
reset_mask |= AMDGPU_RESET_CP;
/* SRBM_STATUS2 */
tmp = RREG32(mmSRBM_STATUS2);
if (tmp & SRBM_STATUS2__SDMA_BUSY_MASK)
reset_mask |= AMDGPU_RESET_DMA;
if (tmp & SRBM_STATUS2__SDMA1_BUSY_MASK)
reset_mask |= AMDGPU_RESET_DMA1;
/* SRBM_STATUS */
tmp = RREG32(mmSRBM_STATUS);
if (tmp & SRBM_STATUS__IH_BUSY_MASK)
reset_mask |= AMDGPU_RESET_IH;
if (tmp & SRBM_STATUS__SEM_BUSY_MASK)
reset_mask |= AMDGPU_RESET_SEM;
if (tmp & SRBM_STATUS__GRBM_RQ_PENDING_MASK)
reset_mask |= AMDGPU_RESET_GRBM;
if (adev->asic_type != CHIP_TOPAZ) {
if (tmp & (SRBM_STATUS__UVD_RQ_PENDING_MASK |
SRBM_STATUS__UVD_BUSY_MASK))
reset_mask |= AMDGPU_RESET_UVD;
}
if (tmp & SRBM_STATUS__VMC_BUSY_MASK)
reset_mask |= AMDGPU_RESET_VMC;
if (tmp & (SRBM_STATUS__MCB_BUSY_MASK | SRBM_STATUS__MCB_NON_DISPLAY_BUSY_MASK |
SRBM_STATUS__MCC_BUSY_MASK | SRBM_STATUS__MCD_BUSY_MASK))
reset_mask |= AMDGPU_RESET_MC;
/* SDMA0_STATUS_REG */
tmp = RREG32(mmSDMA0_STATUS_REG + SDMA0_REGISTER_OFFSET);
if (!(tmp & SDMA0_STATUS_REG__IDLE_MASK))
reset_mask |= AMDGPU_RESET_DMA;
/* SDMA1_STATUS_REG */
tmp = RREG32(mmSDMA0_STATUS_REG + SDMA1_REGISTER_OFFSET);
if (!(tmp & SDMA0_STATUS_REG__IDLE_MASK))
reset_mask |= AMDGPU_RESET_DMA1;
#if 0
/* VCE_STATUS */
if (adev->asic_type != CHIP_TOPAZ) {
tmp = RREG32(mmVCE_STATUS);
if (tmp & VCE_STATUS__VCPU_REPORT_RB0_BUSY_MASK)
reset_mask |= AMDGPU_RESET_VCE;
if (tmp & VCE_STATUS__VCPU_REPORT_RB1_BUSY_MASK)
reset_mask |= AMDGPU_RESET_VCE1;
}
if (adev->asic_type != CHIP_TOPAZ) {
if (amdgpu_display_is_display_hung(adev))
reset_mask |= AMDGPU_RESET_DISPLAY;
}
#endif
/* Skip MC reset as it's mostly likely not hung, just busy */
if (reset_mask & AMDGPU_RESET_MC) {
DRM_DEBUG("MC busy: 0x%08X, clearing.\n", reset_mask);
reset_mask &= ~AMDGPU_RESET_MC;
}
return reset_mask;
}
/**
* vi_gpu_soft_reset - soft reset GPU
*
* @adev: amdgpu_device pointer
* @reset_mask: mask of which blocks to reset
*
* Soft reset the blocks specified in @reset_mask.
*/
static void vi_gpu_soft_reset(struct amdgpu_device *adev, u32 reset_mask)
{
struct amdgpu_mode_mc_save save;
u32 grbm_soft_reset = 0, srbm_soft_reset = 0;
u32 tmp;
if (reset_mask == 0)
return;
dev_info(adev->dev, "GPU softreset: 0x%08X\n", reset_mask);
vi_print_gpu_status_regs(adev);
dev_info(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_ADDR 0x%08X\n",
RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_ADDR));
dev_info(adev->dev, " VM_CONTEXT1_PROTECTION_FAULT_STATUS 0x%08X\n",
RREG32(mmVM_CONTEXT1_PROTECTION_FAULT_STATUS));
/* disable CG/PG */
/* stop the rlc */
//XXX
//gfx_v8_0_rlc_stop(adev);
/* Disable GFX parsing/prefetching */
tmp = RREG32(mmCP_ME_CNTL);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, ME_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, PFP_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, CE_HALT, 1);
WREG32(mmCP_ME_CNTL, tmp);
/* Disable MEC parsing/prefetching */
tmp = RREG32(mmCP_MEC_CNTL);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME1_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME2_HALT, 1);
WREG32(mmCP_MEC_CNTL, tmp);
if (reset_mask & AMDGPU_RESET_DMA) {
/* sdma0 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
}
if (reset_mask & AMDGPU_RESET_DMA1) {
/* sdma1 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET, tmp);
}
gmc_v8_0_mc_stop(adev, &save);
if (amdgpu_asic_wait_for_mc_idle(adev)) {
dev_warn(adev->dev, "Wait for MC idle timedout !\n");
}
if (reset_mask & (AMDGPU_RESET_GFX | AMDGPU_RESET_COMPUTE | AMDGPU_RESET_CP)) {
grbm_soft_reset =
REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP, 1);
grbm_soft_reset =
REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_GFX, 1);
}
if (reset_mask & AMDGPU_RESET_CP) {
grbm_soft_reset =
REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_CP, 1);
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
}
if (reset_mask & AMDGPU_RESET_DMA)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA, 1);
if (reset_mask & AMDGPU_RESET_DMA1)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SDMA1, 1);
if (reset_mask & AMDGPU_RESET_DISPLAY)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_DC, 1);
if (reset_mask & AMDGPU_RESET_RLC)
grbm_soft_reset =
REG_SET_FIELD(grbm_soft_reset, GRBM_SOFT_RESET, SOFT_RESET_RLC, 1);
if (reset_mask & AMDGPU_RESET_SEM)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_SEM, 1);
if (reset_mask & AMDGPU_RESET_IH)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_IH, 1);
if (reset_mask & AMDGPU_RESET_GRBM)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_GRBM, 1);
if (reset_mask & AMDGPU_RESET_VMC)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VMC, 1);
if (reset_mask & AMDGPU_RESET_UVD)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_UVD, 1);
if (reset_mask & AMDGPU_RESET_VCE)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE0, 1);
if (reset_mask & AMDGPU_RESET_VCE)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_VCE1, 1);
if (!(adev->flags & AMDGPU_IS_APU)) {
if (reset_mask & AMDGPU_RESET_MC)
srbm_soft_reset =
REG_SET_FIELD(srbm_soft_reset, SRBM_SOFT_RESET, SOFT_RESET_MC, 1);
}
if (grbm_soft_reset) {
tmp = RREG32(mmGRBM_SOFT_RESET);
tmp |= grbm_soft_reset;
dev_info(adev->dev, "GRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
udelay(50);
tmp &= ~grbm_soft_reset;
WREG32(mmGRBM_SOFT_RESET, tmp);
tmp = RREG32(mmGRBM_SOFT_RESET);
}
if (srbm_soft_reset) {
tmp = RREG32(mmSRBM_SOFT_RESET);
tmp |= srbm_soft_reset;
dev_info(adev->dev, "SRBM_SOFT_RESET=0x%08X\n", tmp);
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
udelay(50);
tmp &= ~srbm_soft_reset;
WREG32(mmSRBM_SOFT_RESET, tmp);
tmp = RREG32(mmSRBM_SOFT_RESET);
}
/* Wait a little for things to settle down */
udelay(50);
gmc_v8_0_mc_resume(adev, &save);
udelay(50);
vi_print_gpu_status_regs(adev);
}
static void vi_gpu_pci_config_reset(struct amdgpu_device *adev)
{
struct amdgpu_mode_mc_save save;
u32 tmp, i;
dev_info(adev->dev, "GPU pci config reset\n");
/* disable dpm? */
/* disable cg/pg */
/* Disable GFX parsing/prefetching */
tmp = RREG32(mmCP_ME_CNTL);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, ME_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, PFP_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_ME_CNTL, CE_HALT, 1);
WREG32(mmCP_ME_CNTL, tmp);
/* Disable MEC parsing/prefetching */
tmp = RREG32(mmCP_MEC_CNTL);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME1_HALT, 1);
tmp = REG_SET_FIELD(tmp, CP_MEC_CNTL, MEC_ME2_HALT, 1);
WREG32(mmCP_MEC_CNTL, tmp);
/* Disable GFX parsing/prefetching */
WREG32(mmCP_ME_CNTL, CP_ME_CNTL__ME_HALT_MASK |
CP_ME_CNTL__PFP_HALT_MASK | CP_ME_CNTL__CE_HALT_MASK);
/* Disable MEC parsing/prefetching */
WREG32(mmCP_MEC_CNTL,
CP_MEC_CNTL__MEC_ME1_HALT_MASK | CP_MEC_CNTL__MEC_ME2_HALT_MASK);
/* sdma0 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + SDMA0_REGISTER_OFFSET, tmp);
/* sdma1 */
tmp = RREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET);
tmp = REG_SET_FIELD(tmp, SDMA0_F32_CNTL, HALT, 1);
WREG32(mmSDMA0_F32_CNTL + SDMA1_REGISTER_OFFSET, tmp);
/* XXX other engines? */
/* halt the rlc, disable cp internal ints */
//XXX
//gfx_v8_0_rlc_stop(adev);
udelay(50);
/* disable mem access */
gmc_v8_0_mc_stop(adev, &save);
if (amdgpu_asic_wait_for_mc_idle(adev)) {
dev_warn(adev->dev, "Wait for MC idle timed out !\n");
}
/* disable BM */
pci_clear_master(adev->pdev);
/* reset */
amdgpu_pci_config_reset(adev);
udelay(100);
/* wait for asic to come out of reset */
for (i = 0; i < adev->usec_timeout; i++) {
if (RREG32(mmCONFIG_MEMSIZE) != 0xffffffff)
break;
udelay(1);
}
}
static void vi_set_bios_scratch_engine_hung(struct amdgpu_device *adev, bool hung)
{
u32 tmp = RREG32(mmBIOS_SCRATCH_3);
if (hung)
tmp |= ATOM_S3_ASIC_GUI_ENGINE_HUNG;
else
tmp &= ~ATOM_S3_ASIC_GUI_ENGINE_HUNG;
WREG32(mmBIOS_SCRATCH_3, tmp);
}
/**
* vi_asic_reset - soft reset GPU
*
* @adev: amdgpu_device pointer
*
* Look up which blocks are hung and attempt
* to reset them.
* Returns 0 for success.
*/
static int vi_asic_reset(struct amdgpu_device *adev)
{
u32 reset_mask;
reset_mask = vi_gpu_check_soft_reset(adev);
if (reset_mask)
vi_set_bios_scratch_engine_hung(adev, true);
/* try soft reset */
vi_gpu_soft_reset(adev, reset_mask);
reset_mask = vi_gpu_check_soft_reset(adev);
/* try pci config reset */
if (reset_mask && amdgpu_hard_reset)
vi_gpu_pci_config_reset(adev);
reset_mask = vi_gpu_check_soft_reset(adev);
if (!reset_mask)
vi_set_bios_scratch_engine_hung(adev, false);
return 0;
}
static int vi_set_uvd_clock(struct amdgpu_device *adev, u32 clock,
u32 cntl_reg, u32 status_reg)
{
int r, i;
struct atom_clock_dividers dividers;
uint32_t tmp;
r = amdgpu_atombios_get_clock_dividers(adev,
COMPUTE_GPUCLK_INPUT_FLAG_DEFAULT_GPUCLK,
clock, false, &dividers);
if (r)
return r;
tmp = RREG32_SMC(cntl_reg);
tmp &= ~(CG_DCLK_CNTL__DCLK_DIR_CNTL_EN_MASK |
CG_DCLK_CNTL__DCLK_DIVIDER_MASK);
tmp |= dividers.post_divider;
WREG32_SMC(cntl_reg, tmp);
for (i = 0; i < 100; i++) {
if (RREG32_SMC(status_reg) & CG_DCLK_STATUS__DCLK_STATUS_MASK)
break;
mdelay(10);
}
if (i == 100)
return -ETIMEDOUT;
return 0;
}
static int vi_set_uvd_clocks(struct amdgpu_device *adev, u32 vclk, u32 dclk)
{
int r;
r = vi_set_uvd_clock(adev, vclk, ixCG_VCLK_CNTL, ixCG_VCLK_STATUS);
if (r)
return r;
r = vi_set_uvd_clock(adev, dclk, ixCG_DCLK_CNTL, ixCG_DCLK_STATUS);
return 0;
}
static int vi_set_vce_clocks(struct amdgpu_device *adev, u32 evclk, u32 ecclk)
{
/* todo */
return 0;
}
static void vi_pcie_gen3_enable(struct amdgpu_device *adev)
{
u32 mask;
int ret;
if (amdgpu_pcie_gen2 == 0)
return;
if (adev->flags & AMDGPU_IS_APU)
return;
ret = drm_pcie_get_speed_cap_mask(adev->ddev, &mask);
if (ret != 0)
return;
if (!(mask & (DRM_PCIE_SPEED_50 | DRM_PCIE_SPEED_80)))
return;
/* todo */
}
static void vi_program_aspm(struct amdgpu_device *adev)
{
if (amdgpu_aspm == 0)
return;
/* todo */
}
static void vi_enable_doorbell_aperture(struct amdgpu_device *adev,
bool enable)
{
u32 tmp;
/* not necessary on CZ */
if (adev->flags & AMDGPU_IS_APU)
return;
tmp = RREG32(mmBIF_DOORBELL_APER_EN);
if (enable)
tmp = REG_SET_FIELD(tmp, BIF_DOORBELL_APER_EN, BIF_DOORBELL_APER_EN, 1);
else
tmp = REG_SET_FIELD(tmp, BIF_DOORBELL_APER_EN, BIF_DOORBELL_APER_EN, 0);
WREG32(mmBIF_DOORBELL_APER_EN, tmp);
}
/* topaz has no DCE, UVD, VCE */
static const struct amdgpu_ip_block_version topaz_ip_blocks[] =
{
/* ORDER MATTERS! */
{
.type = AMDGPU_IP_BLOCK_TYPE_COMMON,
.major = 2,
.minor = 0,
.rev = 0,
.funcs = &vi_common_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GMC,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gmc_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_IH,
.major = 2,
.minor = 4,
.rev = 0,
.funcs = &iceland_ih_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SMC,
.major = 7,
.minor = 1,
.rev = 0,
.funcs = &iceland_dpm_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GFX,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gfx_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SDMA,
.major = 2,
.minor = 4,
.rev = 0,
.funcs = &sdma_v2_4_ip_funcs,
},
};
static const struct amdgpu_ip_block_version tonga_ip_blocks[] =
{
/* ORDER MATTERS! */
{
.type = AMDGPU_IP_BLOCK_TYPE_COMMON,
.major = 2,
.minor = 0,
.rev = 0,
.funcs = &vi_common_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GMC,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gmc_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_IH,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &tonga_ih_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SMC,
.major = 7,
.minor = 1,
.rev = 0,
.funcs = &tonga_dpm_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_DCE,
.major = 10,
.minor = 0,
.rev = 0,
.funcs = &dce_v10_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GFX,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gfx_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SDMA,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &sdma_v3_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_UVD,
.major = 5,
.minor = 0,
.rev = 0,
.funcs = &uvd_v5_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_VCE,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &vce_v3_0_ip_funcs,
},
};
static const struct amdgpu_ip_block_version cz_ip_blocks[] =
{
/* ORDER MATTERS! */
{
.type = AMDGPU_IP_BLOCK_TYPE_COMMON,
.major = 2,
.minor = 0,
.rev = 0,
.funcs = &vi_common_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GMC,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gmc_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_IH,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &cz_ih_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SMC,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &cz_dpm_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_DCE,
.major = 11,
.minor = 0,
.rev = 0,
.funcs = &dce_v11_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_GFX,
.major = 8,
.minor = 0,
.rev = 0,
.funcs = &gfx_v8_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_SDMA,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &sdma_v3_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_UVD,
.major = 6,
.minor = 0,
.rev = 0,
.funcs = &uvd_v6_0_ip_funcs,
},
{
.type = AMDGPU_IP_BLOCK_TYPE_VCE,
.major = 3,
.minor = 0,
.rev = 0,
.funcs = &vce_v3_0_ip_funcs,
},
};
int vi_set_ip_blocks(struct amdgpu_device *adev)
{
switch (adev->asic_type) {
case CHIP_TOPAZ:
adev->ip_blocks = topaz_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(topaz_ip_blocks);
break;
case CHIP_TONGA:
adev->ip_blocks = tonga_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(tonga_ip_blocks);
break;
case CHIP_CARRIZO:
adev->ip_blocks = cz_ip_blocks;
adev->num_ip_blocks = ARRAY_SIZE(cz_ip_blocks);
break;
default:
/* FIXME: not supported yet */
return -EINVAL;
}
adev->ip_block_enabled = kcalloc(adev->num_ip_blocks, sizeof(bool), GFP_KERNEL);
if (adev->ip_block_enabled == NULL)
return -ENOMEM;
return 0;
}
static uint32_t vi_get_rev_id(struct amdgpu_device *adev)
{
if (adev->asic_type == CHIP_TOPAZ)
return (RREG32(mmPCIE_EFUSE4) & PCIE_EFUSE4__STRAP_BIF_ATI_REV_ID_MASK)
>> PCIE_EFUSE4__STRAP_BIF_ATI_REV_ID__SHIFT;
else
return (RREG32(mmCC_DRM_ID_STRAPS) & CC_DRM_ID_STRAPS__ATI_REV_ID_MASK)
>> CC_DRM_ID_STRAPS__ATI_REV_ID__SHIFT;
}
static const struct amdgpu_asic_funcs vi_asic_funcs =
{
.read_disabled_bios = &vi_read_disabled_bios,
.read_register = &vi_read_register,
.reset = &vi_asic_reset,
.set_vga_state = &vi_vga_set_state,
.get_xclk = &vi_get_xclk,
.set_uvd_clocks = &vi_set_uvd_clocks,
.set_vce_clocks = &vi_set_vce_clocks,
.get_cu_info = &gfx_v8_0_get_cu_info,
/* these should be moved to their own ip modules */
.get_gpu_clock_counter = &gfx_v8_0_get_gpu_clock_counter,
.wait_for_mc_idle = &gmc_v8_0_mc_wait_for_idle,
};
static int vi_common_early_init(struct amdgpu_device *adev)
{
bool smc_enabled = false;
adev->smc_rreg = &vi_smc_rreg;
adev->smc_wreg = &vi_smc_wreg;
adev->pcie_rreg = &vi_pcie_rreg;
adev->pcie_wreg = &vi_pcie_wreg;
adev->uvd_ctx_rreg = &vi_uvd_ctx_rreg;
adev->uvd_ctx_wreg = &vi_uvd_ctx_wreg;
adev->didt_rreg = &vi_didt_rreg;
adev->didt_wreg = &vi_didt_wreg;
adev->asic_funcs = &vi_asic_funcs;
if (amdgpu_get_ip_block(adev, AMDGPU_IP_BLOCK_TYPE_SMC) &&
(amdgpu_ip_block_mask & (1 << AMDGPU_IP_BLOCK_TYPE_SMC)))
smc_enabled = true;
adev->rev_id = vi_get_rev_id(adev);
adev->external_rev_id = 0xFF;
switch (adev->asic_type) {
case CHIP_TOPAZ:
adev->has_uvd = false;
adev->cg_flags = 0;
adev->pg_flags = 0;
adev->external_rev_id = 0x1;
if (amdgpu_smc_load_fw && smc_enabled)
adev->firmware.smu_load = true;
break;
case CHIP_TONGA:
adev->has_uvd = true;
adev->cg_flags = 0;
adev->pg_flags = 0;
adev->external_rev_id = adev->rev_id + 0x14;
if (amdgpu_smc_load_fw && smc_enabled)
adev->firmware.smu_load = true;
break;
case CHIP_CARRIZO:
adev->has_uvd = true;
adev->cg_flags = 0;
adev->pg_flags = 0;
adev->external_rev_id = adev->rev_id + 0x1;
if (amdgpu_smc_load_fw && smc_enabled)
adev->firmware.smu_load = true;
break;
default:
/* FIXME: not supported yet */
return -EINVAL;
}
return 0;
}
static int vi_common_sw_init(struct amdgpu_device *adev)
{
return 0;
}
static int vi_common_sw_fini(struct amdgpu_device *adev)
{
return 0;
}
static int vi_common_hw_init(struct amdgpu_device *adev)
{
/* move the golden regs per IP block */
vi_init_golden_registers(adev);
/* enable pcie gen2/3 link */
vi_pcie_gen3_enable(adev);
/* enable aspm */
vi_program_aspm(adev);
/* enable the doorbell aperture */
vi_enable_doorbell_aperture(adev, true);
return 0;
}
static int vi_common_hw_fini(struct amdgpu_device *adev)
{
/* enable the doorbell aperture */
vi_enable_doorbell_aperture(adev, false);
return 0;
}
static int vi_common_suspend(struct amdgpu_device *adev)
{
return vi_common_hw_fini(adev);
}
static int vi_common_resume(struct amdgpu_device *adev)
{
return vi_common_hw_init(adev);
}
static bool vi_common_is_idle(struct amdgpu_device *adev)
{
return true;
}
static int vi_common_wait_for_idle(struct amdgpu_device *adev)
{
return 0;
}
static void vi_common_print_status(struct amdgpu_device *adev)
{
}
static int vi_common_soft_reset(struct amdgpu_device *adev)
{
/* XXX hard reset?? */
return 0;
}
static int vi_common_set_clockgating_state(struct amdgpu_device *adev,
enum amdgpu_clockgating_state state)
{
return 0;
}
static int vi_common_set_powergating_state(struct amdgpu_device *adev,
enum amdgpu_powergating_state state)
{
return 0;
}
const struct amdgpu_ip_funcs vi_common_ip_funcs = {
.early_init = vi_common_early_init,
.late_init = NULL,
.sw_init = vi_common_sw_init,
.sw_fini = vi_common_sw_fini,
.hw_init = vi_common_hw_init,
.hw_fini = vi_common_hw_fini,
.suspend = vi_common_suspend,
.resume = vi_common_resume,
.is_idle = vi_common_is_idle,
.wait_for_idle = vi_common_wait_for_idle,
.soft_reset = vi_common_soft_reset,
.print_status = vi_common_print_status,
.set_clockgating_state = vi_common_set_clockgating_state,
.set_powergating_state = vi_common_set_powergating_state,
};
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __VI_H__
#define __VI_H__
extern const struct amdgpu_ip_funcs vi_common_ip_funcs;
void vi_srbm_select(struct amdgpu_device *adev,
u32 me, u32 pipe, u32 queue, u32 vmid);
int vi_set_ip_blocks(struct amdgpu_device *adev);
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef __VI_DPM_H__
#define __VI_DPM_H__
extern const struct amdgpu_ip_funcs cz_dpm_ip_funcs;
int cz_smu_init(struct amdgpu_device *adev);
int cz_smu_start(struct amdgpu_device *adev);
int cz_smu_fini(struct amdgpu_device *adev);
extern const struct amdgpu_ip_funcs tonga_dpm_ip_funcs;
extern const struct amdgpu_ip_funcs iceland_dpm_ip_funcs;
#endif
/*
* Copyright 2014 Advanced Micro Devices, Inc.
*
* 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.
*
*/
#ifndef VI_H
#define VI_H
#define SDMA0_REGISTER_OFFSET 0x0 /* not a register */
#define SDMA1_REGISTER_OFFSET 0x200 /* not a register */
#define SDMA_MAX_INSTANCE 2
/* crtc instance offsets */
#define CRTC0_REGISTER_OFFSET (0x1b9c - 0x1b9c)
#define CRTC1_REGISTER_OFFSET (0x1d9c - 0x1b9c)
#define CRTC2_REGISTER_OFFSET (0x1f9c - 0x1b9c)
#define CRTC3_REGISTER_OFFSET (0x419c - 0x1b9c)
#define CRTC4_REGISTER_OFFSET (0x439c - 0x1b9c)
#define CRTC5_REGISTER_OFFSET (0x459c - 0x1b9c)
#define CRTC6_REGISTER_OFFSET (0x479c - 0x1b9c)
/* dig instance offsets */
#define DIG0_REGISTER_OFFSET (0x4a00 - 0x4a00)
#define DIG1_REGISTER_OFFSET (0x4b00 - 0x4a00)
#define DIG2_REGISTER_OFFSET (0x4c00 - 0x4a00)
#define DIG3_REGISTER_OFFSET (0x4d00 - 0x4a00)
#define DIG4_REGISTER_OFFSET (0x4e00 - 0x4a00)
#define DIG5_REGISTER_OFFSET (0x4f00 - 0x4a00)
#define DIG6_REGISTER_OFFSET (0x5400 - 0x4a00)
#define DIG7_REGISTER_OFFSET (0x5600 - 0x4a00)
#define DIG8_REGISTER_OFFSET (0x5700 - 0x4a00)
/* audio endpt instance offsets */
#define AUD0_REGISTER_OFFSET (0x17a8 - 0x17a8)
#define AUD1_REGISTER_OFFSET (0x17ac - 0x17a8)
#define AUD2_REGISTER_OFFSET (0x17b0 - 0x17a8)
#define AUD3_REGISTER_OFFSET (0x17b4 - 0x17a8)
#define AUD4_REGISTER_OFFSET (0x17b8 - 0x17a8)
#define AUD5_REGISTER_OFFSET (0x17bc - 0x17a8)
#define AUD6_REGISTER_OFFSET (0x17c4 - 0x17a8)
/* hpd instance offsets */
#define HPD0_REGISTER_OFFSET (0x1898 - 0x1898)
#define HPD1_REGISTER_OFFSET (0x18a0 - 0x1898)
#define HPD2_REGISTER_OFFSET (0x18a8 - 0x1898)
#define HPD3_REGISTER_OFFSET (0x18b0 - 0x1898)
#define HPD4_REGISTER_OFFSET (0x18b8 - 0x1898)
#define HPD5_REGISTER_OFFSET (0x18c0 - 0x1898)
#define AMDGPU_NUM_OF_VMIDS 8
#define RB_BITMAP_WIDTH_PER_SH 2
#define MC_SEQ_MISC0__GDDR5__SHIFT 0x1c
#define MC_SEQ_MISC0__GDDR5_MASK 0xf0000000
#define MC_SEQ_MISC0__GDDR5_VALUE 5
/*
* PM4
*/
#define PACKET_TYPE0 0
#define PACKET_TYPE1 1
#define PACKET_TYPE2 2
#define PACKET_TYPE3 3
#define CP_PACKET_GET_TYPE(h) (((h) >> 30) & 3)
#define CP_PACKET_GET_COUNT(h) (((h) >> 16) & 0x3FFF)
#define CP_PACKET0_GET_REG(h) ((h) & 0xFFFF)
#define CP_PACKET3_GET_OPCODE(h) (((h) >> 8) & 0xFF)
#define PACKET0(reg, n) ((PACKET_TYPE0 << 30) | \
((reg) & 0xFFFF) | \
((n) & 0x3FFF) << 16)
#define CP_PACKET2 0x80000000
#define PACKET2_PAD_SHIFT 0
#define PACKET2_PAD_MASK (0x3fffffff << 0)
#define PACKET2(v) (CP_PACKET2 | REG_SET(PACKET2_PAD, (v)))
#define PACKET3(op, n) ((PACKET_TYPE3 << 30) | \
(((op) & 0xFF) << 8) | \
((n) & 0x3FFF) << 16)
#define PACKET3_COMPUTE(op, n) (PACKET3(op, n) | 1 << 1)
/* Packet 3 types */
#define PACKET3_NOP 0x10
#define PACKET3_SET_BASE 0x11
#define PACKET3_BASE_INDEX(x) ((x) << 0)
#define CE_PARTITION_BASE 3
#define PACKET3_CLEAR_STATE 0x12
#define PACKET3_INDEX_BUFFER_SIZE 0x13
#define PACKET3_DISPATCH_DIRECT 0x15
#define PACKET3_DISPATCH_INDIRECT 0x16
#define PACKET3_ATOMIC_GDS 0x1D
#define PACKET3_ATOMIC_MEM 0x1E
#define PACKET3_OCCLUSION_QUERY 0x1F
#define PACKET3_SET_PREDICATION 0x20
#define PACKET3_REG_RMW 0x21
#define PACKET3_COND_EXEC 0x22
#define PACKET3_PRED_EXEC 0x23
#define PACKET3_DRAW_INDIRECT 0x24
#define PACKET3_DRAW_INDEX_INDIRECT 0x25
#define PACKET3_INDEX_BASE 0x26
#define PACKET3_DRAW_INDEX_2 0x27
#define PACKET3_CONTEXT_CONTROL 0x28
#define PACKET3_INDEX_TYPE 0x2A
#define PACKET3_DRAW_INDIRECT_MULTI 0x2C
#define PACKET3_DRAW_INDEX_AUTO 0x2D
#define PACKET3_NUM_INSTANCES 0x2F
#define PACKET3_DRAW_INDEX_MULTI_AUTO 0x30
#define PACKET3_INDIRECT_BUFFER_CONST 0x33
#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34
#define PACKET3_DRAW_INDEX_OFFSET_2 0x35
#define PACKET3_DRAW_PREAMBLE 0x36
#define PACKET3_WRITE_DATA 0x37
#define WRITE_DATA_DST_SEL(x) ((x) << 8)
/* 0 - register
* 1 - memory (sync - via GRBM)
* 2 - gl2
* 3 - gds
* 4 - reserved
* 5 - memory (async - direct)
*/
#define WR_ONE_ADDR (1 << 16)
#define WR_CONFIRM (1 << 20)
#define WRITE_DATA_CACHE_POLICY(x) ((x) << 25)
/* 0 - LRU
* 1 - Stream
*/
#define WRITE_DATA_ENGINE_SEL(x) ((x) << 30)
/* 0 - me
* 1 - pfp
* 2 - ce
*/
#define PACKET3_DRAW_INDEX_INDIRECT_MULTI 0x38
#define PACKET3_MEM_SEMAPHORE 0x39
# define PACKET3_SEM_USE_MAILBOX (0x1 << 16)
# define PACKET3_SEM_SEL_SIGNAL_TYPE (0x1 << 20) /* 0 = increment, 1 = write 1 */
# define PACKET3_SEM_CLIENT_CODE ((x) << 24) /* 0 = CP, 1 = CB, 2 = DB */
# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29)
# define PACKET3_SEM_SEL_WAIT (0x7 << 29)
#define PACKET3_WAIT_REG_MEM 0x3C
#define WAIT_REG_MEM_FUNCTION(x) ((x) << 0)
/* 0 - always
* 1 - <
* 2 - <=
* 3 - ==
* 4 - !=
* 5 - >=
* 6 - >
*/
#define WAIT_REG_MEM_MEM_SPACE(x) ((x) << 4)
/* 0 - reg
* 1 - mem
*/
#define WAIT_REG_MEM_OPERATION(x) ((x) << 6)
/* 0 - wait_reg_mem
* 1 - wr_wait_wr_reg
*/
#define WAIT_REG_MEM_ENGINE(x) ((x) << 8)
/* 0 - me
* 1 - pfp
*/
#define PACKET3_INDIRECT_BUFFER 0x3F
#define INDIRECT_BUFFER_TCL2_VOLATILE (1 << 22)
#define INDIRECT_BUFFER_VALID (1 << 23)
#define INDIRECT_BUFFER_CACHE_POLICY(x) ((x) << 28)
/* 0 - LRU
* 1 - Stream
* 2 - Bypass
*/
#define PACKET3_COPY_DATA 0x40
#define PACKET3_PFP_SYNC_ME 0x42
#define PACKET3_SURFACE_SYNC 0x43
# define PACKET3_DEST_BASE_0_ENA (1 << 0)
# define PACKET3_DEST_BASE_1_ENA (1 << 1)
# define PACKET3_CB0_DEST_BASE_ENA (1 << 6)
# define PACKET3_CB1_DEST_BASE_ENA (1 << 7)
# define PACKET3_CB2_DEST_BASE_ENA (1 << 8)
# define PACKET3_CB3_DEST_BASE_ENA (1 << 9)
# define PACKET3_CB4_DEST_BASE_ENA (1 << 10)
# define PACKET3_CB5_DEST_BASE_ENA (1 << 11)
# define PACKET3_CB6_DEST_BASE_ENA (1 << 12)
# define PACKET3_CB7_DEST_BASE_ENA (1 << 13)
# define PACKET3_DB_DEST_BASE_ENA (1 << 14)
# define PACKET3_TCL1_VOL_ACTION_ENA (1 << 15)
# define PACKET3_TC_VOL_ACTION_ENA (1 << 16) /* L2 */
# define PACKET3_TC_WB_ACTION_ENA (1 << 18) /* L2 */
# define PACKET3_DEST_BASE_2_ENA (1 << 19)
# define PACKET3_DEST_BASE_3_ENA (1 << 21)
# define PACKET3_TCL1_ACTION_ENA (1 << 22)
# define PACKET3_TC_ACTION_ENA (1 << 23) /* L2 */
# define PACKET3_CB_ACTION_ENA (1 << 25)
# define PACKET3_DB_ACTION_ENA (1 << 26)
# define PACKET3_SH_KCACHE_ACTION_ENA (1 << 27)
# define PACKET3_SH_KCACHE_VOL_ACTION_ENA (1 << 28)
# define PACKET3_SH_ICACHE_ACTION_ENA (1 << 29)
#define PACKET3_COND_WRITE 0x45
#define PACKET3_EVENT_WRITE 0x46
#define EVENT_TYPE(x) ((x) << 0)
#define EVENT_INDEX(x) ((x) << 8)
/* 0 - any non-TS event
* 1 - ZPASS_DONE, PIXEL_PIPE_STAT_*
* 2 - SAMPLE_PIPELINESTAT
* 3 - SAMPLE_STREAMOUTSTAT*
* 4 - *S_PARTIAL_FLUSH
* 5 - EOP events
* 6 - EOS events
*/
#define PACKET3_EVENT_WRITE_EOP 0x47
#define EOP_TCL1_VOL_ACTION_EN (1 << 12)
#define EOP_TC_VOL_ACTION_EN (1 << 13) /* L2 */
#define EOP_TC_WB_ACTION_EN (1 << 15) /* L2 */
#define EOP_TCL1_ACTION_EN (1 << 16)
#define EOP_TC_ACTION_EN (1 << 17) /* L2 */
#define EOP_TCL2_VOLATILE (1 << 24)
#define EOP_CACHE_POLICY(x) ((x) << 25)
/* 0 - LRU
* 1 - Stream
* 2 - Bypass
*/
#define DATA_SEL(x) ((x) << 29)
/* 0 - discard
* 1 - send low 32bit data
* 2 - send 64bit data
* 3 - send 64bit GPU counter value
* 4 - send 64bit sys counter value
*/
#define INT_SEL(x) ((x) << 24)
/* 0 - none
* 1 - interrupt only (DATA_SEL = 0)
* 2 - interrupt when data write is confirmed
*/
#define DST_SEL(x) ((x) << 16)
/* 0 - MC
* 1 - TC/L2
*/
#define PACKET3_EVENT_WRITE_EOS 0x48
#define PACKET3_RELEASE_MEM 0x49
#define PACKET3_PREAMBLE_CNTL 0x4A
# define PACKET3_PREAMBLE_BEGIN_CLEAR_STATE (2 << 28)
# define PACKET3_PREAMBLE_END_CLEAR_STATE (3 << 28)
#define PACKET3_DMA_DATA 0x50
/* 1. header
* 2. CONTROL
* 3. SRC_ADDR_LO or DATA [31:0]
* 4. SRC_ADDR_HI [31:0]
* 5. DST_ADDR_LO [31:0]
* 6. DST_ADDR_HI [7:0]
* 7. COMMAND [30:21] | BYTE_COUNT [20:0]
*/
/* CONTROL */
# define PACKET3_DMA_DATA_ENGINE(x) ((x) << 0)
/* 0 - ME
* 1 - PFP
*/
# define PACKET3_DMA_DATA_SRC_CACHE_POLICY(x) ((x) << 13)
/* 0 - LRU
* 1 - Stream
* 2 - Bypass
*/
# define PACKET3_DMA_DATA_SRC_VOLATILE (1 << 15)
# define PACKET3_DMA_DATA_DST_SEL(x) ((x) << 20)
/* 0 - DST_ADDR using DAS
* 1 - GDS
* 3 - DST_ADDR using L2
*/
# define PACKET3_DMA_DATA_DST_CACHE_POLICY(x) ((x) << 25)
/* 0 - LRU
* 1 - Stream
* 2 - Bypass
*/
# define PACKET3_DMA_DATA_DST_VOLATILE (1 << 27)
# define PACKET3_DMA_DATA_SRC_SEL(x) ((x) << 29)
/* 0 - SRC_ADDR using SAS
* 1 - GDS
* 2 - DATA
* 3 - SRC_ADDR using L2
*/
# define PACKET3_DMA_DATA_CP_SYNC (1 << 31)
/* COMMAND */
# define PACKET3_DMA_DATA_DIS_WC (1 << 21)
# define PACKET3_DMA_DATA_CMD_SRC_SWAP(x) ((x) << 22)
/* 0 - none
* 1 - 8 in 16
* 2 - 8 in 32
* 3 - 8 in 64
*/
# define PACKET3_DMA_DATA_CMD_DST_SWAP(x) ((x) << 24)
/* 0 - none
* 1 - 8 in 16
* 2 - 8 in 32
* 3 - 8 in 64
*/
# define PACKET3_DMA_DATA_CMD_SAS (1 << 26)
/* 0 - memory
* 1 - register
*/
# define PACKET3_DMA_DATA_CMD_DAS (1 << 27)
/* 0 - memory
* 1 - register
*/
# define PACKET3_DMA_DATA_CMD_SAIC (1 << 28)
# define PACKET3_DMA_DATA_CMD_DAIC (1 << 29)
# define PACKET3_DMA_DATA_CMD_RAW_WAIT (1 << 30)
#define PACKET3_AQUIRE_MEM 0x58
#define PACKET3_REWIND 0x59
#define PACKET3_LOAD_UCONFIG_REG 0x5E
#define PACKET3_LOAD_SH_REG 0x5F
#define PACKET3_LOAD_CONFIG_REG 0x60
#define PACKET3_LOAD_CONTEXT_REG 0x61
#define PACKET3_SET_CONFIG_REG 0x68
#define PACKET3_SET_CONFIG_REG_START 0x00002000
#define PACKET3_SET_CONFIG_REG_END 0x00002c00
#define PACKET3_SET_CONTEXT_REG 0x69
#define PACKET3_SET_CONTEXT_REG_START 0x0000a000
#define PACKET3_SET_CONTEXT_REG_END 0x0000a400
#define PACKET3_SET_CONTEXT_REG_INDIRECT 0x73
#define PACKET3_SET_SH_REG 0x76
#define PACKET3_SET_SH_REG_START 0x00002c00
#define PACKET3_SET_SH_REG_END 0x00003000
#define PACKET3_SET_SH_REG_OFFSET 0x77
#define PACKET3_SET_QUEUE_REG 0x78
#define PACKET3_SET_UCONFIG_REG 0x79
#define PACKET3_SET_UCONFIG_REG_START 0x0000c000
#define PACKET3_SET_UCONFIG_REG_END 0x0000c400
#define PACKET3_SCRATCH_RAM_WRITE 0x7D
#define PACKET3_SCRATCH_RAM_READ 0x7E
#define PACKET3_LOAD_CONST_RAM 0x80
#define PACKET3_WRITE_CONST_RAM 0x81
#define PACKET3_DUMP_CONST_RAM 0x83
#define PACKET3_INCREMENT_CE_COUNTER 0x84
#define PACKET3_INCREMENT_DE_COUNTER 0x85
#define PACKET3_WAIT_ON_CE_COUNTER 0x86
#define PACKET3_WAIT_ON_DE_COUNTER_DIFF 0x88
#define PACKET3_SWITCH_BUFFER 0x8B
#define VCE_CMD_NO_OP 0x00000000
#define VCE_CMD_END 0x00000001
#define VCE_CMD_IB 0x00000002
#define VCE_CMD_FENCE 0x00000003
#define VCE_CMD_TRAP 0x00000004
#define VCE_CMD_IB_AUTO 0x00000005
#define VCE_CMD_SEMAPHORE 0x00000006
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment