2016-01-19 14:02:14 +08:00
/*
* Copyright 2015 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 .
*
*/
2019-06-10 00:07:57 +02:00
# include <linux/pci.h>
2016-01-19 14:02:14 +08:00
# include "amdgpu.h"
# include "amdgpu_ih.h"
2017-01-24 18:00:57 -05:00
# include "sid.h"
2016-01-19 14:02:14 +08:00
# include "si_ih.h"
static void si_ih_set_interrupt_funcs ( struct amdgpu_device * adev ) ;
static void si_ih_enable_interrupts ( struct amdgpu_device * adev )
{
u32 ih_cntl = RREG32 ( IH_CNTL ) ;
u32 ih_rb_cntl = RREG32 ( IH_RB_CNTL ) ;
ih_cntl | = ENABLE_INTR ;
ih_rb_cntl | = IH_RB_ENABLE ;
WREG32 ( IH_CNTL , ih_cntl ) ;
WREG32 ( IH_RB_CNTL , ih_rb_cntl ) ;
adev - > irq . ih . enabled = true ;
}
static void si_ih_disable_interrupts ( struct amdgpu_device * adev )
{
u32 ih_rb_cntl = RREG32 ( IH_RB_CNTL ) ;
u32 ih_cntl = RREG32 ( IH_CNTL ) ;
ih_rb_cntl & = ~ IH_RB_ENABLE ;
ih_cntl & = ~ ENABLE_INTR ;
WREG32 ( IH_RB_CNTL , ih_rb_cntl ) ;
WREG32 ( IH_CNTL , ih_cntl ) ;
WREG32 ( IH_RB_RPTR , 0 ) ;
WREG32 ( IH_RB_WPTR , 0 ) ;
adev - > irq . ih . enabled = false ;
adev - > irq . ih . rptr = 0 ;
}
static int si_ih_irq_init ( struct amdgpu_device * adev )
{
2018-09-18 14:24:49 +02:00
struct amdgpu_ih_ring * ih = & adev - > irq . ih ;
2016-01-19 14:02:14 +08:00
int rb_bufsz ;
u32 interrupt_cntl , ih_cntl , ih_rb_cntl ;
si_ih_disable_interrupts ( adev ) ;
WREG32 ( INTERRUPT_CNTL2 , adev - > irq . ih . gpu_addr > > 8 ) ;
interrupt_cntl = RREG32 ( INTERRUPT_CNTL ) ;
interrupt_cntl & = ~ IH_DUMMY_RD_OVERRIDE ;
interrupt_cntl & = ~ IH_REQ_NONSNOOP_EN ;
WREG32 ( INTERRUPT_CNTL , interrupt_cntl ) ;
WREG32 ( IH_RB_BASE , adev - > irq . ih . gpu_addr > > 8 ) ;
rb_bufsz = order_base_2 ( adev - > irq . ih . ring_size / 4 ) ;
2016-09-06 11:36:42 -04:00
ih_rb_cntl = IH_WPTR_OVERFLOW_ENABLE |
IH_WPTR_OVERFLOW_CLEAR |
( rb_bufsz < < 1 ) |
IH_WPTR_WRITEBACK_ENABLE ;
2016-01-19 14:02:14 +08:00
2018-09-18 14:24:49 +02:00
WREG32 ( IH_RB_WPTR_ADDR_LO , lower_32_bits ( ih - > wptr_addr ) ) ;
WREG32 ( IH_RB_WPTR_ADDR_HI , upper_32_bits ( ih - > wptr_addr ) & 0xFF ) ;
2016-01-19 14:02:14 +08:00
WREG32 ( IH_RB_CNTL , ih_rb_cntl ) ;
WREG32 ( IH_RB_RPTR , 0 ) ;
WREG32 ( IH_RB_WPTR , 0 ) ;
ih_cntl = MC_WRREQ_CREDIT ( 0x10 ) | MC_WR_CLEAN_CNT ( 0x10 ) | MC_VMID ( 0 ) ;
if ( adev - > irq . msi_enabled )
ih_cntl | = RPTR_REARM ;
WREG32 ( IH_CNTL , ih_cntl ) ;
pci_set_master ( adev - > pdev ) ;
si_ih_enable_interrupts ( adev ) ;
2016-09-06 11:36:42 -04:00
return 0 ;
2016-01-19 14:02:14 +08:00
}
static void si_ih_irq_disable ( struct amdgpu_device * adev )
{
si_ih_disable_interrupts ( adev ) ;
mdelay ( 1 ) ;
}
2018-09-17 16:13:49 +02:00
static u32 si_ih_get_wptr ( struct amdgpu_device * adev ,
struct amdgpu_ih_ring * ih )
2016-01-19 14:02:14 +08:00
{
u32 wptr , tmp ;
2018-09-18 14:24:49 +02:00
wptr = le32_to_cpu ( * ih - > wptr_cpu ) ;
2016-01-19 14:02:14 +08:00
if ( wptr & IH_RB_WPTR__RB_OVERFLOW_MASK ) {
wptr & = ~ IH_RB_WPTR__RB_OVERFLOW_MASK ;
dev_warn ( adev - > dev , " IH ring buffer overflow (0x%08X, 0x%08X, 0x%08X) \n " ,
2018-09-17 16:13:49 +02:00
wptr , ih - > rptr , ( wptr + 16 ) & ih - > ptr_mask ) ;
ih - > rptr = ( wptr + 16 ) & ih - > ptr_mask ;
2016-01-19 14:02:14 +08:00
tmp = RREG32 ( IH_RB_CNTL ) ;
tmp | = IH_RB_CNTL__WPTR_OVERFLOW_CLEAR_MASK ;
WREG32 ( IH_RB_CNTL , tmp ) ;
}
2018-09-17 16:13:49 +02:00
return ( wptr & ih - > ptr_mask ) ;
2016-01-19 14:02:14 +08:00
}
static void si_ih_decode_iv ( struct amdgpu_device * adev ,
2018-09-17 16:13:49 +02:00
struct amdgpu_ih_ring * ih ,
struct amdgpu_iv_entry * entry )
2016-01-19 14:02:14 +08:00
{
2018-09-17 16:13:49 +02:00
u32 ring_index = ih - > rptr > > 2 ;
2016-01-19 14:02:14 +08:00
uint32_t dw [ 4 ] ;
2018-09-17 16:13:49 +02:00
dw [ 0 ] = le32_to_cpu ( ih - > ring [ ring_index + 0 ] ) ;
dw [ 1 ] = le32_to_cpu ( ih - > ring [ ring_index + 1 ] ) ;
dw [ 2 ] = le32_to_cpu ( ih - > ring [ ring_index + 2 ] ) ;
dw [ 3 ] = le32_to_cpu ( ih - > ring [ ring_index + 3 ] ) ;
2016-01-19 14:02:14 +08:00
2018-09-17 15:29:28 +02:00
entry - > client_id = AMDGPU_IRQ_CLIENTID_LEGACY ;
2016-01-19 14:02:14 +08:00
entry - > src_id = dw [ 0 ] & 0xff ;
2016-11-29 18:02:12 -05:00
entry - > src_data [ 0 ] = dw [ 1 ] & 0xfffffff ;
2016-01-19 14:02:14 +08:00
entry - > ring_id = dw [ 2 ] & 0xff ;
2017-12-18 17:08:25 +01:00
entry - > vmid = ( dw [ 2 ] > > 8 ) & 0xff ;
2016-01-19 14:02:14 +08:00
2018-09-17 16:13:49 +02:00
ih - > rptr + = 16 ;
2016-01-19 14:02:14 +08:00
}
2018-09-17 16:13:49 +02:00
static void si_ih_set_rptr ( struct amdgpu_device * adev ,
struct amdgpu_ih_ring * ih )
2016-01-19 14:02:14 +08:00
{
2018-09-17 16:13:49 +02:00
WREG32 ( IH_RB_RPTR , ih - > rptr ) ;
2016-01-19 14:02:14 +08:00
}
static int si_ih_early_init ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
si_ih_set_interrupt_funcs ( adev ) ;
return 0 ;
}
static int si_ih_sw_init ( void * handle )
{
int r ;
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
2018-09-16 20:13:21 +02:00
r = amdgpu_ih_ring_init ( adev , & adev - > irq . ih , 64 * 1024 , false ) ;
2016-01-19 14:02:14 +08:00
if ( r )
return r ;
2016-09-06 11:36:42 -04:00
return amdgpu_irq_init ( adev ) ;
2016-01-19 14:02:14 +08:00
}
static int si_ih_sw_fini ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
amdgpu_irq_fini ( adev ) ;
2018-09-16 20:13:21 +02:00
amdgpu_ih_ring_fini ( adev , & adev - > irq . ih ) ;
2016-01-19 14:02:14 +08:00
return 0 ;
}
static int si_ih_hw_init ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
2016-09-06 11:36:42 -04:00
return si_ih_irq_init ( adev ) ;
2016-01-19 14:02:14 +08:00
}
static int si_ih_hw_fini ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
si_ih_irq_disable ( adev ) ;
return 0 ;
}
static int si_ih_suspend ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
return si_ih_hw_fini ( adev ) ;
}
static int si_ih_resume ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
return si_ih_hw_init ( adev ) ;
}
static bool si_ih_is_idle ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
u32 tmp = RREG32 ( SRBM_STATUS ) ;
if ( tmp & SRBM_STATUS__IH_BUSY_MASK )
return false ;
return true ;
}
static int si_ih_wait_for_idle ( void * handle )
{
unsigned i ;
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
for ( i = 0 ; i < adev - > usec_timeout ; i + + ) {
2016-09-06 11:36:42 -04:00
if ( si_ih_is_idle ( handle ) )
2016-01-19 14:02:14 +08:00
return 0 ;
udelay ( 1 ) ;
}
return - ETIMEDOUT ;
}
static int si_ih_soft_reset ( void * handle )
{
struct amdgpu_device * adev = ( struct amdgpu_device * ) handle ;
u32 srbm_soft_reset = 0 ;
u32 tmp = RREG32 ( SRBM_STATUS ) ;
if ( tmp & SRBM_STATUS__IH_BUSY_MASK )
srbm_soft_reset | = SRBM_SOFT_RESET__SOFT_RESET_IH_MASK ;
if ( srbm_soft_reset ) {
tmp = RREG32 ( SRBM_SOFT_RESET ) ;
tmp | = srbm_soft_reset ;
dev_info ( adev - > dev , " SRBM_SOFT_RESET=0x%08X \n " , tmp ) ;
WREG32 ( SRBM_SOFT_RESET , tmp ) ;
tmp = RREG32 ( SRBM_SOFT_RESET ) ;
udelay ( 50 ) ;
tmp & = ~ srbm_soft_reset ;
WREG32 ( SRBM_SOFT_RESET , tmp ) ;
tmp = RREG32 ( SRBM_SOFT_RESET ) ;
udelay ( 50 ) ;
}
return 0 ;
}
static int si_ih_set_clockgating_state ( void * handle ,
enum amd_clockgating_state state )
{
return 0 ;
}
static int si_ih_set_powergating_state ( void * handle ,
enum amd_powergating_state state )
{
return 0 ;
}
2016-10-13 17:41:13 -04:00
static const struct amd_ip_funcs si_ih_ip_funcs = {
2016-01-19 14:02:14 +08:00
. name = " si_ih " ,
. early_init = si_ih_early_init ,
. late_init = NULL ,
. sw_init = si_ih_sw_init ,
. sw_fini = si_ih_sw_fini ,
. hw_init = si_ih_hw_init ,
. hw_fini = si_ih_hw_fini ,
. suspend = si_ih_suspend ,
. resume = si_ih_resume ,
. is_idle = si_ih_is_idle ,
. wait_for_idle = si_ih_wait_for_idle ,
. soft_reset = si_ih_soft_reset ,
. set_clockgating_state = si_ih_set_clockgating_state ,
. set_powergating_state = si_ih_set_powergating_state ,
} ;
static const struct amdgpu_ih_funcs si_ih_funcs = {
. get_wptr = si_ih_get_wptr ,
. decode_iv = si_ih_decode_iv ,
. set_rptr = si_ih_set_rptr
} ;
static void si_ih_set_interrupt_funcs ( struct amdgpu_device * adev )
{
2018-09-17 15:41:45 +02:00
adev - > irq . ih_funcs = & si_ih_funcs ;
2016-01-19 14:02:14 +08:00
}
2016-10-13 17:41:13 -04:00
const struct amdgpu_ip_block_version si_ih_ip_block =
{
. type = AMD_IP_BLOCK_TYPE_IH ,
. major = 1 ,
. minor = 0 ,
. rev = 0 ,
. funcs = & si_ih_ip_funcs ,
} ;