/*
* 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);