/////////////////////////////////////////////////////////////////////////////////// // File : ioc_driver.c // Date : 23/05/2013 // Author : alain greiner // Maintainer : cesar fuguet // Copyright (c) UPMC-LIP6 /////////////////////////////////////////////////////////////////////////////////// // Implementation notes: // 1) In order to share the code, the two _ioc_read() and _ioc_write() functions // call the same _ioc_access() function, and this function call the selected // physical driver (BDV / HBA / SPI / RDK). // 2) The IOMMU is not supported yet, but the method is the following: // A fixed size 2 Mbytes vseg is allocated to the IOC peripheral, in the I/O // virtual space, and the user buffer is dynamically remapped to one single // big page in the IOMMU page table. // The user buffer is unmapped by the _ioc_completed() function when // the transfer is completed. /////////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include #include #include #include #include #include #include #if !defined( SEG_IOC_BASE ) # error: You must define SEG_IOC_BASE in the hard_config.h file #endif #if !defined( USE_IOB ) # error: You must define USE_IOB in the hard_config.h file #endif #if !defined(GIET_USE_IOMMU) # error: You must define GIET_USE_IOMMU in the giet_config.h file #endif #if (USE_IOC_BDV + USE_IOC_SPI + USE_IOC_HBA + USE_IOC_RDK) != 1 # error: You must use only one IOC controller type (BDV or SPI or HBA or RDK) #endif #if USE_IOC_BDV # include #endif #if USE_IOC_SPI # include #endif #if USE_IOC_HBA # include #endif #if USE_IOC_RDK # include #endif /////////////////////////////////////////////////////////////////////////////// // IOC global variables /////////////////////////////////////////////////////////////////////////////// #define in_unckdata __attribute__((section (".unckdata"))) in_unckdata volatile unsigned int _ioc_iommu_ix1 = 0; in_unckdata volatile unsigned int _ioc_iommu_npages; /////////////////////////////////////////////////////////////////////////////// // This function transfer data between a memory buffer and the block device. // The buffer lentgth is (count*block_size) bytes. // Arguments are: // - to_mem : from external storage to memory when non 0. // - mode : BOOT_PA / BOOT_VA / KERNEL / USER // - lba : first block index on the external storage. // - buf_vaddr : virtual base address of the memory buffer. // - count : number of blocks to be transfered. // Returns 0 if success, > 0 if error. /////////////////////////////////////////////////////////////////////////////// static unsigned int _ioc_access( unsigned int to_mem, unsigned int channel, unsigned int mode, unsigned int lba, unsigned int buf_vaddr, unsigned int count) { #if GIET_DEBUG_IOC_DRIVER unsigned int procid = _get_procid(); unsigned int x = procid >> (Y_WIDTH + P_WIDTH); unsigned int y = (procid >> P_WIDTH) & ((1< 0) ) { _puts("\n[IOC ERROR] in _ioc_access() : channel must be 0 when HBA not used\n"); _exit(); } unsigned int length = count << 9; // count * 512 bytes // computing memory buffer physical address if ( (mode == IOC_BOOT_MODE) && ((_get_mmu_mode() & 0x4) == 0) ) // identity mapping { buf_paddr = (paddr_t)buf_vaddr; } else // V2P translation required { // get page table virtual address pt_vbase = _get_context_slot(CTX_PTAB_ID); // get user buffer first page ppn and flags _v2p_translate( (page_table_t*)pt_vbase, buf_vaddr >> 12, &ppn, &flags ); // check access rights if ( (mode == IOC_USER_MODE) && ((flags & PTE_U) == 0) ) { _puts("\n[IOC ERROR] in _ioc_access() : buffer not user accessible\n"); _exit(); } if ( ((flags & PTE_W) == 0 ) && to_mem ) { _puts("\n[IOC ERROR] in _ioc_access() : buffer not writable\n"); _exit(); } buf_paddr = (((paddr_t)ppn) << 12) | (buf_vaddr & 0xFFF); } // cache coherence for both L1 & L2 caches if ( to_mem ) // memory write : invalidate data caches { // L1 cache (only if L1 cache coherence not guaranteed by hardware) if ( GIET_NO_HARD_CC ) _dcache_buf_invalidate( buf_vaddr, length ); // L2 cache (only if we use an IO-Bridge component in architecture)) if ( USE_IOB ) _mmc_inval( buf_paddr, length ); } else // memory read : update data caches { // L1 cache : nothing to do for L1 write-through // L2 cache (only if we use an IO-Bridge component in architecture)) if ( USE_IOB ) _mmc_sync( buf_paddr, length ); } // select the proper physical device #if ( USE_IOC_BDV ) if (to_mem) error = _bdv_read ( mode, lba, buf_paddr, count); else error = _bdv_write( mode, lba, buf_paddr, count); #elif ( USE_IOC_SPI ) if (to_mem) error = _sdc_read (mode, lba, buf_paddr, count); else error = _sdc_write(mode, lba, buf_paddr, count); #elif ( USE_IOC_HBA ) if (to_mem) error = _hba_read (channel, mode, lba, buf_paddr, count); else error = _hba_write(channel, mode, lba, buf_paddr, count); #elif ( USE_IOC_RDK ) if (to_mem) error = _rdk_read (lba, buf_vaddr, count); else error = _rdk_write(lba, buf_vaddr, count); #endif return error; } // end _ioc_access() ////////////////////////////////////////////// unsigned int _ioc_init( unsigned int channel ) { #if ( USE_IOC_BDV ) return _bdv_init(); #elif ( USE_IOC_SPI ) return _sdc_init(); #elif ( USE_IOC_HBA ) return _hba_init( channel ); #elif ( USE_IOC_RDK ) return _rdk_init(); #endif } ////////////////////////////////////////////// unsigned int _ioc_read( unsigned int channel, unsigned int mode, unsigned int lba, void* buffer, unsigned int count) { return _ioc_access( 1, // read access channel, mode, lba, (unsigned int) buffer, count ); } ////////////////////////////////////////////// unsigned int _ioc_write( unsigned int channel, unsigned int mode, unsigned int lba, const void* buffer, unsigned int count ) { return _ioc_access( 0, // write access channel, mode, lba, (unsigned int) buffer, count ); } ///////////////////////////////////////////////////// unsigned int _ioc_get_status( unsigned int channel ) { #if ( USE_IOC_BDV ) return _bdv_get_status( ); #elif ( USE_IOC_SPI ) return _sdc_get_status( ); #elif ( USE_IOC_HBA ) return _hba_get_status( channel ); #elif ( USE_IOC_RDK ) return rdk_get_status(); #endif } ////////////////////////////////// unsigned int _ioc_get_block_size() { #if ( USE_IOC_BDV ) return _bdv_get_block_size(); #elif ( USE_IOC_SPI ) return _sdc_get_block_size(); #elif ( USE_IOC_HBA ) return _hba_get_block_size(); #elif ( USE_IOC_RDK ) return 512; #endif } // Local Variables: // tab-width: 4 // c-basic-offset: 4 // c-file-offsets:((innamespace . 0)(inline-open . 0)) // indent-tabs-mode: nil // End: // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4