si_smc.c (7347B)
1/* 2 * Copyright 2011 Advanced Micro Devices, Inc. 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in 12 * all copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20 * OTHER DEALINGS IN THE SOFTWARE. 21 * 22 * Authors: Alex Deucher 23 */ 24 25#include <linux/firmware.h> 26 27#include "radeon.h" 28#include "sid.h" 29#include "ppsmc.h" 30#include "radeon_ucode.h" 31#include "sislands_smc.h" 32 33static int si_set_smc_sram_address(struct radeon_device *rdev, 34 u32 smc_address, u32 limit) 35{ 36 if (smc_address & 3) 37 return -EINVAL; 38 if ((smc_address + 3) > limit) 39 return -EINVAL; 40 41 WREG32(SMC_IND_INDEX_0, smc_address); 42 WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0); 43 44 return 0; 45} 46 47int si_copy_bytes_to_smc(struct radeon_device *rdev, 48 u32 smc_start_address, 49 const u8 *src, u32 byte_count, u32 limit) 50{ 51 unsigned long flags; 52 int ret = 0; 53 u32 data, original_data, addr, extra_shift; 54 55 if (smc_start_address & 3) 56 return -EINVAL; 57 if ((smc_start_address + byte_count) > limit) 58 return -EINVAL; 59 60 addr = smc_start_address; 61 62 spin_lock_irqsave(&rdev->smc_idx_lock, flags); 63 while (byte_count >= 4) { 64 /* SMC address space is BE */ 65 data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; 66 67 ret = si_set_smc_sram_address(rdev, addr, limit); 68 if (ret) 69 goto done; 70 71 WREG32(SMC_IND_DATA_0, data); 72 73 src += 4; 74 byte_count -= 4; 75 addr += 4; 76 } 77 78 /* RMW for the final bytes */ 79 if (byte_count > 0) { 80 data = 0; 81 82 ret = si_set_smc_sram_address(rdev, addr, limit); 83 if (ret) 84 goto done; 85 86 original_data = RREG32(SMC_IND_DATA_0); 87 88 extra_shift = 8 * (4 - byte_count); 89 90 while (byte_count > 0) { 91 /* SMC address space is BE */ 92 data = (data << 8) + *src++; 93 byte_count--; 94 } 95 96 data <<= extra_shift; 97 98 data |= (original_data & ~((~0UL) << extra_shift)); 99 100 ret = si_set_smc_sram_address(rdev, addr, limit); 101 if (ret) 102 goto done; 103 104 WREG32(SMC_IND_DATA_0, data); 105 } 106 107done: 108 spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 109 110 return ret; 111} 112 113void si_start_smc(struct radeon_device *rdev) 114{ 115 u32 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL); 116 117 tmp &= ~RST_REG; 118 119 WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp); 120} 121 122void si_reset_smc(struct radeon_device *rdev) 123{ 124 u32 tmp; 125 126 RREG32(CB_CGTT_SCLK_CTRL); 127 RREG32(CB_CGTT_SCLK_CTRL); 128 RREG32(CB_CGTT_SCLK_CTRL); 129 RREG32(CB_CGTT_SCLK_CTRL); 130 131 tmp = RREG32_SMC(SMC_SYSCON_RESET_CNTL); 132 tmp |= RST_REG; 133 WREG32_SMC(SMC_SYSCON_RESET_CNTL, tmp); 134} 135 136int si_program_jump_on_start(struct radeon_device *rdev) 137{ 138 static const u8 data[] = { 0x0E, 0x00, 0x40, 0x40 }; 139 140 return si_copy_bytes_to_smc(rdev, 0x0, data, 4, sizeof(data)+1); 141} 142 143void si_stop_smc_clock(struct radeon_device *rdev) 144{ 145 u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0); 146 147 tmp |= CK_DISABLE; 148 149 WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp); 150} 151 152void si_start_smc_clock(struct radeon_device *rdev) 153{ 154 u32 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0); 155 156 tmp &= ~CK_DISABLE; 157 158 WREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0, tmp); 159} 160 161bool si_is_smc_running(struct radeon_device *rdev) 162{ 163 u32 rst = RREG32_SMC(SMC_SYSCON_RESET_CNTL); 164 u32 clk = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0); 165 166 if (!(rst & RST_REG) && !(clk & CK_DISABLE)) 167 return true; 168 169 return false; 170} 171 172PPSMC_Result si_send_msg_to_smc(struct radeon_device *rdev, PPSMC_Msg msg) 173{ 174 u32 tmp; 175 int i; 176 177 if (!si_is_smc_running(rdev)) 178 return PPSMC_Result_Failed; 179 180 WREG32(SMC_MESSAGE_0, msg); 181 182 for (i = 0; i < rdev->usec_timeout; i++) { 183 tmp = RREG32(SMC_RESP_0); 184 if (tmp != 0) 185 break; 186 udelay(1); 187 } 188 tmp = RREG32(SMC_RESP_0); 189 190 return (PPSMC_Result)tmp; 191} 192 193PPSMC_Result si_wait_for_smc_inactive(struct radeon_device *rdev) 194{ 195 u32 tmp; 196 int i; 197 198 if (!si_is_smc_running(rdev)) 199 return PPSMC_Result_OK; 200 201 for (i = 0; i < rdev->usec_timeout; i++) { 202 tmp = RREG32_SMC(SMC_SYSCON_CLOCK_CNTL_0); 203 if ((tmp & CKEN) == 0) 204 break; 205 udelay(1); 206 } 207 208 return PPSMC_Result_OK; 209} 210 211int si_load_smc_ucode(struct radeon_device *rdev, u32 limit) 212{ 213 unsigned long flags; 214 u32 ucode_start_address; 215 u32 ucode_size; 216 const u8 *src; 217 u32 data; 218 219 if (!rdev->smc_fw) 220 return -EINVAL; 221 222 if (rdev->new_fw) { 223 const struct smc_firmware_header_v1_0 *hdr = 224 (const struct smc_firmware_header_v1_0 *)rdev->smc_fw->data; 225 226 radeon_ucode_print_smc_hdr(&hdr->header); 227 228 ucode_start_address = le32_to_cpu(hdr->ucode_start_addr); 229 ucode_size = le32_to_cpu(hdr->header.ucode_size_bytes); 230 src = (const u8 *) 231 (rdev->smc_fw->data + le32_to_cpu(hdr->header.ucode_array_offset_bytes)); 232 } else { 233 switch (rdev->family) { 234 case CHIP_TAHITI: 235 ucode_start_address = TAHITI_SMC_UCODE_START; 236 ucode_size = TAHITI_SMC_UCODE_SIZE; 237 break; 238 case CHIP_PITCAIRN: 239 ucode_start_address = PITCAIRN_SMC_UCODE_START; 240 ucode_size = PITCAIRN_SMC_UCODE_SIZE; 241 break; 242 case CHIP_VERDE: 243 ucode_start_address = VERDE_SMC_UCODE_START; 244 ucode_size = VERDE_SMC_UCODE_SIZE; 245 break; 246 case CHIP_OLAND: 247 ucode_start_address = OLAND_SMC_UCODE_START; 248 ucode_size = OLAND_SMC_UCODE_SIZE; 249 break; 250 case CHIP_HAINAN: 251 ucode_start_address = HAINAN_SMC_UCODE_START; 252 ucode_size = HAINAN_SMC_UCODE_SIZE; 253 break; 254 default: 255 DRM_ERROR("unknown asic in smc ucode loader\n"); 256 BUG(); 257 } 258 src = (const u8 *)rdev->smc_fw->data; 259 } 260 261 if (ucode_size & 3) 262 return -EINVAL; 263 264 spin_lock_irqsave(&rdev->smc_idx_lock, flags); 265 WREG32(SMC_IND_INDEX_0, ucode_start_address); 266 WREG32_P(SMC_IND_ACCESS_CNTL, AUTO_INCREMENT_IND_0, ~AUTO_INCREMENT_IND_0); 267 while (ucode_size >= 4) { 268 /* SMC address space is BE */ 269 data = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; 270 271 WREG32(SMC_IND_DATA_0, data); 272 273 src += 4; 274 ucode_size -= 4; 275 } 276 WREG32_P(SMC_IND_ACCESS_CNTL, 0, ~AUTO_INCREMENT_IND_0); 277 spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 278 279 return 0; 280} 281 282int si_read_smc_sram_dword(struct radeon_device *rdev, u32 smc_address, 283 u32 *value, u32 limit) 284{ 285 unsigned long flags; 286 int ret; 287 288 spin_lock_irqsave(&rdev->smc_idx_lock, flags); 289 ret = si_set_smc_sram_address(rdev, smc_address, limit); 290 if (ret == 0) 291 *value = RREG32(SMC_IND_DATA_0); 292 spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 293 294 return ret; 295} 296 297int si_write_smc_sram_dword(struct radeon_device *rdev, u32 smc_address, 298 u32 value, u32 limit) 299{ 300 unsigned long flags; 301 int ret; 302 303 spin_lock_irqsave(&rdev->smc_idx_lock, flags); 304 ret = si_set_smc_sram_address(rdev, smc_address, limit); 305 if (ret == 0) 306 WREG32(SMC_IND_DATA_0, value); 307 spin_unlock_irqrestore(&rdev->smc_idx_lock, flags); 308 309 return ret; 310}