/* * dev_icu.c - ICU (Interrupt Controler Unit) generic device API implementation. * * Authors Alain Greiner (2016) * * Copyright (c) UPMC Sorbonne Universites * * This file is part of ALMOS-MKH. * * ALMOS-MKH is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2.0 of the License. * * ALMOS-MKH is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ALMOS-MKH; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////////////////// // Extern global variables ///////////////////////////////////////////////////////////////////////////////////////// extern devices_directory_t devices_dir; // allocated in kernel_init.c extern devices_input_irq_t devices_input_irq; // allocated in kernel_init.c /////////////////////////////////// void dev_icu_init( xptr_t dev_xp, uint32_t hwi_nr, uint32_t wti_nr, uint32_t pti_nr ) { // get ICU device cluster and local pointer cxy_t dev_cxy = GET_CXY( dev_xp ); device_t * dev_ptr = (device_t *)GET_PTR( dev_xp ); if( dev_cxy != local_cxy ) { printk("\n[PANIC] %s for cluster %x must be local\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } // set ICU device extension fields dev_ptr->ext.icu.hwi_nr = hwi_nr; dev_ptr->ext.icu.wti_nr = wti_nr; dev_ptr->ext.icu.pti_nr = pti_nr; dev_ptr->ext.icu.wti_bitmap = 0; spinlock_init( &dev_ptr->ext.icu.wti_lock ); // get implementation index from ICU device descriptor uint32_t impl = dev_ptr->impl; // set field "name" in device descriptor // and call the relevant driver init function if( impl == IMPL_ICU_XCU ) { memcpy( dev_ptr->name , "ICU_XCU" , 16 ); uint32_t lid; cluster_t * cluster = LOCAL_CLUSTER; for( lid = 0 ; lid < cluster->cores_nr ; lid++ ) { soclib_xcu_init( dev_ptr , lid ); } } else { printk("\n[PANIC] %s : undefined ICU implementation for cluste %x\n", __FUNCTION__ , local_cxy ); hal_core_sleep(); } } // end dev_icu_init() ///////////////////////////////////////////////////////////////////////////////////// // This static function check the irq_type / irq_index arguments for a remote ICU. // It is called by the dev_icu_enable_irq() & dev_icu_disable_irq() functions. // returns 0 if OK / returns non zero if invalid arguments ///////////////////////////////////////////////////////////////////////////////////// static inline void dev_icu_check_irq( xptr_t icu_xp, uint32_t irq_type, uint32_t irq_index ) { // get cluster and local pointer on remote ICU cxy_t icu_cxy = GET_CXY( icu_xp ); device_t * icu_ptr = (device_t *)GET_PTR( icu_xp ); if( irq_type == HWI_TYPE ) { if( irq_index >= hal_remote_lw( XPTR( icu_cxy , &icu_ptr->ext.icu.hwi_nr ) ) ) { printk("\n[PANIC] in %s : illegal HWI index = %d for ICU in cluster %x\n", __FUNCTION__ , irq_index , icu_cxy ); hal_core_sleep(); } } if( irq_type == WTI_TYPE ) { if( irq_index >= hal_remote_lw( XPTR( icu_cxy , &icu_ptr->ext.icu.wti_nr ) ) ) { printk("\n[PANIC] in %s : illegal WTI index = %d for ICU in cluster %x\n", __FUNCTION__ , irq_index , icu_cxy ); hal_core_sleep(); } } if( irq_type == PTI_TYPE ) { if( irq_index >= hal_remote_lw( XPTR( icu_cxy , &icu_ptr->ext.icu.pti_nr ) ) ) { printk("\n[PANIC] in %s : illegal PTI index = %d for ICU in cluster %x\n", __FUNCTION__ , irq_index , icu_cxy ); hal_core_sleep(); } } } // end dev_icu check irq() //////////////////////////////////////////// void dev_icu_enable_irq( cxy_t icu_cxy, lid_t lid, uint32_t irq_type, uint32_t irq_index, xptr_t src_dev_xp ) { // get extended pointer & local pointer on remote ICU device xptr_t icu_xp = devices_dir.icu[icu_cxy]; device_t * icu_ptr = (device_t *)GET_PTR( icu_xp ); // check IRQ type and index dev_icu_check_irq( icu_xp , irq_type , irq_index ); // (1) call implementation specific ICU driver to enable IRQ if( icu_ptr->impl == IMPL_ICU_XCU ) { soclib_xcu_enable_irq( icu_xp , 1<core_tbl[lid] ) ); xptr_t core_xp = XPTR( icu_cxy , core_ptr ); core_set_irq_vector_entry( core_xp , irq_type , irq_index , src_dev_xp ); // (3) get extended pointer on source device, and // register IRQ type and index in source device descriptor cxy_t src_dev_cxy = GET_CXY( src_dev_xp ); device_t * src_dev_ptr = (device_t *)GET_PTR( src_dev_xp ); hal_remote_sw( XPTR( src_dev_cxy , &src_dev_ptr->irq_type ) , irq_type ); hal_remote_sw( XPTR( src_dev_cxy , &src_dev_ptr->irq_id ) , irq_index ); } // end dev_icu_enable_irq() ///////////////////////////////////////////// void dev_icu_disable_irq( cxy_t icu_cxy, lid_t lid, uint32_t irq_type, uint32_t irq_index ) { // get extended pointer & local pointer on remote ICU device xptr_t icu_xp = devices_dir.icu[icu_cxy]; device_t * icu_ptr = (device_t *)GET_PTR( icu_xp ); // check IRQ type and index dev_icu_check_irq( icu_xp , irq_type , irq_index ); // (1) call the implementation specific ICU driver to disable IRQ if( icu_ptr->impl == IMPL_ICU_XCU ) { soclib_xcu_disable_irq( icu_xp , 1<core_tbl[lid] ) ); xptr_t core_xp = XPTR( icu_cxy , core_ptr ); core_set_irq_vector_entry( core_xp , irq_type , irq_index , XPTR_NULL ); } // end dev_icu_disable_irq() ////////////////////////////////////////////// void dev_icu_set_period( uint32_t pti_index, uint32_t period ) { // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // check PTI index if( pti_index >= icu->ext.icu.pti_nr ) { printk("\n[PANIC] in %s : illegal PTI index = %d\n", __FUNCTION__ , pti_index ); hal_core_sleep(); } // call the implementation specific driver ICU to set period if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_set_period( icu , pti_index , period ); } } // end dev_icu_set_period() //////////////////////////////////////////// void dev_icu_ack_timer( uint32_t pti_index ) { // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // check PTI index if( pti_index >= icu->ext.icu.pti_nr ) { printk("\n[PANIC] in %s : illegal PTI index = %d\n", __FUNCTION__ , pti_index ); hal_core_sleep(); } // call the implementation specific driver ICU to acknowledge PTI IRQ if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_ack_timer( icu , pti_index ); } else { printk("\n[PANIC] in %s : undefined ICU implementation" , __FUNCTION__ ); hal_core_sleep(); } } // end dev_icu_ack_timer() //////////////////////////////////// void dev_icu_send_ipi( cxy_t cxy, lid_t lid ) { // check arguments cluster_t * cluster = LOCAL_CLUSTER; uint32_t y_width = cluster->y_width; uint32_t x_size = cluster->x_size; uint32_t y_size = cluster->y_size; uint32_t cores_nr = cluster->cores_nr; uint32_t x = cxy >> y_width; uint32_t y = cxy & ((1<= x_size) || (y >= y_size) ) { hal_core_sleep("%s : illegal cluster identifier = %x\n", __FUNCTION__ , cxy ); } if( lid >= cores_nr ) { hal_core_sleep("%s : illegal core local index = %d\n", __FUNCTION__ , lid ); } // get extended pointer on target ICU device xptr_t icu_xp = devices_dir.icu[cxy]; // get target ICU cluster and local pointer cxy_t cxy_icu = GET_CXY( icu_xp ); device_t * ptr_icu = (device_t *)GET_PTR( icu_xp ); // get driver implementation from target ICU device uint32_t impl = hal_remote_lw( XPTR( cxy_icu , &ptr_icu->impl ) ); // call the implementation specific ICU driver to send IPI if( impl == IMPL_ICU_XCU ) { soclib_xcu_send_ipi( icu_xp , lid ); } else { printk("\n[PANIC] in %s : undefined ICU implementation" , __FUNCTION__ ); hal_core_sleep(); } } // end dev_icu_send_ipi() ////////////////////////// void dev_icu_irq_handler() { uint32_t hwi_status; // HWI index + 1 / no pending HWI if 0 uint32_t wti_status; // WTI index + 1 / no pending WTI if 0 uint32_t pti_status; // PTI index + 1 / no pending PTI if 0 xptr_t src_dev_xp; // extended pointer on source device descriptor cxy_t src_dev_cxy; // source device cluster device_t * src_dev_ptr; // source device local pointer uint32_t index; // IRQ index // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // call the implementation specific ICU driver // to return highest priority pending IRQ of each type if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_get_status( icu , core->lid , &hwi_status , &wti_status , &pti_status ); } else { printk("\n[PANIC] in %s : undefined ICU implementation" , __FUNCTION__ ); hal_core_sleep(); } // analyse ICU status and handle up to 3 pending IRQ (one WTI, one HWI, one PTI) if( wti_status ) // pending WTI TODO what about IPIs ??? [AG] { index = wti_status - 1; // get extended pointer on IRQ source device src_dev_xp = core->wti_vector[index]; src_dev_cxy = GET_CXY( src_dev_xp ); src_dev_ptr = (device_t *)GET_PTR( src_dev_xp ); if( src_dev_xp == XPTR_NULL ) // strange, but not fatal => disable IRQ { printk("\n[WARNING] in %s : no handler for WTI %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); core->spurious_irqs ++; dev_icu_disable_irq( local_cxy , core->lid , WTI_TYPE , index ); } else if( src_dev_cxy != local_cxy ) // WTI must be handled in device cluster { printk("\n[PANIC] in %s : non local WTI %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); hal_core_sleep(); } else // call relevant ISR { icu_dmsg("\n[INFO] %s received WTI : index = %d for cpu %d in cluster %d\n", __FUNCTION__ , index , core->lid , local_cxy ); src_dev_ptr->isr( src_dev_ptr ); } } if( hwi_status ) // pending HWI { index = hwi_status - 1; // get pointer on IRQ source device src_dev_xp = core->wti_vector[index]; src_dev_cxy = GET_CXY( src_dev_xp ); src_dev_ptr = (device_t *)GET_PTR( src_dev_xp ); if( src_dev_xp == XPTR_NULL ) // strange, but not fatal => disable IRQ { printk("\n[WARNING] in %s : no handler for HWI %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); core->spurious_irqs ++; dev_icu_disable_irq( local_cxy , core->lid , HWI_TYPE , index ); } else if( src_dev_cxy != local_cxy ) // HWI must be handled in device cluster { printk("\n[PANIC] in %s : non local HWI %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); hal_core_sleep(); } else // call relevant ISR { icu_dmsg("\n[INFO] %s received HWI : index = %d for cpu %d in cluster %d\n", __FUNCTION__ , index , core->lid , local_cxy ); src_dev_ptr->isr( src_dev_ptr ); } } if( pti_status ) // pending PTI { index = pti_status - 1; icu_dmsg("\n[INFO] %s received PTI : index = %d for cpu %d in cluster %d\n", __FUNCTION__ , index , core->lid , local_cxy ); // acknowledge PTI dev_icu_ack_timer( index ); // execute all actions related to TICK event core_clock( core ); } } // end dev_icu_irq_handler() //////////////////////////// uint32_t dev_icu_wti_alloc() { // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // get bitmap pointer, lock, and size bitmap_t * bitmap = &icu->ext.icu.wti_bitmap; spinlock_t * lock = &icu->ext.icu.wti_lock; uint32_t size = icu->ext.icu.wti_nr; // get lock protecting WTI allocator spinlock_lock( lock ); // get first free mailbox index uint32_t index = (uint32_t)bitmap_ffc( bitmap , size ); // set bitmap entry if found if( index < size ) bitmap_set( bitmap , index ); // release lock spinlock_unlock( lock ); return index; } // end dev_icu_wti_alloc() ////////////////////////////////////////// void dev_icu_wti_release( uint32_t index ) { // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // get bitmap pointer, lock, and size bitmap_t * bitmap = &icu->ext.icu.wti_bitmap; spinlock_t * lock = &icu->ext.icu.wti_lock; uint32_t size = icu->ext.icu.wti_nr; // check index if( index >= size ) { printk("\n[PANIC] in %s : illegal WTI index = %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); hal_core_sleep(); } // get lock protecting WTI allocator spinlock_lock( lock ); // clear bitmap entry bitmap_clear( bitmap , index ); // release lock spinlock_unlock( lock ); } // end dev_icu_wti_release() /////////////////////////////////////// xptr_t dev_icu_wti_xptr( cxy_t cxy, uint32_t wti_id ) { uint32_t * ptr = NULL; // local pointer on mailbox // get pointer on local ICU device descriptor core_t * core = CURRENT_CORE; device_t * icu = core->icu; // call implementation specific ICU driver if( icu->impl == IMPL_ICU_XCU ) { ptr = soclib_xcu_wti_ptr( icu , wti_id ); } else { printk("\n[PANIC] in %s : undefined ICU implementation" , __FUNCTION__ ); hal_core_sleep(); } // return extended pointer on mailbox return XPTR( cxy , ptr ); } // end dev_icu_wti_xptr()