diff options
author | Kenneth Feng <kenneth.feng@amd.com> | 2023-04-26 14:02:25 +0800 |
---|---|---|
committer | Alex Deucher <alexander.deucher@amd.com> | 2023-10-13 10:59:55 -0400 |
commit | fe6cd9152464ed086fbeb45b6118ca386ee7aca2 (patch) | |
tree | 409e93fbfa5ed302c55390856966569737705d57 /drivers/gpu/drm/amd | |
parent | cd6d69dd9be2b0e586accf82943e9f5b5c592c96 (diff) | |
download | linux-fe6cd9152464ed086fbeb45b6118ca386ee7aca2.tar.gz linux-fe6cd9152464ed086fbeb45b6118ca386ee7aca2.tar.bz2 linux-fe6cd9152464ed086fbeb45b6118ca386ee7aca2.zip |
drm/amd/swsmu: add smu14 ip support
Add initial swSMU support for smu 14 series ASIC.
v2: squash in build fixes and updates (Li Ma)
fix warnings (Alex)
v3: squash in updates (Alex)
v4: squash in updates (Alex)
v5: squash in avg/current power updates (Alex)
Signed-off-by: Li Ma <li.ma@amd.com>
Signed-off-by: Kenneth Feng <kenneth.feng@amd.com>
Signed-off-by: Likun Gao <Likun.Gao@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
Diffstat (limited to 'drivers/gpu/drm/amd')
-rw-r--r-- | drivers/gpu/drm/amd/pm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h | 230 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/smu14/Makefile | 30 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c | 1727 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 1078 | ||||
-rw-r--r-- | drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.h | 28 |
10 files changed, 3110 insertions, 3 deletions
diff --git a/drivers/gpu/drm/amd/pm/Makefile b/drivers/gpu/drm/amd/pm/Makefile index 51751db436b0..ebbf188f625c 100644 --- a/drivers/gpu/drm/amd/pm/Makefile +++ b/drivers/gpu/drm/amd/pm/Makefile @@ -30,6 +30,7 @@ subdir-ccflags-y += \ -I$(FULL_AMD_PATH)/pm/swsmu/smu11 \ -I$(FULL_AMD_PATH)/pm/swsmu/smu12 \ -I$(FULL_AMD_PATH)/pm/swsmu/smu13 \ + -I$(FULL_AMD_PATH)/pm/swsmu/smu14 \ -I$(FULL_AMD_PATH)/pm/powerplay/inc \ -I$(FULL_AMD_PATH)/pm/powerplay/smumgr\ -I$(FULL_AMD_PATH)/pm/powerplay/hwmgr \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/Makefile b/drivers/gpu/drm/amd/pm/swsmu/Makefile index 7987c6cf849d..e5fdda49c96c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/Makefile +++ b/drivers/gpu/drm/amd/pm/swsmu/Makefile @@ -22,7 +22,7 @@ AMD_SWSMU_PATH = ../pm/swsmu -SWSMU_LIBS = smu11 smu12 smu13 +SWSMU_LIBS = smu11 smu12 smu13 smu14 AMD_SWSMU = $(addsuffix /Makefile,$(addprefix $(FULL_AMD_PATH)/pm/swsmu/,$(SWSMU_LIBS))) diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 99750c182279..da6f018aff12 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -43,6 +43,7 @@ #include "smu_v13_0_5_ppt.h" #include "smu_v13_0_6_ppt.h" #include "smu_v13_0_7_ppt.h" +#include "smu_v14_0_0_ppt.h" #include "amd_pcie.h" /* @@ -660,6 +661,9 @@ static int smu_set_funcs(struct amdgpu_device *adev) case IP_VERSION(13, 0, 7): smu_v13_0_7_set_ppt_funcs(smu); break; + case IP_VERSION(14, 0, 0): + smu_v14_0_0_set_ppt_funcs(smu); + break; default: return -EINVAL; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index f3cab5e633a7..72d632be0ee6 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -1343,6 +1343,12 @@ struct pptable_funcs { * @init_pptable_microcode: Prepare the pptable microcode to upload via PSP */ int (*init_pptable_microcode)(struct smu_context *smu); + + /** + * @dpm_set_vpe_enable: Enable/disable VPE engine dynamic power + * management. + */ + int (*dpm_set_vpe_enable)(struct smu_context *smu, bool enable); }; typedef enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 4850e48bbef5..3958c9cb4e91 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -253,7 +253,9 @@ __SMU_DUMMY_MAP(QueryValidMcaCeCount), \ __SMU_DUMMY_MAP(McaBankDumpDW), \ __SMU_DUMMY_MAP(McaBankCeDumpDW), \ - __SMU_DUMMY_MAP(SelectPLPDMode), + __SMU_DUMMY_MAP(SelectPLPDMode), \ + __SMU_DUMMY_MAP(PowerUpVpe), \ + __SMU_DUMMY_MAP(PowerDownVpe), #undef __SMU_DUMMY_MAP #define __SMU_DUMMY_MAP(type) SMU_MSG_##type @@ -415,7 +417,8 @@ enum smu_clk_type { __SMU_DUMMY_MAP(MEM_TEMP_READ), \ __SMU_DUMMY_MAP(ATHUB_MMHUB_PG), \ __SMU_DUMMY_MAP(BACO_CG), \ - __SMU_DUMMY_MAP(SOC_CG), + __SMU_DUMMY_MAP(SOC_CG), \ + __SMU_DUMMY_MAP(LOW_POWER_DCNCLKS), #undef __SMU_DUMMY_MAP #define __SMU_DUMMY_MAP(feature) SMU_FEATURE_##feature##_BIT diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h new file mode 100644 index 000000000000..a5b569976f19 --- /dev/null +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v14_0.h @@ -0,0 +1,230 @@ +/* + * Copyright 2023 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_V14_0_H__ +#define __SMU_V14_0_H__ + +#include "amdgpu_smu.h" + +#define SMU14_DRIVER_IF_VERSION_INV 0xFFFFFFFF +#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_2 0x1 +#define SMU14_DRIVER_IF_VERSION_SMU_V14_0_0 0x6 + +#define FEATURE_MASK(feature) (1ULL << feature) + +/* MP Apertures */ +#define MP0_Public 0x03800000 +#define MP0_SRAM 0x03900000 +#define MP1_Public 0x03b00000 +#define MP1_SRAM 0x03c00004 + +/* address block */ +#define smnMP1_FIRMWARE_FLAGS 0x3010028 +#define smnMP1_PUB_CTRL 0x3010d10 + +#define MAX_DPM_LEVELS 16 +#define MAX_PCIE_CONF 3 + +struct smu_14_0_max_sustainable_clocks { + uint32_t display_clock; + uint32_t phy_clock; + uint32_t pixel_clock; + uint32_t uclock; + uint32_t dcef_clock; + uint32_t soc_clock; +}; + +struct smu_14_0_dpm_clk_level { + bool enabled; + uint32_t value; +}; + +struct smu_14_0_dpm_table { + uint32_t min; /* MHz */ + uint32_t max; /* MHz */ + uint32_t count; + bool is_fine_grained; + struct smu_14_0_dpm_clk_level dpm_levels[MAX_DPM_LEVELS]; +}; + +struct smu_14_0_pcie_table { + uint8_t pcie_gen[MAX_PCIE_CONF]; + uint8_t pcie_lane[MAX_PCIE_CONF]; + uint16_t clk_freq[MAX_PCIE_CONF]; + uint32_t num_of_link_levels; +}; + +struct smu_14_0_dpm_tables { + struct smu_14_0_dpm_table soc_table; + struct smu_14_0_dpm_table gfx_table; + struct smu_14_0_dpm_table uclk_table; + struct smu_14_0_dpm_table eclk_table; + struct smu_14_0_dpm_table vclk_table; + struct smu_14_0_dpm_table dclk_table; + struct smu_14_0_dpm_table dcef_table; + struct smu_14_0_dpm_table pixel_table; + struct smu_14_0_dpm_table display_table; + struct smu_14_0_dpm_table phy_table; + struct smu_14_0_dpm_table fclk_table; + struct smu_14_0_pcie_table pcie_table; +}; + +struct smu_14_0_dpm_context { + struct smu_14_0_dpm_tables dpm_tables; + uint32_t workload_policy_mask; + uint32_t dcef_min_ds_clk; +}; + +enum smu_14_0_power_state { + SMU_14_0_POWER_STATE__D0 = 0, + SMU_14_0_POWER_STATE__D1, + SMU_14_0_POWER_STATE__D3, /* Sleep*/ + SMU_14_0_POWER_STATE__D4, /* Hibernate*/ + SMU_14_0_POWER_STATE__D5, /* Power off*/ +}; + +struct smu_14_0_power_context { + uint32_t power_source; + uint8_t in_power_limit_boost_mode; + enum smu_14_0_power_state power_state; +}; + +#if defined(SWSMU_CODE_LAYER_L2) || defined(SWSMU_CODE_LAYER_L3) + +int smu_v14_0_init_microcode(struct smu_context *smu); + +void smu_v14_0_fini_microcode(struct smu_context *smu); + +int smu_v14_0_load_microcode(struct smu_context *smu); + +int smu_v14_0_init_smc_tables(struct smu_context *smu); + +int smu_v14_0_fini_smc_tables(struct smu_context *smu); + +int smu_v14_0_init_power(struct smu_context *smu); + +int smu_v14_0_fini_power(struct smu_context *smu); + +int smu_v14_0_check_fw_status(struct smu_context *smu); + +int smu_v14_0_setup_pptable(struct smu_context *smu); + +int smu_v14_0_get_vbios_bootup_values(struct smu_context *smu); + +int smu_v14_0_check_fw_version(struct smu_context *smu); + +int smu_v14_0_set_driver_table_location(struct smu_context *smu); + +int smu_v14_0_set_tool_table_location(struct smu_context *smu); + +int smu_v14_0_notify_memory_pool_location(struct smu_context *smu); + +int smu_v14_0_system_features_control(struct smu_context *smu, + bool en); + +int smu_v14_0_set_allowed_mask(struct smu_context *smu); + +int smu_v14_0_notify_display_change(struct smu_context *smu); + +int smu_v14_0_get_current_power_limit(struct smu_context *smu, + uint32_t *power_limit); + +int smu_v14_0_set_power_limit(struct smu_context *smu, + enum smu_ppt_limit_type limit_type, + uint32_t limit); + +int smu_v14_0_gfx_off_control(struct smu_context *smu, bool enable); + +int smu_v14_0_register_irq_handler(struct smu_context *smu); + +int smu_v14_0_baco_set_armd3_sequence(struct smu_context *smu, + enum smu_baco_seq baco_seq); + +bool smu_v14_0_baco_is_support(struct smu_context *smu); + +enum smu_baco_state smu_v14_0_baco_get_state(struct smu_context *smu); + +int smu_v14_0_baco_set_state(struct smu_context *smu, enum smu_baco_state state); + +int smu_v14_0_baco_enter(struct smu_context *smu); +int smu_v14_0_baco_exit(struct smu_context *smu); + +int smu_v14_0_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, + uint32_t *min, uint32_t *max); + +int smu_v14_0_set_soft_freq_limited_range(struct smu_context *smu, enum smu_clk_type clk_type, + uint32_t min, uint32_t max); + +int smu_v14_0_set_hard_freq_limited_range(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t min, + uint32_t max); + +int smu_v14_0_set_performance_level(struct smu_context *smu, + enum amd_dpm_forced_level level); + +int smu_v14_0_set_power_source(struct smu_context *smu, + enum smu_power_src_type power_src); + +int smu_v14_0_set_single_dpm_table(struct smu_context *smu, + enum smu_clk_type clk_type, + struct smu_14_0_dpm_table *single_dpm_table); + +int smu_v14_0_gfx_ulv_control(struct smu_context *smu, + bool enablement); + +int smu_v14_0_wait_for_event(struct smu_context *smu, enum smu_event_type event, + uint64_t event_arg); + +int smu_v14_0_set_vcn_enable(struct smu_context *smu, + bool enable); + +int smu_v14_0_set_jpeg_enable(struct smu_context *smu, + bool enable); + +int smu_v14_0_init_pptable_microcode(struct smu_context *smu); + +int smu_v14_0_run_btc(struct smu_context *smu); + +int smu_v14_0_gpo_control(struct smu_context *smu, + bool enablement); + +int smu_v14_0_deep_sleep_control(struct smu_context *smu, + bool enablement); + +int smu_v14_0_set_gfx_power_up_by_imu(struct smu_context *smu); + +int smu_v14_0_set_default_dpm_tables(struct smu_context *smu); + +int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, + void **table, + uint32_t *size, + uint32_t pptable_id); + +int smu_v14_0_od_edit_dpm_table(struct smu_context *smu, + enum PP_OD_DPM_TABLE_COMMAND type, + long input[], uint32_t size); + +void smu_v14_0_set_smu_mailbox_registers(struct smu_context *smu); + +#endif +#endif diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/Makefile b/drivers/gpu/drm/amd/pm/swsmu/smu14/Makefile new file mode 100644 index 000000000000..ddbac5c655f7 --- /dev/null +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/Makefile @@ -0,0 +1,30 @@ +# +# Copyright 2023 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. +# +# +# Makefile for the 'smu manager' sub-component of powerplay. +# It provides the smu management services for the driver. + +SMU14_MGR = smu_v14_0.o smu_v14_0_0_ppt.o + +AMD_SWSMU_SMU14MGR = $(addprefix $(AMD_SWSMU_PATH)/smu14/,$(SMU14_MGR)) + +AMD_POWERPLAY_FILES += $(AMD_SWSMU_SMU14MGR) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c new file mode 100644 index 000000000000..4ac22f44d160 --- /dev/null +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0.c @@ -0,0 +1,1727 @@ +/* + * Copyright 2023 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/module.h> +#include <linux/pci.h> +#include <linux/reboot.h> + +#define SWSMU_CODE_LAYER_L3 + +#include "amdgpu.h" +#include "amdgpu_smu.h" +#include "atomfirmware.h" +#include "amdgpu_atomfirmware.h" +#include "amdgpu_atombios.h" +#include "smu_v14_0.h" +#include "soc15_common.h" +#include "atom.h" +#include "amdgpu_ras.h" +#include "smu_cmn.h" + +#include "asic_reg/mp/mp_14_0_0_offset.h" +#include "asic_reg/mp/mp_14_0_0_sh_mask.h" + +/* + * DO NOT use these for err/warn/info/debug messages. + * Use dev_err, dev_warn, dev_info and dev_dbg instead. + * They are more MGPU friendly. + */ +#undef pr_err +#undef pr_warn +#undef pr_info +#undef pr_debug + +MODULE_FIRMWARE("amdgpu/smu_14_0_2.bin"); + +int smu_v14_0_init_microcode(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + char fw_name[30]; + char ucode_prefix[30]; + int err = 0; + const struct smc_firmware_header_v1_0 *hdr; + const struct common_firmware_header *header; + struct amdgpu_firmware_info *ucode = NULL; + + /* doesn't need to load smu firmware in IOV mode */ + if (amdgpu_sriov_vf(adev)) + return 0; + + amdgpu_ucode_ip_version_decode(adev, MP1_HWIP, ucode_prefix, sizeof(ucode_prefix)); + + snprintf(fw_name, sizeof(fw_name), "amdgpu/%s.bin", ucode_prefix); + + err = amdgpu_ucode_request(adev, &adev->pm.fw, fw_name); + if (err) + goto out; + + 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); + + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + ucode = &adev->firmware.ucode[AMDGPU_UCODE_ID_SMC]; + ucode->ucode_id = AMDGPU_UCODE_ID_SMC; + ucode->fw = adev->pm.fw; + header = (const struct common_firmware_header *)ucode->fw->data; + adev->firmware.fw_size += + ALIGN(le32_to_cpu(header->ucode_size_bytes), PAGE_SIZE); + } + +out: + if (err) + amdgpu_ucode_release(&adev->pm.fw); + return err; +} + +void smu_v14_0_fini_microcode(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + + amdgpu_ucode_release(&adev->pm.fw); + adev->pm.fw_version = 0; +} + +int smu_v14_0_load_microcode(struct smu_context *smu) +{ +#if 0 + struct amdgpu_device *adev = smu->adev; + const uint32_t *src; + const struct smc_firmware_header_v1_0 *hdr; + uint32_t addr_start = MP1_SRAM; + uint32_t i; + uint32_t smc_fw_size; + uint32_t mp1_fw_flags; + + hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; + src = (const uint32_t *)(adev->pm.fw->data + + le32_to_cpu(hdr->header.ucode_array_offset_bytes)); + smc_fw_size = hdr->header.ucode_size_bytes; + + for (i = 1; i < smc_fw_size/4 - 1; i++) { + WREG32_PCIE(addr_start, src[i]); + addr_start += 4; + } + + WREG32_PCIE(MP1_Public | (smnMP1_PUB_CTRL & 0xffffffff), + 1 & MP1_SMN_PUB_CTRL__LX3_RESET_MASK); + WREG32_PCIE(MP1_Public | (smnMP1_PUB_CTRL & 0xffffffff), + 1 & ~MP1_SMN_PUB_CTRL__LX3_RESET_MASK); + + for (i = 0; i < adev->usec_timeout; i++) { + mp1_fw_flags = RREG32_PCIE(MP1_Public | + (smnMP1_FIRMWARE_FLAGS & 0xffffffff)); + if ((mp1_fw_flags & MP1_CRU1_MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >> + MP1_CRU1_MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT) + break; + udelay(1); + } + + if (i == adev->usec_timeout) + return -ETIME; + +#endif + return 0; + +} + +int smu_v14_0_init_pptable_microcode(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + struct amdgpu_firmware_info *ucode = NULL; + uint32_t size = 0, pptable_id = 0; + int ret = 0; + void *table; + + /* doesn't need to load smu firmware in IOV mode */ + if (amdgpu_sriov_vf(adev)) + return 0; + + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) + return 0; + + if (!adev->scpm_enabled) + return 0; + + /* override pptable_id from driver parameter */ + if (amdgpu_smu_pptable_id >= 0) { + pptable_id = amdgpu_smu_pptable_id; + dev_info(adev->dev, "override pptable id %d\n", pptable_id); + } else { + pptable_id = smu->smu_table.boot_values.pp_table_id; + } + + /* "pptable_id == 0" means vbios carries the pptable. */ + if (!pptable_id) + return 0; + + ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + if (ret) + return ret; + + smu->pptable_firmware.data = table; + smu->pptable_firmware.size = size; + + ucode = &adev->firmware.ucode[AMDGPU_UCODE_ID_PPTABLE]; + ucode->ucode_id = AMDGPU_UCODE_ID_PPTABLE; + ucode->fw = &smu->pptable_firmware; + adev->firmware.fw_size += + ALIGN(smu->pptable_firmware.size, PAGE_SIZE); + + return 0; +} + +int smu_v14_0_check_fw_status(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + uint32_t mp1_fw_flags; + + mp1_fw_flags = RREG32_PCIE(MP1_Public | + (smnMP1_FIRMWARE_FLAGS & 0xffffffff)); + + if ((mp1_fw_flags & MP1_CRU1_MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED_MASK) >> + MP1_CRU1_MP1_FIRMWARE_FLAGS__INTERRUPTS_ENABLED__SHIFT) + return 0; + + return -EIO; +} + +int smu_v14_0_check_fw_version(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + uint32_t if_version = 0xff, smu_version = 0xff; + uint8_t smu_program, smu_major, smu_minor, smu_debug; + int ret = 0; + + ret = smu_cmn_get_smc_version(smu, &if_version, &smu_version); + if (ret) + return ret; + + smu_program = (smu_version >> 24) & 0xff; + smu_major = (smu_version >> 16) & 0xff; + smu_minor = (smu_version >> 8) & 0xff; + smu_debug = (smu_version >> 0) & 0xff; + if (smu->is_apu) + adev->pm.fw_version = smu_version; + + switch (adev->ip_versions[MP1_HWIP][0]) { + case IP_VERSION(14, 0, 2): + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_2; + break; + case IP_VERSION(14, 0, 0): + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_SMU_V14_0_0; + break; + default: + dev_err(adev->dev, "smu unsupported IP version: 0x%x.\n", + adev->ip_versions[MP1_HWIP][0]); + smu->smc_driver_if_version = SMU14_DRIVER_IF_VERSION_INV; + break; + } + + if (adev->pm.fw) + dev_dbg(smu->adev->dev, "smu fw reported program %d, version = 0x%08x (%d.%d.%d)\n", + smu_program, smu_version, smu_major, smu_minor, smu_debug); + + /* + * 1. if_version mismatch is not critical as our fw is designed + * to be backward compatible. + * 2. New fw usually brings some optimizations. But that's visible + * only on the paired driver. + * Considering above, we just leave user a verbal message instead + * of halt driver loading. + */ + if (if_version != smu->smc_driver_if_version) { + dev_info(adev->dev, "smu driver if version = 0x%08x, smu fw if version = 0x%08x, " + "smu fw program = %d, smu fw version = 0x%08x (%d.%d.%d)\n", + smu->smc_driver_if_version, if_version, + smu_program, smu_version, smu_major, smu_minor, smu_debug); + dev_info(adev->dev, "SMU driver if version not matched\n"); + } + + return ret; +} + +static int smu_v14_0_set_pptable_v2_0(struct smu_context *smu, void **table, uint32_t *size) +{ + struct amdgpu_device *adev = smu->adev; + uint32_t ppt_offset_bytes; + const struct smc_firmware_header_v2_0 *v2; + + v2 = (const struct smc_firmware_header_v2_0 *) adev->pm.fw->data; + + ppt_offset_bytes = le32_to_cpu(v2->ppt_offset_bytes); + *size = le32_to_cpu(v2->ppt_size_bytes); + *table = (uint8_t *)v2 + ppt_offset_bytes; + + return 0; +} + +static int smu_v14_0_set_pptable_v2_1(struct smu_context *smu, void **table, + uint32_t *size, uint32_t pptable_id) +{ + struct amdgpu_device *adev = smu->adev; + const struct smc_firmware_header_v2_1 *v2_1; + struct smc_soft_pptable_entry *entries; + uint32_t pptable_count = 0; + int i = 0; + + v2_1 = (const struct smc_firmware_header_v2_1 *) adev->pm.fw->data; + entries = (struct smc_soft_pptable_entry *) + ((uint8_t *)v2_1 + le32_to_cpu(v2_1->pptable_entry_offset)); + pptable_count = le32_to_cpu(v2_1->pptable_count); + for (i = 0; i < pptable_count; i++) { + if (le32_to_cpu(entries[i].id) == pptable_id) { + *table = ((uint8_t *)v2_1 + le32_to_cpu(entries[i].ppt_offset_bytes)); + *size = le32_to_cpu(entries[i].ppt_size_bytes); + break; + } + } + + if (i == pptable_count) + return -EINVAL; + + return 0; +} + +static int smu_v14_0_get_pptable_from_vbios(struct smu_context *smu, void **table, uint32_t *size) +{ + struct amdgpu_device *adev = smu->adev; + uint16_t atom_table_size; + uint8_t frev, crev; + int ret, index; + + dev_info(adev->dev, "use vbios provided pptable\n"); + index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, + powerplayinfo); + + ret = amdgpu_atombios_get_data_table(adev, index, &atom_table_size, &frev, &crev, + (uint8_t **)table); + if (ret) + return ret; + + if (size) + *size = atom_table_size; + + return 0; +} + +int smu_v14_0_get_pptable_from_firmware(struct smu_context *smu, + void **table, + uint32_t *size, + uint32_t pptable_id) +{ + const struct smc_firmware_header_v1_0 *hdr; + struct amdgpu_device *adev = smu->adev; + uint16_t version_major, version_minor; + int ret; + + hdr = (const struct smc_firmware_header_v1_0 *) adev->pm.fw->data; + if (!hdr) + return -EINVAL; + + dev_info(adev->dev, "use driver provided pptable %d\n", pptable_id); + + version_major = le16_to_cpu(hdr->header.header_version_major); + version_minor = le16_to_cpu(hdr->header.header_version_minor); + if (version_major != 2) { + dev_err(adev->dev, "Unsupported smu firmware version %d.%d\n", + version_major, version_minor); + return -EINVAL; + } + + switch (version_minor) { + case 0: + ret = smu_v14_0_set_pptable_v2_0(smu, table, size); + break; + case 1: + ret = smu_v14_0_set_pptable_v2_1(smu, table, size, pptable_id); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +int smu_v14_0_setup_pptable(struct smu_context *smu) +{ + struct amdgpu_device *adev = smu->adev; + uint32_t size = 0, pptable_id = 0; + void *table; + int ret = 0; + + /* override pptable_id from driver parameter */ + if (amdgpu_smu_pptable_id >= 0) { + pptable_id = amdgpu_smu_pptable_id; + dev_info(adev->dev, "override pptable id %d\n", pptable_id); + } else { + pptable_id = smu->smu_table.boot_values.pp_table_id; + } + + /* force using vbios pptable in sriov mode */ + if ((amdgpu_sriov_vf(adev) || !pptable_id) && (amdgpu_emu_mode != 1)) + ret = smu_v14_0_get_pptable_from_vbios(smu, &table, &size); + else + ret = smu_v14_0_get_pptable_from_firmware(smu, &table, &size, pptable_id); + + if (ret) + return ret; + + if (!smu->smu_table.power_play_table) + smu->smu_table.power_play_table = table; + if (!smu->smu_table.power_play_table_size) + smu->smu_table.power_play_table_size = size; + + return 0; +} + +int smu_v14_0_init_smc_tables(struct smu_context *smu) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_table *tables = smu_table->tables; + int ret = 0; + + smu_table->driver_pptable = + kzalloc(tables[SMU_TABLE_PPTABLE].size, GFP_KERNEL); + if (!smu_table->driver_pptable) { + ret = -ENOMEM; + goto err0_out; + } + + smu_table->max_sustainable_clocks = + kzalloc(sizeof(struct smu_14_0_max_sustainable_clocks), GFP_KERNEL); + if (!smu_table->max_sustainable_clocks) { + ret = -ENOMEM; + goto err1_out; + } + + if (tables[SMU_TABLE_OVERDRIVE].size) { + smu_table->overdrive_table = + kzalloc(tables[SMU_TABLE_OVERDRIVE].size, GFP_KERNEL); + if (!smu_table->overdrive_table) { + ret = -ENOMEM; + goto err2_out; + } + + smu_table->boot_overdrive_table = + kzalloc(tables[SMU_TABLE_OVERDRIVE].size, GFP_KERNEL); + if (!smu_table->boot_overdrive_table) { + ret = -ENOMEM; + goto err3_out; + } + } + + smu_table->combo_pptable = + kzalloc(tables[SMU_TABLE_COMBO_PPTABLE].size, GFP_KERNEL); + if (!smu_table->combo_pptable) { + ret = -ENOMEM; + goto err4_out; + } + + return 0; + +err4_out: + kfree(smu_table->boot_overdrive_table); +err3_out: + kfree(smu_table->overdrive_table); +err2_out: + kfree(smu_table->max_sustainable_clocks); +err1_out: + kfree(smu_table->driver_pptable); +err0_out: + return ret; +} + +int smu_v14_0_fini_smc_tables(struct smu_context *smu) +{ + struct smu_table_context *smu_table = &smu->smu_table; + struct smu_dpm_context *smu_dpm = &smu->smu_dpm; + + kfree(smu_table->gpu_metrics_table); + kfree(smu_table->combo_pptable); + kfree(smu_table->boot_overdrive_table); + kfree(smu_table->overdrive_table); + kfree(smu_table->max_sustainable_clocks); + kfree(smu_table->driver_pptable); + smu_table->gpu_metrics_table = NULL; + smu_table->combo_pptable = NULL; + smu_table->boot_overdrive_table = NULL; + smu_table->overdrive_table = NULL; + smu_table->max_sustainable_clocks = NULL; + smu_table->driver_pptable = NULL; + kfree(smu_table->hardcode_pptable); + smu_table->hardcode_pptable = NULL; + + kfree(smu_table->ecc_table); + kfree(smu_table->metrics_table); + kfree(smu_table->watermarks_table); + smu_table->ecc_table = NULL; + smu_table->metrics_table = NULL; + smu_table->watermarks_table = NULL; + smu_table->metrics_time = 0; + + kfree(smu_dpm->dpm_context); + kfree(smu_dpm->golden_dpm_context); + kfree(smu_dpm->dpm_current_power_state); + kfree(smu_dpm->dpm_request_power_state); + smu_dpm->dpm_context = NULL; + smu_dpm->golden_dpm_context = NULL; + smu_dpm->dpm_context_size = 0; + smu_dpm->dpm_current_power_state = NULL; + smu_dpm->dpm_request_power_state = NULL; + + return 0; +} + +int smu_v14_0_init_power(struct smu_context *smu) +{ + struct smu_power_context *smu_power = &smu->smu_power; + + if (smu_power->power_context || smu_power->power_context_size != 0) + return -EINVAL; + + smu_power->power_context = kzalloc(sizeof(struct smu_14_0_dpm_context), + GFP_KERNEL); + if (!smu_power->power_context) + return -ENOMEM; + smu_power->power_context_size = sizeof(struct smu_14_0_dpm_context); + + return 0; +} + +int smu_v14_0_fini_power(struct smu_context *smu) +{ + struct smu_power_context *smu_power = &smu->smu_power; + + if (!smu_power->power_context || smu_power->power_context_size == 0) + return -EINVAL; + + kfree(smu_power->power_context); + smu_power->power_context = NULL; + smu_power->power_context_size = 0; + + return 0; +} + +int smu_v14_0_get_vbios_bootup_values(struct smu_context *smu) +{ + int ret, index; + uint16_t size; + uint8_t frev, crev; + struct atom_common_table_header *header; + struct atom_firmware_info_v3_4 *v_3_4; + struct atom_firmware_info_v3_3 *v_3_3; + struct atom_firmware_info_v3_1 *v_3_1; + struct atom_smu_info_v3_6 *smu_info_v3_6; + struct atom_smu_info_v4_0 *smu_info_v4_0; + + index = get_index_into_master_table(atom_master_list_of_data_tables_v2_1, + firmwareinfo); + + ret = amdgpu_atombios_get_data_table(smu->adev, index, &size, &frev, &crev, + (uint8_t **)&header); + if (ret) + return ret; + + if (header->format_revision != 3) { + dev_err(smu->adev->dev, "unknown atom_firmware_info version! for smu14\n"); + return -EINVAL; + } + + switch (header->content_revision) { + case 0: + case 1: + case 2: + v_3_1 = (struct atom_firmware_info_v3_1 *)header; + smu->smu_table.boot_values.revision = v_3_1->firmware_revision; + smu->smu_table.boot_values.gfxclk = v_3_1->bootup_sclk_in10khz; + smu->smu_table.boot_values.uclk = v_3_1->bootup_mclk_in10khz; + smu->smu_table.boot_values.socclk = 0; + smu->smu_table.boot_values.dcefclk = 0; + smu->smu_table.boot_values.vddc = v_3_1->bootup_vddc_mv; + smu->smu_table.boot_values.vddci = v_3_1->bootup_vddci_mv; + smu->smu_table.boot_values.mvddc = v_3_1->bootup_mvddc_mv; + smu->smu_table.boot_values.vdd_gfx = v_3_1->bootup_vddgfx_mv; + smu->smu_table.boot_values.cooling_id = v_3_1->coolingsolution_id; + smu->smu_table.boot_values.pp_table_id = 0; + break; + case 3: + v_3_3 = (struct atom_firmware_info_v3_3 *)header; + smu->smu_table.boot_values.revision = v_3_3->firmware_revision; + smu->smu_table.boot_values.gfxclk = v_3_3->bootup_sclk_in10khz; + smu->smu_table.boot_values.uclk = v_3_3->bootup_mclk_in10khz; + smu->smu_table.boot_values.socclk = 0; + smu->smu_tabl |