/*
* Copyright 2020 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.
*/
#define SWSMU_CODE_LAYER_L4
#include "amdgpu.h"
#include "amdgpu_smu.h"
#include "smu_cmn.h"
#include "soc15_common.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
#define MP1_C2PMSG_90__CONTENT_MASK 0xFFFFFFFFL
const int link_speed[] = {25, 50, 80, 160, 320, 640};
#undef __SMU_DUMMY_MAP
#define __SMU_DUMMY_MAP(type) #type
static const char * const __smu_message_names[] = {
SMU_MESSAGE_TYPES
};
#define smu_cmn_call_asic_func(intf, smu, args...) \
((smu)->ppt_funcs ? ((smu)->ppt_funcs->intf ? \
(smu)->ppt_funcs->intf(smu, ##args) : \
-ENOTSUPP) : \
-EINVAL)
static const char *smu_get_message_name(struct smu_context *smu,
enum smu_message_type type)
{
if (type >= SMU_MSG_MAX_COUNT)
return "unknown smu message";
return __smu_message_names[type];
}
static void smu_cmn_read_arg(struct smu_context *smu,
uint32_t *arg)
{
struct amdgpu_device *adev = smu->adev;
*arg = RREG32(smu->param_reg);
}
/* Redefine the SMU error codes here.
*
* Note that these definitions are redundant and should be removed
* when the SMU has exported a unified header file containing these
* macros, which header file we can just include and use the SMU's
* macros. At the moment, these error codes are defined by the SMU
* per-ASIC unfortunately, yet we're a one driver for all ASICs.
*/
#define SMU_RESP_NONE 0
#define SMU_RESP_OK 1
#define SMU_RESP_CMD_FAIL 0xFF
#define SMU_RESP_CMD_UNKNOWN 0xFE
#define SMU_RESP_CMD_BAD_PREREQ 0xFD
#define SMU_RESP_BUSY_OTHER 0xFC
#define SMU_RESP_DEBUG_END 0xFB
/**
* __smu_cmn_poll_stat -- poll for a status from the SMU
* @smu: a pointer to SMU context
*
* Returns the status of the SMU, which could be,
* 0, the SMU is busy with your command;
* 1, execution status: success, execution result: success;
* 0xFF, execution status: success, execution result: failure;
* 0xFE, unknown command;
* 0xFD, valid command, but bad (command) prerequisites;
* 0xFC, the command was rejected as the SMU is busy;
* 0xFB, "SMC_Result_DebugDataDumpEnd".
*
* The values here are not defined by macros, because I'd rather we
* include a single header file which defines them, which is
* maintained by the SMU FW team, so that we're impervious to firmware
* changes. At the moment those values are defined in various header
* files, one for each ASIC, yet here we're a single ASIC-agnostic
* interface. Such a change can be followed-up by a subsequent patch.
*/
static u32 __smu_cmn_poll_stat(struct smu_context *smu)
{
struct amdgpu_device *adev = smu->adev;
int timeout = adev->usec_timeout * 20;
u32 reg;
for ( ; timeout > 0; timeout--) {
reg = RREG32(smu->resp_reg);
if ((reg & MP1_C2PMSG_90__CONTENT_MASK) != 0)
break;
udelay(1);
}
return reg;
}
static void __smu_cmn_reg_print_error(struct smu_context *smu,
u32 reg_c2pmsg_90,
int msg_index,
u32 param,
enum smu_message_type msg)
{
struct amdgpu_device *adev = smu->adev;
const char *message = smu_get_message_name(smu, msg);
u32 msg_idx, prm;
switch (reg_c2pmsg_90) {
case SMU_RESP_NONE: {
msg_idx = RREG32(smu->msg_reg);
prm = RREG32(smu->param_reg);
dev_err_ratelimited(adev->dev,
"SMU: I'm not done with your previous command: SMN_C2PMSG_66:0x%08X SMN_C2PMSG_82:0x%08X",
msg_idx, prm);
}
break;
case SMU_RESP_OK:
/* The SMU executed the command. It completed with a
* successful result.
*/
break;
case SMU_RESP_CMD_FAIL:
/* The SMU executed the command. It completed with an
* unsuccessful result.
*/
break;
case SMU_RESP_CMD_UNKNOWN:
dev_err_ratelimited(adev->dev,
"SMU: unknown command: index:%d param:0x%08X message:%s",
msg_index, param, message);
break;
case SMU_RESP_CMD_BAD_PREREQ:
dev_err_ratelimited(adev->dev,
"SMU: valid command, bad prerequisites: index:%d param:0x%08X message:%s"