/* * 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 chdev_directory_t chdev_dir; // allocated in kernel_init.c extern chdev_pic_input_t chdev_pic_input; // allocated in kernel_init.c ///////////////////////////////// void dev_icu_init( chdev_t * icu, uint32_t hwi_nr, uint32_t wti_nr, uint32_t pti_nr ) { // set chdev name snprintf( icu->name , 16 , "icu_%x" , local_cxy ); // set ICU chdev extension fields icu->ext.icu.hwi_nr = hwi_nr; icu->ext.icu.wti_nr = wti_nr; icu->ext.icu.pti_nr = pti_nr; icu->ext.icu.wti_bitmap = 0; spinlock_init( &icu->ext.icu.wti_lock ); // get implementation uint32_t impl = icu->impl; // call the relevant driver init function if( impl == IMPL_ICU_XCU ) { uint32_t lid; for( lid = 0 ; lid < LOCAL_CLUSTER->cores_nr ; lid++ ) { soclib_xcu_init( icu , lid ); } } else { assert( false , __FUNCTION__ , "undefined ICU implementation" ); } } ///////////////////////////////////////////////////////////////////////////////////// // This static function check the irq_type / irq_index arguments. // It is called by the dev_icu_enable_irq() & dev_icu_disable_irq() functions. ///////////////////////////////////////////////////////////////////////////////////// static inline void dev_icu_check_irq( chdev_t * icu, uint32_t irq_type, uint32_t irq_index ) { if( irq_type == HWI_TYPE ) { if( irq_index >= icu->ext.icu.hwi_nr ) { printk("\n[PANIC] in %s : illegal HWI index = %d / max = %d\n", __FUNCTION__ , irq_index , icu->ext.icu.hwi_nr ); hal_core_sleep(); } } else if( irq_type == WTI_TYPE ) { if( irq_index >= icu->ext.icu.wti_nr ) { printk("\n[PANIC] in %s : illegal WTI index = %d / max = %d\n", __FUNCTION__ , irq_index , icu->ext.icu.wti_nr ); hal_core_sleep(); } } else // irq_type == PTI_TYPE { if( irq_index >= icu->ext.icu.pti_nr ) { printk("\n[PANIC] in %s : illegal PTI index = %d / max = %d\n", __FUNCTION__ , irq_index , icu->ext.icu.pti_nr ); hal_core_sleep(); } } } //////////////////////////////////////// void dev_icu_enable_irq( lid_t lid, uint32_t irq_type, uint32_t irq_index, chdev_t * src_chdev ) { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // check IRQ type and index dev_icu_check_irq( icu , irq_type , irq_index ); // call implementation specific ICU driver to enable IRQ if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_enable_irq( icu , 1<core_tbl[lid]; core_set_irq_vector_entry( core , irq_type , irq_index , src_chdev ); // (3) register IRQ type and index in source chdev descriptor src_chdev->irq_type = irq_type; src_chdev->irq_id = irq_index; } } ///////////////////////////////////////// void dev_icu_disable_irq( lid_t lid, uint32_t irq_type, uint32_t irq_index ) { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // check IRQ type and index dev_icu_check_irq( icu , irq_type , irq_index ); // call the implementation specific ICU driver to disable IRQ if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_disable_irq( icu , 1<core_tbl[lid]; core_set_irq_vector_entry( core , irq_type , irq_index , NULL ); } } /////////////////////////////////////// void dev_icu_get_masks( lid_t lid, uint32_t * hwi_mask, uint32_t * wti_mask, uint32_t * pti_mask ) { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_get_masks( icu , lid , hwi_mask , wti_mask , pti_mask ); } } ////////////////////////////////////////////// void dev_icu_set_period( uint32_t pti_index, uint32_t period ) { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // check PTI index assert( (pti_index < icu->ext.icu.pti_nr) , __FUNCTION__ , "illegal PTI index" ); // call the implementation specific driver ICU to set period if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_set_period( icu , pti_index , period ); } } //////////////////////////////////////////// void dev_icu_ack_timer( uint32_t pti_index ) { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // check PTI index assert( (pti_index < icu->ext.icu.pti_nr) , __FUNCTION__ , "illegal PTI index" ); // call the implementation specific driver ICU to acknowledge PTI IRQ if( icu->impl == IMPL_ICU_XCU ) { soclib_xcu_ack_timer( icu , pti_index ); } } //////////////////////////////////// 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<impl ) ); // call the implementation specific ICU driver to send IPI if( impl == IMPL_ICU_XCU ) { soclib_xcu_send_ipi( icu_xp , lid ); } } ////////////////////////// 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 chdev_t * src_chdev; // pointer on source chdev descriptor uint32_t index; // IRQ index core_t * core = CURRENT_CORE; // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // 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 ); } // analyse ICU status and handle up to 3 pending IRQ (one WTI, one HWI, one PTI) if( wti_status ) // pending WTI { index = wti_status - 1; if( index < LOCAL_CLUSTER->cores_nr ) // it is an IPI { assert( (index == core->lid) , __FUNCTION__ , "illegal IPI index" ); // TODO acknowledge WTI [AG] // TODO force scheduling [AG] } else // it is an external device { // get pointer on IRQ source chdev src_chdev = core->wti_vector[index]; if( src_chdev == 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( core->lid , WTI_TYPE , index ); } 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 ); // call ISR src_chdev->isr( src_chdev ); } } } if( hwi_status ) // pending HWI { index = hwi_status - 1; // get pointer on IRQ source chdev src_chdev = core->hwi_vector[index]; if( src_chdev == 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( core->lid , HWI_TYPE , index ); } 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 ); // call ISR src_chdev->isr( src_chdev ); } } 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 ); if( index < LOCAL_CLUSTER->cores_nr ) // its a TICK event { // TODO execute all actions related to TICK event core_clock( core ); } else { printk("\n[WARNING] in %s : no handler for PTI %d on core %d in cluster %x\n", __FUNCTION__ , index , core->lid , local_cxy ); core->spurious_irqs ++; dev_icu_disable_irq( core->lid , PTI_TYPE , index ); } } } //////////////////////////// uint32_t dev_icu_wti_alloc() { // get local pointer on local ICU chdev xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // get bitmap pointer, lock pointer, and size uint32_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; } ////////////////////////////////////////// void dev_icu_wti_release( uint32_t index ) { // get pointer on local ICU chdev descriptor xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu_ptr = (chdev_t *)GET_PTR( icu_xp ); // get bitmap pointer, lock, and size bitmap_t * bitmap = &icu_ptr->ext.icu.wti_bitmap; spinlock_t * lock = &icu_ptr->ext.icu.wti_lock; uint32_t size = icu_ptr->ext.icu.wti_nr; // check index assert( (index < size) , __FUNCTION__ , "illegal WTI index" ); // get lock protecting WTI allocator spinlock_lock( lock ); // clear bitmap entry bitmap_clear( bitmap , index ); // release lock spinlock_unlock( lock ); } ////////////////////////////////////////////// uint32_t * dev_icu_wti_ptr( uint32_t wti_id ) { uint32_t * wti_ptr = NULL; // get pointer on local ICU chdev descriptor xptr_t icu_xp = chdev_dir.icu[local_cxy]; chdev_t * icu = (chdev_t *)GET_PTR( icu_xp ); // call implementation specific ICU driver if( icu->impl == IMPL_ICU_XCU ) { wti_ptr = soclib_xcu_wti_ptr( icu , wti_id ); } return wti_ptr; }