/* * soclib_hba.c - soclib AHCI block device driver implementation. * * Author 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 ////////////////////////////////////////////////////////////////////////////////// // SOCLIB_HBA specific global variables ////////////////////////////////////////////////////////////////////////////////// // command list : up to 32 commands __attribute__((section(".kdata"))) hba_cmd_desc_t hba_cmd_list[32] __attribute__((aligned(0x40))); // command tables array : one command table per entry in command list __attribute__((section(".kdata"))) hba_cmd_table_t hba_cmd_table[32] __attribute__((aligned(0x40))); // extended pointer on the owner thread, for each slot __attribute__((section(".kdata"))) xptr_t hba_owner_thread[32]; // bit vector of active slots __attribute__((section(".kdata"))) uint32_t hba_active_slots; // spinlock protecting the command slot allocator __attribute__((section(".kdata"))) spinlock_t hba_lock; /////////////////////////////////////// void soclib_hba_init( chdev_t * chdev ) { // get hardware device base address xptr_t hba_xp = chdev->base; // get hardware device cluster and local pointer cxy_t hba_cxy = GET_CXY( hba_xp ); uint32_t * hba_ptr = (uint32_t *)GET_PTR( hba_xp ); // get block_size and block_count uint32_t block_size = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_BLOCK_SIZE_REG ) ); uint32_t block_count = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_BLOCK_COUNT_REG ) ); // set device descriptor extension chdev->ext.ioc.size = block_size; chdev->ext.ioc.count = block_count; // activate HBA interrupts hal_remote_sw( XPTR( hba_cxy , hba_ptr + HBA_PXIE_REG ) , 0x1 ); // reset SOCLIB_HBA driver global variable hba_active_slots = 0; } // end soclib_hba_init() ////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_hba_cmd( xptr_t th_xp ) { uint32_t cmd_type; // IOC_READ / IOC_WRITE / IOC_SYNC_READ uint32_t lba; // lba : command argument uint32_t count; // count : command argument xptr_t buf_xp; // buffer : command argument xptr_t dev_xp; // device : command argument uint32_t cmd_id; // current slot index in command bit_vector hba_cmd_desc_t * cmd_desc; // command descriptor pointer hba_cmd_table_t * cmd_table; // command table pointer bool_t found; uint32_t iter; // get client thread cluster and local pointer cxy_t th_cxy = GET_CXY( th_xp ); thread_t * th_ptr = (thread_t *)GET_PTR( th_xp ); // get command arguments and extended pointer on IOC device cmd_type = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.type ) ); lba = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.lba ) ); count = hal_remote_lw ( XPTR( th_cxy , &th_ptr->command.ioc.count ) ); buf_xp = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->command.ioc.buf_xp ) ); dev_xp = (xptr_t)hal_remote_lwd( XPTR( th_cxy , &th_ptr->command.ioc.dev_xp ) ); // get IOC device cluster and local pointer cxy_t dev_cxy = GET_CXY( dev_xp ); chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); // get extended pointer on SOCLIB-HBA peripheral xptr_t hba_xp = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->base ) ); // get SOCLIB_HBA device cluster and local pointer cxy_t hba_cxy = GET_CXY( hba_xp ); uint32_t * hba_ptr = (uint32_t *)GET_PTR( hba_xp ); // try to register the I/O operation in a free slot // returns if success, deschedule if no slot available // we do not need a lock to access the slot allocator, // because the driver is only called by the server thread. while( 1 ) { // try to find a free slot in the 32 slots command list cmd_id = 0; found = false; for ( iter = 0 ; iter < 32 ; iter++ ) { if( (hba_active_slots & (1<buffer.dba = (uint32_t)(buf_xp); cmd_table->buffer.dbau = (uint32_t)(buf_xp >> 32); cmd_table->buffer.dbc = count * 512; // initialize command table header cmd_table->header.lba0 = (char)lba; cmd_table->header.lba1 = (char)(lba>>8); cmd_table->header.lba2 = (char)(lba>>16); cmd_table->header.lba3 = (char)(lba>>24); cmd_table->header.lba4 = 0; cmd_table->header.lba5 = 0; // initialise command descriptor cmd_desc->prdtl[0] = 1; cmd_desc->prdtl[1] = 0; if( cmd_type == IOC_WRITE ) cmd_desc->flag[0] = 0x40; else cmd_desc->flag[0] = 0x00; #if USE_IOB // software L2/L3 cache coherence dev_mmc_sync( cmd_table , sizeof(hba_cmd_table_t) ); dev_mmc_sync( cmd_desc , sizeof(hba_cmd_desc_t) ); #endif // end software L2/L3 cache coherence // set hba_owner_thread[slot] hba_owner_thread[cmd_id] = th_xp; // register slot in bit_vector hba_active_slots |= 1<> 30; fault_id = (pxis & 0x1F000000) >> 24; if( (pxci & (1<command.ioc.error ) , 1 ); } else { hal_remote_sw( XPTR( th_cxy , &th_ptr->command.ioc.error ) , 0 ); } // exit while break; } } } else // descheduling + IRQ { thread_block( CURRENT_THREAD , THREAD_BLOCKED_DEV_ISR ); sched_yield(); } } // end soclib_hba_cmd() ///////////////////////////////////////////////////////////////// void __attribute__ ((noinline)) soclib_hba_isr( chdev_t * chdev ) { // get extended pointer on client thread xptr_t root = XPTR( local_cxy , &chdev->wait_root ); xptr_t client_xp = XLIST_FIRST_ELEMENT( root , thread_t , wait_list ); // get client thread cluster and local pointer cxy_t client_cxy = GET_CXY( client_xp ); thread_t * client_ptr = (thread_t *)GET_PTR( client_xp ); // get SOCLIB_HBA device cluster and local pointer cxy_t hba_cxy = GET_CXY( chdev->base ); uint32_t * hba_ptr = (uint32_t *)GET_PTR( chdev->base ); // get HBA_PXIS_REG and HBA_PXCI_REG current values uint32_t current_pxis = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_PXIS_REG ) ); uint32_t current_pxci = hal_remote_lw( XPTR( hba_cxy , hba_ptr + HBA_PXCI_REG ) ); uint32_t error = (current_pxis & 0x40000000) >> 30; uint32_t fault_id = (current_pxis & 0x1F000000) >> 24; uint32_t iter; // loop on active commands to signal one or several completed I/O operations for( iter = 0 ; iter < 32 ; iter++ ) { if ( ( (hba_active_slots & (1<command.ioc.error ) , 1 ); } else { hal_remote_sw( XPTR( client_cxy , &client_ptr->command.ioc.error ) , 0 ); } // unblock client thread thread_unblock( client_xp , THREAD_BLOCKED_IO ); ioc_dmsg("INFO in %s : thread %x at cycle %d\n", __FUNCTION__ , hal_remote_lw( XPTR( client_cxy , &client_ptr->trdid ) ) , hal_time_stamp() ); } } // reset HBA_PXIS_REG hal_remote_sw( XPTR( hba_cxy , hba_ptr + HBA_PXIS_REG ) , 0 ); } // end soclib_hba_isr()