1/* $NetBSD: radeon_kv_smc.c,v 1.2 2021/12/18 23:45:43 riastradh Exp $ */ 2 3/* 4 * Copyright 2013 Advanced Micro Devices, Inc. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in 14 * all copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 * OTHER DEALINGS IN THE SOFTWARE. 23 * 24 * Authors: Alex Deucher 25 */ 26 27#include <sys/cdefs.h> 28__KERNEL_RCSID(0, "$NetBSD: radeon_kv_smc.c,v 1.2 2021/12/18 23:45:43 riastradh Exp $"); 29 30#include "radeon.h" 31#include "cikd.h" 32#include "kv_dpm.h" 33 34int kv_notify_message_to_smu(struct radeon_device *rdev, u32 id) 35{ 36 u32 i; 37 u32 tmp = 0; 38 39 WREG32(SMC_MESSAGE_0, id & SMC_MSG_MASK); 40 41 for (i = 0; i < rdev->usec_timeout; i++) { 42 if ((RREG32(SMC_RESP_0) & SMC_RESP_MASK) != 0) 43 break; 44 udelay(1); 45 } 46 tmp = RREG32(SMC_RESP_0) & SMC_RESP_MASK; 47 48 if (tmp != 1) { 49 if (tmp == 0xFF) 50 return -EINVAL; 51 else if (tmp == 0xFE) 52 return -EINVAL; 53 } 54 55 return 0; 56} 57 58int kv_dpm_get_enable_mask(struct radeon_device *rdev, u32 *enable_mask) 59{ 60 int ret; 61 62 ret = kv_notify_message_to_smu(rdev, PPSMC_MSG_SCLKDPM_GetEnabledMask); 63 64 if (ret == 0) 65 *enable_mask = RREG32_SMC(SMC_SYSCON_MSG_ARG_0); 66 67 return ret; 68} 69 70int kv_send_msg_to_smc_with_parameter(struct radeon_device *rdev, 71 PPSMC_Msg msg, u32 parameter) 72{ 73 74 WREG32(SMC_MSG_ARG_0, parameter); 75 76 return kv_notify_message_to_smu(rdev, msg); 77} 78 79static int kv_set_smc_sram_address(struct radeon_device *rdev, 80 u32 smc_address, u32 limit) 81{ 82 if (smc_address & 3) 83 return -EINVAL; 84 if ((smc_address + 3) > limit) 85 return -EINVAL; 86 87 WREG32(SMC_IND_INDEX_0, smc_address); 88 WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0); 89 90 return 0; 91} 92 93int kv_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address, 94 u32 *value, u32 limit) 95{ 96 int ret; 97 98 ret = kv_set_smc_sram_address(rdev, smc_address, limit); 99 if (ret) 100 return ret; 101 102 *value = RREG32(SMC_IND_DATA_0); 103 return 0; 104} 105 106int kv_smc_dpm_enable(struct radeon_device *rdev, bool enable) 107{ 108 if (enable) 109 return kv_notify_message_to_smu(rdev, PPSMC_MSG_DPM_Enable); 110 else 111 return kv_notify_message_to_smu(rdev, PPSMC_MSG_DPM_Disable); 112} 113 114int kv_smc_bapm_enable(struct radeon_device *rdev, bool enable) 115{ 116 if (enable) 117 return kv_notify_message_to_smu(rdev, PPSMC_MSG_EnableBAPM); 118 else 119 return kv_notify_message_to_smu(rdev, PPSMC_MSG_DisableBAPM); 120} 121 122int kv_copy_bytes_to_smc(struct radeon_device *rdev, 123 u32 smc_start_address, 124 const u8 *src, u32 byte_count, u32 limit) 125{ 126 int ret; 127 u32 data, original_data, addr, extra_shift, t_byte, count, mask; 128 129 if ((smc_start_address + byte_count) > limit) 130 return -EINVAL; 131 132 addr = smc_start_address; 133 t_byte = addr & 3; 134 135 /* RMW for the initial bytes */ 136 if (t_byte != 0) { 137 addr -= t_byte; 138 139 ret = kv_set_smc_sram_address(rdev, addr, limit); 140 if (ret) 141 return ret; 142 143 original_data = RREG32(SMC_IND_DATA_0); 144 145 data = 0; 146 mask = 0; 147 count = 4; 148 while (count > 0) { 149 if (t_byte > 0) { 150 mask = (mask << 8) | 0xff; 151 t_byte--; 152 } else if (byte_count > 0) { 153 data = (data << 8) + *src++; 154 byte_count--; 155 mask <<= 8; 156 } else { 157 data <<= 8; 158 mask = (mask << 8) | 0xff; 159 } 160 count--; 161 } 162 163 data |= original_data & mask; 164 165 ret = kv_set_smc_sram_address(rdev, addr, limit); 166 if (ret) 167 return ret; 168 169 WREG32(SMC_IND_DATA_0, data); 170 171 addr += 4; 172 } 173 174 while (byte_count >= 4) { 175 /* SMC address space is BE */ 176 data = (src[0] << 24) + (src[1] << 16) + (src[2] << 8) + src[3]; 177 178 ret = kv_set_smc_sram_address(rdev, addr, limit); 179 if (ret) 180 return ret; 181 182 WREG32(SMC_IND_DATA_0, data); 183 184 src += 4; 185 byte_count -= 4; 186 addr += 4; 187 } 188 189 /* RMW for the final bytes */ 190 if (byte_count > 0) { 191 data = 0; 192 193 ret = kv_set_smc_sram_address(rdev, addr, limit); 194 if (ret) 195 return ret; 196 197 original_data= RREG32(SMC_IND_DATA_0); 198 199 extra_shift = 8 * (4 - byte_count); 200 201 while (byte_count > 0) { 202 /* SMC address space is BE */ 203 data = (data << 8) + *src++; 204 byte_count--; 205 } 206 207 data <<= extra_shift; 208 209 data |= (original_data & ~((~0UL) << extra_shift)); 210 211 ret = kv_set_smc_sram_address(rdev, addr, limit); 212 if (ret) 213 return ret; 214 215 WREG32(SMC_IND_DATA_0, data); 216 } 217 return 0; 218} 219 220