These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / gpu / drm / amd / amdgpu / cz_smc.c
diff --git a/kernel/drivers/gpu/drm/amd/amdgpu/cz_smc.c b/kernel/drivers/gpu/drm/amd/amdgpu/cz_smc.c
new file mode 100644 (file)
index 0000000..ac7fee7
--- /dev/null
@@ -0,0 +1,1000 @@
+/*
+ * 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;
+
+       if (adev->asic_type == CHIP_STONEY)
+               fw_to_check &= ~(UCODE_ID_SDMA1_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 || adev->asic_type == CHIP_STONEY) {
+               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;
+
+       if (adev->asic_type == CHIP_STONEY)
+               adev->smu.fw_flags &= ~(AMDGPU_SDMA1_UCODE_LOADED | AMDGPU_CPMEC2_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);
+               if (adev->asic_type == CHIP_STONEY) {
+                       cz_smu_populate_single_ucode_load_task(adev,
+                               CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
+               } else {
+                       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);
+               if (adev->asic_type == CHIP_STONEY) {
+                       cz_smu_populate_single_ucode_load_task(adev,
+                               CZ_SCRATCH_ENTRY_UCODE_ID_SDMA0, false);
+               } else {
+                       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);
+               if (adev->asic_type == CHIP_STONEY) {
+                       cz_smu_populate_single_ucode_load_task(adev,
+                               CZ_SCRATCH_ENTRY_UCODE_ID_CP_MEC_JT1, false);
+               } else {
+                       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, 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, 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 (adev->asic_type == CHIP_STONEY) {
+                       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;
+               } else {
+                       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 (adev->asic_type == CHIP_STONEY) {
+                       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;
+               } else {
+                       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;
+}