2019-02-16 00:39:17 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
2022-06-26 18:20:03 +03:00
* Copyright 2016 - 2022 HabanaLabs , Ltd .
2019-02-16 00:39:17 +02:00
* All Rights Reserved .
*/
# include "habanalabs.h"
2019-02-16 00:39:18 +02:00
# include <linux/slab.h>
/**
2020-06-26 14:05:24 +01:00
* struct hl_eqe_work - This structure is used to schedule work of EQ
2020-08-15 16:28:10 +03:00
* entry and cpucp_reset event
2019-02-16 00:39:18 +02:00
*
2020-07-01 09:58:38 +01:00
* @ eq_work : workqueue object to run when EQ entry is received
* @ hdev : pointer to device structure
* @ eq_entry : copy of the EQ entry
2019-02-16 00:39:18 +02:00
*/
struct hl_eqe_work {
struct work_struct eq_work ;
struct hl_device * hdev ;
struct hl_eq_entry eq_entry ;
} ;
2019-02-16 00:39:17 +02:00
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:17 +02:00
* hl_cq_inc_ptr - increment ci or pi of cq
*
* @ ptr : the current ci or pi value of the completion queue
*
* Increment ptr by 1. If it reaches the number of completion queue
* entries , set it to 0
*/
inline u32 hl_cq_inc_ptr ( u32 ptr )
{
ptr + + ;
if ( unlikely ( ptr = = HL_CQ_LENGTH ) )
ptr = 0 ;
return ptr ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:18 +02:00
* hl_eq_inc_ptr - increment ci of eq
*
* @ ptr : the current ci value of the event queue
*
* Increment ptr by 1. If it reaches the number of event queue
* entries , set it to 0
*/
2021-02-16 21:49:30 +02:00
static inline u32 hl_eq_inc_ptr ( u32 ptr )
2019-02-16 00:39:18 +02:00
{
ptr + + ;
if ( unlikely ( ptr = = HL_EQ_LENGTH ) )
ptr = 0 ;
return ptr ;
}
static void irq_handle_eqe ( struct work_struct * work )
{
struct hl_eqe_work * eqe_work = container_of ( work , struct hl_eqe_work ,
eq_work ) ;
struct hl_device * hdev = eqe_work - > hdev ;
hdev - > asic_funcs - > handle_eqe ( hdev , & eqe_work - > eq_entry ) ;
kfree ( eqe_work ) ;
}
2022-06-27 15:05:28 +03:00
/**
* job_finish - queue job finish work
*
* @ hdev : pointer to device structure
* @ cs_seq : command submission sequence
* @ cq : completion queue
*
*/
static void job_finish ( struct hl_device * hdev , u32 cs_seq , struct hl_cq * cq )
{
struct hl_hw_queue * queue ;
struct hl_cs_job * job ;
queue = & hdev - > kernel_queues [ cq - > hw_queue_id ] ;
job = queue - > shadow_queue [ hl_pi_2_offset ( cs_seq ) ] ;
queue_work ( hdev - > cq_wq [ cq - > cq_idx ] , & job - > finish_work ) ;
atomic_inc ( & queue - > ci ) ;
}
/**
* cs_finish - queue all cs jobs finish work
*
* @ hdev : pointer to device structure
* @ cs_seq : command submission sequence
*
*/
static void cs_finish ( struct hl_device * hdev , u16 cs_seq )
{
struct asic_fixed_properties * prop = & hdev - > asic_prop ;
struct hl_hw_queue * queue ;
struct hl_cs * cs ;
struct hl_cs_job * job ;
cs = hdev - > shadow_cs_queue [ cs_seq & ( prop - > max_pending_cs - 1 ) ] ;
if ( ! cs ) {
dev_warn ( hdev - > dev ,
" No pointer to CS in shadow array at index %d \n " ,
cs_seq ) ;
return ;
}
list_for_each_entry ( job , & cs - > job_list , cs_node ) {
queue = & hdev - > kernel_queues [ job - > hw_queue_id ] ;
atomic_inc ( & queue - > ci ) ;
}
queue_work ( hdev - > cs_cmplt_wq , & cs - > finish_work ) ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:17 +02:00
* hl_irq_handler_cq - irq handler for completion queue
*
* @ irq : irq number
* @ arg : pointer to completion queue structure
*
*/
irqreturn_t hl_irq_handler_cq ( int irq , void * arg )
{
struct hl_cq * cq = arg ;
struct hl_device * hdev = cq - > hdev ;
2022-06-27 15:05:28 +03:00
bool shadow_index_valid , entry_ready ;
2019-02-16 00:39:17 +02:00
u16 shadow_index ;
2019-08-01 23:22:20 +00:00
struct hl_cq_entry * cq_entry , * cq_base ;
2019-02-16 00:39:17 +02:00
if ( hdev - > disabled ) {
dev_dbg ( hdev - > dev ,
" Device disabled but received IRQ %d for CQ %d \n " ,
irq , cq - > hw_queue_id ) ;
return IRQ_HANDLED ;
}
2020-10-26 17:08:06 +01:00
cq_base = cq - > kernel_address ;
2019-02-16 00:39:17 +02:00
while ( 1 ) {
2022-06-27 15:05:28 +03:00
cq_entry = ( struct hl_cq_entry * ) & cq_base [ cq - > ci ] ;
2019-02-16 00:39:17 +02:00
2022-06-27 15:05:28 +03:00
entry_ready = ! ! FIELD_GET ( CQ_ENTRY_READY_MASK ,
le32_to_cpu ( cq_entry - > data ) ) ;
2019-02-16 00:39:17 +02:00
if ( ! entry_ready )
break ;
2019-08-01 23:22:20 +00:00
/* Make sure we read CQ entry contents after we've
2019-02-16 00:39:17 +02:00
* checked the ownership bit .
*/
dma_rmb ( ) ;
2022-06-27 15:05:28 +03:00
shadow_index_valid =
! ! FIELD_GET ( CQ_ENTRY_SHADOW_INDEX_VALID_MASK ,
le32_to_cpu ( cq_entry - > data ) ) ;
2019-02-16 00:39:17 +02:00
2022-06-27 15:05:28 +03:00
shadow_index = FIELD_GET ( CQ_ENTRY_SHADOW_INDEX_MASK ,
le32_to_cpu ( cq_entry - > data ) ) ;
2019-02-16 00:39:17 +02:00
2022-06-27 15:05:28 +03:00
/*
* CQ interrupt handler has 2 modes of operation :
* 1. Interrupt per CS completion : ( Single CQ for all queues )
* CQ entry represents a completed CS
*
* 2. Interrupt per CS job completion in queue : ( CQ per queue )
* CQ entry represents a completed job in a certain queue
*/
if ( shadow_index_valid & & ! hdev - > disabled ) {
if ( hdev - > asic_prop . completion_mode = =
HL_COMPLETION_MODE_CS )
cs_finish ( hdev , shadow_index ) ;
else
job_finish ( hdev , shadow_index , cq ) ;
2019-02-16 00:39:17 +02:00
}
/* Clear CQ entry ready bit */
2019-08-01 23:22:20 +00:00
cq_entry - > data = cpu_to_le32 ( le32_to_cpu ( cq_entry - > data ) &
~ CQ_ENTRY_READY_MASK ) ;
2019-02-16 00:39:17 +02:00
cq - > ci = hl_cq_inc_ptr ( cq - > ci ) ;
/* Increment free slots */
atomic_inc ( & cq - > free_slots_cnt ) ;
}
return IRQ_HANDLED ;
}
2021-12-23 13:24:34 +02:00
/*
* hl_ts_free_objects - handler of the free objects workqueue .
* This function should put refcount to objects that the registration node
* took refcount to them .
* @ work : workqueue object pointer
*/
static void hl_ts_free_objects ( struct work_struct * work )
{
struct timestamp_reg_work_obj * job =
container_of ( work , struct timestamp_reg_work_obj , free_obj ) ;
struct timestamp_reg_free_node * free_obj , * temp_free_obj ;
struct list_head * free_list_head = job - > free_obj_head ;
struct hl_device * hdev = job - > hdev ;
list_for_each_entry_safe ( free_obj , temp_free_obj , free_list_head , free_objects_node ) {
2022-03-20 16:15:01 +02:00
dev_dbg ( hdev - > dev , " About to put refcount to buf (%p) cq_cb(%p) \n " ,
free_obj - > buf ,
2021-12-23 13:24:34 +02:00
free_obj - > cq_cb ) ;
2022-03-20 16:15:01 +02:00
hl_mmap_mem_buf_put ( free_obj - > buf ) ;
2021-12-23 13:24:34 +02:00
hl_cb_put ( free_obj - > cq_cb ) ;
kfree ( free_obj ) ;
}
kfree ( free_list_head ) ;
kfree ( job ) ;
}
/*
* This function called with spin_lock of wait_list_lock taken
* This function will set timestamp and delete the registration node from the
* wait_list_lock .
* and since we ' re protected with spin_lock here , so we cannot just put the refcount
* for the objects here , since the release function may be called and it ' s also a long
* logic ( which might sleep also ) that cannot be handled in irq context .
* so here we ' ll be filling a list with nodes of " put " jobs and then will send this
* list to a dedicated workqueue to do the actual put .
*/
2022-02-02 10:32:53 +02:00
static int handle_registration_node ( struct hl_device * hdev , struct hl_user_pending_interrupt * pend ,
2021-12-23 13:24:34 +02:00
struct list_head * * free_list )
{
struct timestamp_reg_free_node * free_node ;
u64 timestamp ;
if ( ! ( * free_list ) ) {
/* Alloc/Init the timestamp registration free objects list */
* free_list = kmalloc ( sizeof ( struct list_head ) , GFP_ATOMIC ) ;
if ( ! ( * free_list ) )
return - ENOMEM ;
INIT_LIST_HEAD ( * free_list ) ;
}
free_node = kmalloc ( sizeof ( * free_node ) , GFP_ATOMIC ) ;
if ( ! free_node )
return - ENOMEM ;
timestamp = ktime_get_ns ( ) ;
* pend - > ts_reg_info . timestamp_kernel_addr = timestamp ;
dev_dbg ( hdev - > dev , " Timestamp is set to ts cb address (%p), ts: 0x%llx \n " ,
pend - > ts_reg_info . timestamp_kernel_addr ,
* ( u64 * ) pend - > ts_reg_info . timestamp_kernel_addr ) ;
list_del ( & pend - > wait_list_node ) ;
/* Mark kernel CB node as free */
pend - > ts_reg_info . in_use = 0 ;
/* Putting the refcount for ts_buff and cq_cb objects will be handled
* in workqueue context , just add job to free_list .
*/
2022-03-20 16:15:01 +02:00
free_node - > buf = pend - > ts_reg_info . buf ;
2021-12-23 13:24:34 +02:00
free_node - > cq_cb = pend - > ts_reg_info . cq_cb ;
list_add ( & free_node - > free_objects_node , * free_list ) ;
return 0 ;
}
2022-06-28 21:05:28 +03:00
static void handle_user_interrupt ( struct hl_device * hdev , struct hl_user_interrupt * intr )
2021-01-12 18:37:19 +02:00
{
2021-12-23 13:24:34 +02:00
struct hl_user_pending_interrupt * pend , * temp_pend ;
struct list_head * ts_reg_free_list_head = NULL ;
struct timestamp_reg_work_obj * job ;
bool reg_node_handle_fail = false ;
2021-10-06 11:58:02 +03:00
ktime_t now = ktime_get ( ) ;
2021-12-23 13:24:34 +02:00
int rc ;
/* For registration nodes:
* As part of handling the registration nodes , we should put refcount to
* some objects . the problem is that we cannot do that under spinlock
* or in irq handler context at all ( since release functions are long and
* might sleep ) , so we will need to handle that part in workqueue context .
* To avoid handling kmalloc failure which compels us rolling back actions
* and move nodes hanged on the free list back to the interrupt wait list
* we always alloc the job of the WQ at the beginning .
*/
job = kmalloc ( sizeof ( * job ) , GFP_ATOMIC ) ;
if ( ! job )
return ;
2021-01-12 18:37:19 +02:00
2022-06-28 21:05:28 +03:00
spin_lock ( & intr - > wait_list_lock ) ;
list_for_each_entry_safe ( pend , temp_pend , & intr - > wait_list_head , wait_list_node ) {
2021-12-23 13:24:34 +02:00
if ( ( pend - > cq_kernel_addr & & * ( pend - > cq_kernel_addr ) > = pend - > cq_target_value ) | |
2021-11-02 11:34:18 +02:00
! pend - > cq_kernel_addr ) {
2022-03-20 16:15:01 +02:00
if ( pend - > ts_reg_info . buf ) {
2021-12-23 13:24:34 +02:00
if ( ! reg_node_handle_fail ) {
rc = handle_registration_node ( hdev , pend ,
& ts_reg_free_list_head ) ;
if ( rc )
reg_node_handle_fail = true ;
}
} else {
/* Handle wait target value node */
pend - > fence . timestamp = now ;
complete_all ( & pend - > fence . completion ) ;
}
2021-11-02 11:34:18 +02:00
}
2021-09-23 17:40:14 +03:00
}
2022-06-28 21:05:28 +03:00
spin_unlock ( & intr - > wait_list_lock ) ;
2021-12-23 13:24:34 +02:00
if ( ts_reg_free_list_head ) {
INIT_WORK ( & job - > free_obj , hl_ts_free_objects ) ;
job - > free_obj_head = ts_reg_free_list_head ;
job - > hdev = hdev ;
queue_work ( hdev - > ts_free_obj_wq , & job - > free_obj ) ;
} else {
kfree ( job ) ;
}
2021-01-12 18:37:19 +02:00
}
2021-01-12 14:43:09 +02:00
/**
2022-06-26 18:20:03 +03:00
* hl_irq_handler_user_interrupt - irq handler for user interrupts
2021-01-12 14:43:09 +02:00
*
* @ irq : irq number
* @ arg : pointer to user interrupt structure
*
*/
2022-06-26 18:20:03 +03:00
irqreturn_t hl_irq_handler_user_interrupt ( int irq , void * arg )
2021-01-12 14:43:09 +02:00
{
2022-06-26 18:20:03 +03:00
struct hl_user_interrupt * user_int = arg ;
struct hl_device * hdev = user_int - > hdev ;
2021-01-12 14:43:09 +02:00
2022-06-28 18:34:58 +03:00
if ( user_int - > is_decoder )
handle_user_interrupt ( hdev , & hdev - > common_decoder_interrupt ) ;
else
2022-06-28 21:05:28 +03:00
handle_user_interrupt ( hdev , & hdev - > common_user_cq_interrupt ) ;
2021-01-12 18:37:19 +02:00
2022-06-26 18:20:03 +03:00
/* Handle user cq or decoder interrupts registered on this specific irq */
2022-06-28 21:05:28 +03:00
handle_user_interrupt ( hdev , user_int ) ;
2021-01-12 14:43:09 +02:00
return IRQ_HANDLED ;
}
/**
* hl_irq_handler_default - default irq handler
*
* @ irq : irq number
* @ arg : pointer to user interrupt structure
*
*/
irqreturn_t hl_irq_handler_default ( int irq , void * arg )
{
struct hl_user_interrupt * user_interrupt = arg ;
struct hl_device * hdev = user_interrupt - > hdev ;
u32 interrupt_id = user_interrupt - > interrupt_id ;
2022-06-26 18:20:03 +03:00
dev_err ( hdev - > dev , " got invalid user interrupt %u " , interrupt_id ) ;
2021-01-12 14:43:09 +02:00
return IRQ_HANDLED ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:18 +02:00
* hl_irq_handler_eq - irq handler for event queue
*
* @ irq : irq number
* @ arg : pointer to event queue structure
*
*/
irqreturn_t hl_irq_handler_eq ( int irq , void * arg )
{
struct hl_eq * eq = arg ;
struct hl_device * hdev = eq - > hdev ;
struct hl_eq_entry * eq_entry ;
struct hl_eq_entry * eq_base ;
struct hl_eqe_work * handle_eqe_work ;
2021-05-19 14:52:14 +03:00
bool entry_ready ;
u32 cur_eqe ;
u16 cur_eqe_index ;
2019-02-16 00:39:18 +02:00
2020-10-26 17:08:06 +01:00
eq_base = eq - > kernel_address ;
2019-02-16 00:39:18 +02:00
while ( 1 ) {
2021-05-19 14:52:14 +03:00
cur_eqe = le32_to_cpu ( eq_base [ eq - > ci ] . hdr . ctl ) ;
entry_ready = ! ! FIELD_GET ( EQ_CTL_READY_MASK , cur_eqe ) ;
2019-02-16 00:39:18 +02:00
if ( ! entry_ready )
break ;
2021-05-19 14:52:14 +03:00
cur_eqe_index = FIELD_GET ( EQ_CTL_INDEX_MASK , cur_eqe ) ;
if ( ( hdev - > event_queue . check_eqe_index ) & &
( ( ( eq - > prev_eqe_index + 1 ) & EQ_CTL_INDEX_MASK )
! = cur_eqe_index ) ) {
dev_dbg ( hdev - > dev ,
" EQE 0x%x in queue is ready but index does not match %d!=%d " ,
eq_base [ eq - > ci ] . hdr . ctl ,
( ( eq - > prev_eqe_index + 1 ) & EQ_CTL_INDEX_MASK ) ,
cur_eqe_index ) ;
break ;
}
eq - > prev_eqe_index + + ;
2019-02-16 00:39:18 +02:00
eq_entry = & eq_base [ eq - > ci ] ;
/*
* Make sure we read EQ entry contents after we ' ve
* checked the ownership bit .
*/
dma_rmb ( ) ;
2022-07-07 12:00:24 +03:00
if ( hdev - > disabled & & ! hdev - > reset_info . in_compute_reset ) {
2021-11-18 08:46:15 +02:00
dev_warn ( hdev - > dev , " Device disabled but received an EQ event \n " ) ;
2019-02-16 00:39:18 +02:00
goto skip_irq ;
}
handle_eqe_work = kmalloc ( sizeof ( * handle_eqe_work ) , GFP_ATOMIC ) ;
if ( handle_eqe_work ) {
INIT_WORK ( & handle_eqe_work - > eq_work , irq_handle_eqe ) ;
handle_eqe_work - > hdev = hdev ;
memcpy ( & handle_eqe_work - > eq_entry , eq_entry ,
sizeof ( * eq_entry ) ) ;
queue_work ( hdev - > eq_wq , & handle_eqe_work - > eq_work ) ;
}
skip_irq :
/* Clear EQ entry ready bit */
2019-02-28 10:46:24 +02:00
eq_entry - > hdr . ctl =
2019-08-08 17:05:45 +03:00
cpu_to_le32 ( le32_to_cpu ( eq_entry - > hdr . ctl ) &
2019-02-28 10:46:24 +02:00
~ EQ_CTL_READY_MASK ) ;
2019-02-16 00:39:18 +02:00
eq - > ci = hl_eq_inc_ptr ( eq - > ci ) ;
hdev - > asic_funcs - > update_eq_ci ( hdev , eq - > ci ) ;
}
return IRQ_HANDLED ;
}
2022-06-26 18:20:03 +03:00
/**
* hl_irq_handler_dec_abnrm - Decoder error interrupt handler
* @ irq : IRQ number
* @ arg : pointer to decoder structure .
*/
irqreturn_t hl_irq_handler_dec_abnrm ( int irq , void * arg )
{
struct hl_dec * dec = arg ;
schedule_work ( & dec - > completion_abnrm_work ) ;
return IRQ_HANDLED ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:17 +02:00
* hl_cq_init - main initialization function for an cq object
*
* @ hdev : pointer to device structure
* @ q : pointer to cq structure
* @ hw_queue_id : The H / W queue ID this completion queue belongs to
2022-06-26 18:20:03 +03:00
* HL_INVALID_QUEUE if cq is not attached to any specific queue
2019-02-16 00:39:17 +02:00
*
* Allocate dma - able memory for the completion queue and initialize fields
* Returns 0 on success
*/
int hl_cq_init ( struct hl_device * hdev , struct hl_cq * q , u32 hw_queue_id )
{
void * p ;
2022-06-12 15:00:29 +03:00
p = hl_asic_dma_alloc_coherent ( hdev , HL_CQ_SIZE_IN_BYTES , & q - > bus_address ,
GFP_KERNEL | __GFP_ZERO ) ;
2019-02-16 00:39:17 +02:00
if ( ! p )
return - ENOMEM ;
q - > hdev = hdev ;
2020-10-26 17:08:06 +01:00
q - > kernel_address = p ;
2019-02-16 00:39:17 +02:00
q - > hw_queue_id = hw_queue_id ;
q - > ci = 0 ;
q - > pi = 0 ;
atomic_set ( & q - > free_slots_cnt , HL_CQ_LENGTH ) ;
return 0 ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:17 +02:00
* hl_cq_fini - destroy completion queue
*
* @ hdev : pointer to device structure
* @ q : pointer to cq structure
*
* Free the completion queue memory
*/
void hl_cq_fini ( struct hl_device * hdev , struct hl_cq * q )
{
2022-06-12 15:00:29 +03:00
hl_asic_dma_free_coherent ( hdev , HL_CQ_SIZE_IN_BYTES , q - > kernel_address , q - > bus_address ) ;
2019-02-16 00:39:17 +02:00
}
2019-02-16 00:39:18 +02:00
2019-02-16 00:39:20 +02:00
void hl_cq_reset ( struct hl_device * hdev , struct hl_cq * q )
{
q - > ci = 0 ;
q - > pi = 0 ;
atomic_set ( & q - > free_slots_cnt , HL_CQ_LENGTH ) ;
/*
* It ' s not enough to just reset the PI / CI because the H / W may have
* written valid completion entries before it was halted and therefore
* we need to clean the actual queues so we won ' t process old entries
* when the device is operational again
*/
2020-10-26 17:08:06 +01:00
memset ( q - > kernel_address , 0 , HL_CQ_SIZE_IN_BYTES ) ;
2019-02-16 00:39:20 +02:00
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:18 +02:00
* hl_eq_init - main initialization function for an event queue object
*
* @ hdev : pointer to device structure
* @ q : pointer to eq structure
*
* Allocate dma - able memory for the event queue and initialize fields
* Returns 0 on success
*/
int hl_eq_init ( struct hl_device * hdev , struct hl_eq * q )
{
void * p ;
2022-06-12 15:00:29 +03:00
p = hl_cpu_accessible_dma_pool_alloc ( hdev , HL_EQ_SIZE_IN_BYTES , & q - > bus_address ) ;
2019-02-16 00:39:18 +02:00
if ( ! p )
return - ENOMEM ;
q - > hdev = hdev ;
2020-10-26 17:08:06 +01:00
q - > kernel_address = p ;
2019-02-16 00:39:18 +02:00
q - > ci = 0 ;
2021-05-19 14:52:14 +03:00
q - > prev_eqe_index = 0 ;
2019-02-16 00:39:18 +02:00
return 0 ;
}
2020-07-01 09:58:38 +01:00
/**
2019-02-16 00:39:18 +02:00
* hl_eq_fini - destroy event queue
*
* @ hdev : pointer to device structure
* @ q : pointer to eq structure
*
* Free the event queue memory
*/
void hl_eq_fini ( struct hl_device * hdev , struct hl_eq * q )
{
flush_workqueue ( hdev - > eq_wq ) ;
2022-06-12 15:00:29 +03:00
hl_cpu_accessible_dma_pool_free ( hdev , HL_EQ_SIZE_IN_BYTES , q - > kernel_address ) ;
2019-02-16 00:39:18 +02:00
}
2019-02-16 00:39:20 +02:00
void hl_eq_reset ( struct hl_device * hdev , struct hl_eq * q )
{
q - > ci = 0 ;
2021-05-19 14:52:14 +03:00
q - > prev_eqe_index = 0 ;
2019-02-16 00:39:20 +02:00
/*
* It ' s not enough to just reset the PI / CI because the H / W may have
* written valid completion entries before it was halted and therefore
* we need to clean the actual queues so we won ' t process old entries
* when the device is operational again
*/
2020-10-26 17:08:06 +01:00
memset ( q - > kernel_address , 0 , HL_EQ_SIZE_IN_BYTES ) ;
2019-02-16 00:39:20 +02:00
}