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