2013-08-14 01:03:41 -04:00
/*
* Copyright 2011 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 .
*
* Authors : Alex Deucher
*/
# include <linux/firmware.h>
2019-06-08 10:02:41 +02:00
2013-08-14 01:03:41 -04:00
# include "radeon.h"
# include "cikd.h"
# include "ppsmc.h"
# include "radeon_ucode.h"
2014-01-06 22:05:34 +05:30
# include "ci_dpm.h"
2013-08-14 01:03:41 -04:00
static int ci_set_smc_sram_address ( struct radeon_device * rdev ,
u32 smc_address , u32 limit )
{
if ( smc_address & 3 )
return - EINVAL ;
if ( ( smc_address + 3 ) > limit )
return - EINVAL ;
WREG32 ( SMC_IND_INDEX_0 , smc_address ) ;
WREG32_P ( SMC_IND_ACCESS_CNTL , 0 , ~ AUTO_INCREMENT_IND_0 ) ;
return 0 ;
}
int ci_copy_bytes_to_smc ( struct radeon_device * rdev ,
u32 smc_start_address ,
const u8 * src , u32 byte_count , u32 limit )
{
2013-09-03 18:19:42 -04:00
unsigned long flags ;
2013-08-14 01:03:41 -04:00
u32 data , original_data ;
u32 addr ;
u32 extra_shift ;
2013-09-03 18:19:42 -04:00
int ret = 0 ;
2013-08-14 01:03:41 -04:00
if ( smc_start_address & 3 )
return - EINVAL ;
if ( ( smc_start_address + byte_count ) > limit )
return - EINVAL ;
addr = smc_start_address ;
2013-09-03 18:19:42 -04:00
spin_lock_irqsave ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
while ( byte_count > = 4 ) {
/* SMC address space is BE */
data = ( src [ 0 ] < < 24 ) | ( src [ 1 ] < < 16 ) | ( src [ 2 ] < < 8 ) | src [ 3 ] ;
ret = ci_set_smc_sram_address ( rdev , addr , limit ) ;
if ( ret )
2013-09-03 18:19:42 -04:00
goto done ;
2013-08-14 01:03:41 -04:00
WREG32 ( SMC_IND_DATA_0 , data ) ;
src + = 4 ;
byte_count - = 4 ;
addr + = 4 ;
}
/* RMW for the final bytes */
if ( byte_count > 0 ) {
data = 0 ;
ret = ci_set_smc_sram_address ( rdev , addr , limit ) ;
if ( ret )
2013-09-03 18:19:42 -04:00
goto done ;
2013-08-14 01:03:41 -04:00
original_data = RREG32 ( SMC_IND_DATA_0 ) ;
extra_shift = 8 * ( 4 - byte_count ) ;
while ( byte_count > 0 ) {
data = ( data < < 8 ) + * src + + ;
byte_count - - ;
}
data < < = extra_shift ;
data | = ( original_data & ~ ( ( ~ 0UL ) < < extra_shift ) ) ;
ret = ci_set_smc_sram_address ( rdev , addr , limit ) ;
if ( ret )
2013-09-03 18:19:42 -04:00
goto done ;
2013-08-14 01:03:41 -04:00
WREG32 ( SMC_IND_DATA_0 , data ) ;
}
2013-09-03 18:19:42 -04:00
done :
spin_unlock_irqrestore ( & rdev - > smc_idx_lock , flags ) ;
return ret ;
2013-08-14 01:03:41 -04:00
}
void ci_start_smc ( struct radeon_device * rdev )
{
u32 tmp = RREG32_SMC ( SMC_SYSCON_RESET_CNTL ) ;
tmp & = ~ RST_REG ;
WREG32_SMC ( SMC_SYSCON_RESET_CNTL , tmp ) ;
}
void ci_reset_smc ( struct radeon_device * rdev )
{
u32 tmp = RREG32_SMC ( SMC_SYSCON_RESET_CNTL ) ;
tmp | = RST_REG ;
WREG32_SMC ( SMC_SYSCON_RESET_CNTL , tmp ) ;
}
int ci_program_jump_on_start ( struct radeon_device * rdev )
{
2014-11-10 16:51:08 +10:00
static const u8 data [ ] = { 0xE0 , 0x00 , 0x80 , 0x40 } ;
2013-08-14 01:03:41 -04:00
return ci_copy_bytes_to_smc ( rdev , 0x0 , data , 4 , sizeof ( data ) + 1 ) ;
}
void ci_stop_smc_clock ( struct radeon_device * rdev )
{
u32 tmp = RREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 ) ;
tmp | = CK_DISABLE ;
WREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 , tmp ) ;
}
void ci_start_smc_clock ( struct radeon_device * rdev )
{
u32 tmp = RREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 ) ;
tmp & = ~ CK_DISABLE ;
WREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 , tmp ) ;
}
bool ci_is_smc_running ( struct radeon_device * rdev )
{
u32 clk = RREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 ) ;
u32 pc_c = RREG32_SMC ( SMC_PC_C ) ;
if ( ! ( clk & CK_DISABLE ) & & ( 0x20100 < = pc_c ) )
return true ;
return false ;
}
2015-01-14 16:15:46 -05:00
#if 0
2013-08-14 01:03:41 -04:00
PPSMC_Result ci_wait_for_smc_inactive ( struct radeon_device * rdev )
{
u32 tmp ;
int i ;
if ( ! ci_is_smc_running ( rdev ) )
return PPSMC_Result_OK ;
for ( i = 0 ; i < rdev - > usec_timeout ; i + + ) {
2016-03-16 12:56:45 +01:00
tmp = RREG32_SMC ( SMC_SYSCON_CLOCK_CNTL_0 ) ;
if ( ( tmp & CKEN ) = = 0 )
2013-08-14 01:03:41 -04:00
break ;
2016-03-16 12:56:45 +01:00
udelay ( 1 ) ;
}
2013-08-14 01:03:41 -04:00
return PPSMC_Result_OK ;
}
2015-01-14 16:15:46 -05:00
# endif
2013-08-14 01:03:41 -04:00
int ci_load_smc_ucode ( struct radeon_device * rdev , u32 limit )
{
2013-09-03 18:19:42 -04:00
unsigned long flags ;
2013-08-14 01:03:41 -04:00
u32 ucode_start_address ;
u32 ucode_size ;
const u8 * src ;
u32 data ;
if ( ! rdev - > smc_fw )
return - EINVAL ;
2014-06-25 19:32:36 -04:00
if ( rdev - > new_fw ) {
const struct smc_firmware_header_v1_0 * hdr =
( const struct smc_firmware_header_v1_0 * ) rdev - > smc_fw - > data ;
radeon_ucode_print_smc_hdr ( & hdr - > header ) ;
ucode_start_address = le32_to_cpu ( hdr - > ucode_start_addr ) ;
ucode_size = le32_to_cpu ( hdr - > header . ucode_size_bytes ) ;
src = ( const u8 * )
( rdev - > smc_fw - > data + le32_to_cpu ( hdr - > header . ucode_array_offset_bytes ) ) ;
} else {
switch ( rdev - > family ) {
case CHIP_BONAIRE :
ucode_start_address = BONAIRE_SMC_UCODE_START ;
ucode_size = BONAIRE_SMC_UCODE_SIZE ;
break ;
case CHIP_HAWAII :
ucode_start_address = HAWAII_SMC_UCODE_START ;
ucode_size = HAWAII_SMC_UCODE_SIZE ;
break ;
default :
DRM_ERROR ( " unknown asic in smc ucode loader \n " ) ;
BUG ( ) ;
}
src = ( const u8 * ) rdev - > smc_fw - > data ;
2013-08-14 01:03:41 -04:00
}
if ( ucode_size & 3 )
return - EINVAL ;
2013-09-03 18:19:42 -04:00
spin_lock_irqsave ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
WREG32 ( SMC_IND_INDEX_0 , ucode_start_address ) ;
WREG32_P ( SMC_IND_ACCESS_CNTL , AUTO_INCREMENT_IND_0 , ~ AUTO_INCREMENT_IND_0 ) ;
while ( ucode_size > = 4 ) {
/* SMC address space is BE */
data = ( src [ 0 ] < < 24 ) | ( src [ 1 ] < < 16 ) | ( src [ 2 ] < < 8 ) | src [ 3 ] ;
WREG32 ( SMC_IND_DATA_0 , data ) ;
src + = 4 ;
ucode_size - = 4 ;
}
WREG32_P ( SMC_IND_ACCESS_CNTL , 0 , ~ AUTO_INCREMENT_IND_0 ) ;
2013-09-03 18:19:42 -04:00
spin_unlock_irqrestore ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
return 0 ;
}
int ci_read_smc_sram_dword ( struct radeon_device * rdev ,
u32 smc_address , u32 * value , u32 limit )
{
2013-09-03 18:19:42 -04:00
unsigned long flags ;
2013-08-14 01:03:41 -04:00
int ret ;
2013-09-03 18:19:42 -04:00
spin_lock_irqsave ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
ret = ci_set_smc_sram_address ( rdev , smc_address , limit ) ;
2013-09-03 18:19:42 -04:00
if ( ret = = 0 )
* value = RREG32 ( SMC_IND_DATA_0 ) ;
spin_unlock_irqrestore ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
2013-09-03 18:19:42 -04:00
return ret ;
2013-08-14 01:03:41 -04:00
}
int ci_write_smc_sram_dword ( struct radeon_device * rdev ,
u32 smc_address , u32 value , u32 limit )
{
2013-09-03 18:19:42 -04:00
unsigned long flags ;
2013-08-14 01:03:41 -04:00
int ret ;
2013-09-03 18:19:42 -04:00
spin_lock_irqsave ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
ret = ci_set_smc_sram_address ( rdev , smc_address , limit ) ;
2013-09-03 18:19:42 -04:00
if ( ret = = 0 )
WREG32 ( SMC_IND_DATA_0 , value ) ;
spin_unlock_irqrestore ( & rdev - > smc_idx_lock , flags ) ;
2013-08-14 01:03:41 -04:00
2013-09-03 18:19:42 -04:00
return ret ;
2013-08-14 01:03:41 -04:00
}