| [283] | 1 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [284] | 2 | // File      : bdv_driver.c | 
|---|
 | 3 | // Date      : 23/05/2013 | 
|---|
| [529] | 4 | // Author    : alain greiner cesar fuguet | 
|---|
| [283] | 5 | // Copyright (c) UPMC-LIP6 | 
|---|
 | 6 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
| [295] | 7 | // Implementation notes: | 
|---|
| [529] | 8 | // All accesses to BDV registers are done by the two | 
|---|
 | 9 | // _bdv_set_register() and _bdv_get_register() low-level functions, | 
|---|
 | 10 | // that are handling virtual / physical extended addressing. | 
|---|
| [283] | 11 | /////////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 12 |  | 
|---|
 | 13 | #include <giet_config.h> | 
|---|
| [320] | 14 | #include <hard_config.h> | 
|---|
| [295] | 15 | #include <bdv_driver.h> | 
|---|
 | 16 | #include <xcu_driver.h> | 
|---|
| [545] | 17 | #include <mmc_driver.h> | 
|---|
| [529] | 18 | #include <kernel_locks.h> | 
|---|
| [283] | 19 | #include <utils.h> | 
|---|
| [456] | 20 | #include <tty0.h> | 
|---|
| [283] | 21 | #include <ctx_handler.h> | 
|---|
| [529] | 22 | #include <irq_handler.h> | 
|---|
| [283] | 23 |  | 
|---|
 | 24 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [630] | 25 | //      Extern variables | 
|---|
 | 26 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 27 |  | 
|---|
 | 28 | // allocated in the boot.c or kernel_init.c files | 
|---|
 | 29 | extern static_scheduler_t* _schedulers[X_SIZE][Y_SIZE][NB_PROCS_MAX];  | 
|---|
 | 30 |   | 
|---|
 | 31 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [529] | 32 | //      Global variables | 
|---|
| [295] | 33 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 34 |  | 
|---|
| [529] | 35 | // lock protecting single channel BDV peripheral | 
|---|
| [496] | 36 | __attribute__((section(".kdata"))) | 
|---|
| [456] | 37 | spin_lock_t  _bdv_lock __attribute__((aligned(64))); | 
|---|
| [496] | 38 |  | 
|---|
| [529] | 39 | // global index of the waiting task (only used in descheduling mode) | 
|---|
| [496] | 40 | __attribute__((section(".kdata"))) | 
|---|
| [529] | 41 | unsigned int _bdv_gtid; | 
|---|
| [496] | 42 |  | 
|---|
| [529] | 43 | // BDV peripheral status (only used in descheduling mode) | 
|---|
| [496] | 44 | __attribute__((section(".kdata"))) | 
|---|
| [529] | 45 | unsigned int _bdv_status; | 
|---|
| [295] | 46 |  | 
|---|
 | 47 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 48 | // This low_level function returns the value contained in register (index). | 
|---|
 | 49 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 50 | unsigned int _bdv_get_register( unsigned int index ) | 
|---|
 | 51 | { | 
|---|
| [320] | 52 |     unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index; | 
|---|
| [295] | 53 |     return _io_extended_read( vaddr ); | 
|---|
 | 54 | } | 
|---|
 | 55 |  | 
|---|
 | 56 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 57 | // This low-level function set a new value in register (index). | 
|---|
 | 58 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
 | 59 | void _bdv_set_register( unsigned int index, | 
|---|
 | 60 |                         unsigned int value )  | 
|---|
 | 61 | { | 
|---|
| [320] | 62 |     unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index; | 
|---|
| [295] | 63 |     _io_extended_write( vaddr, value ); | 
|---|
 | 64 | } | 
|---|
 | 65 |  | 
|---|
 | 66 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [529] | 67 | //      Extern functions | 
|---|
| [283] | 68 | /////////////////////////////////////////////////////////////////////////////// | 
|---|
| [529] | 69 |  | 
|---|
 | 70 | ///////////////////////////////////////////////////// | 
|---|
 | 71 | unsigned int _bdv_access( unsigned int       use_irq, | 
|---|
 | 72 |                           unsigned int       to_mem, | 
|---|
 | 73 |                           unsigned int       lba, | 
|---|
 | 74 |                           unsigned long long buf_paddr, | 
|---|
 | 75 |                           unsigned int       count)  | 
|---|
| [283] | 76 | { | 
|---|
| [426] | 77 |     unsigned int procid  = _get_procid(); | 
|---|
 | 78 |     unsigned int x       = procid >> (Y_WIDTH + P_WIDTH); | 
|---|
 | 79 |     unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH) - 1); | 
|---|
 | 80 |     unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [283] | 81 |  | 
|---|
| [593] | 82 | #if GIET_DEBUG_IOC | 
|---|
 | 83 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 84 | _printf("\n[BDV DEBUG] P[%d,%d,%d] enters _bdv_access at cycle %d\n" | 
|---|
 | 85 |         "  use_irq = %d / to_mem = %d / lba = %x / paddr = %l / count = %d\n", | 
|---|
 | 86 |         x , y , p , _get_proctime() , use_irq , to_mem , lba , buf_paddr, count ); | 
|---|
| [283] | 87 | #endif | 
|---|
 | 88 |  | 
|---|
| [529] | 89 |     // check buffer alignment | 
|---|
| [593] | 90 |     if( buf_paddr & 0x3F ) | 
|---|
| [529] | 91 |     { | 
|---|
| [593] | 92 |         _printf("\n[BDV ERROR] in _bdv_access() : buffer not cache ligne aligned\n"); | 
|---|
| [529] | 93 |         return -1; | 
|---|
 | 94 |     } | 
|---|
| [283] | 95 |  | 
|---|
| [529] | 96 |     unsigned int error; | 
|---|
 | 97 |     unsigned int status; | 
|---|
 | 98 |  | 
|---|
| [295] | 99 |     // get the lock protecting BDV | 
|---|
| [469] | 100 |     _spin_lock_acquire( &_bdv_lock ); | 
|---|
| [283] | 101 |  | 
|---|
| [295] | 102 |     // set device registers | 
|---|
 | 103 |     _bdv_set_register( BLOCK_DEVICE_BUFFER    , (unsigned int)buf_paddr ); | 
|---|
 | 104 |     _bdv_set_register( BLOCK_DEVICE_BUFFER_EXT, (unsigned int)(buf_paddr>>32) ); | 
|---|
 | 105 |     _bdv_set_register( BLOCK_DEVICE_COUNT     , count ); | 
|---|
 | 106 |     _bdv_set_register( BLOCK_DEVICE_LBA       , lba ); | 
|---|
| [283] | 107 |  | 
|---|
| [545] | 108 | #if USE_IOB    // software L2/L3 cache coherence | 
|---|
 | 109 |     if ( to_mem )  _mmc_inval( buf_paddr, count<<9 ); | 
|---|
 | 110 |     else           _mmc_sync( buf_paddr, count<<9 ); | 
|---|
 | 111 | #endif     // end software L2/L3 cache coherence | 
|---|
 | 112 |  | 
|---|
| [529] | 113 |     ///////////////////////////////////////////////////////////////////// | 
|---|
 | 114 |     // In synchronous mode, we launch transfer,  | 
|---|
 | 115 |     // and poll the BDV_STATUS register until completion. | 
|---|
 | 116 |     ///////////////////////////////////////////////////////////////////// | 
|---|
 | 117 |     if ( use_irq == 0 )  | 
|---|
| [283] | 118 |     { | 
|---|
 | 119 |         // Launch transfert | 
|---|
| [295] | 120 |         if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE ); | 
|---|
 | 121 |         else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ ); | 
|---|
| [283] | 122 |  | 
|---|
| [593] | 123 | #if GIET_DEBUG_IOC | 
|---|
 | 124 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 125 | _printf("\n[BDV DEBUG] _bdv_access() : P[%d,%d,%d] launch transfer" | 
|---|
 | 126 |         " in polling mode at cycle %d\n", | 
|---|
 | 127 |         x , y , p , _get_proctime() ); | 
|---|
| [333] | 128 | #endif | 
|---|
| [529] | 129 |  | 
|---|
| [289] | 130 |         do | 
|---|
| [283] | 131 |         { | 
|---|
| [295] | 132 |             status = _bdv_get_register( BLOCK_DEVICE_STATUS ); | 
|---|
| [283] | 133 |  | 
|---|
| [593] | 134 | #if GIET_DEBUG_IOC | 
|---|
 | 135 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 136 | _printf("\n[BDV DEBUG] _bdv_access() : P[%d,%d,%d] wait on BDV_STATUS ...\n", | 
|---|
 | 137 |         x , y , p ); | 
|---|
| [283] | 138 | #endif | 
|---|
| [289] | 139 |         } | 
|---|
 | 140 |         while( (status != BLOCK_DEVICE_READ_SUCCESS)  && | 
|---|
 | 141 |                (status != BLOCK_DEVICE_READ_ERROR)    && | 
|---|
 | 142 |                (status != BLOCK_DEVICE_WRITE_SUCCESS) && | 
|---|
| [295] | 143 |                (status != BLOCK_DEVICE_WRITE_ERROR)   );      // busy waiting | 
|---|
| [283] | 144 |  | 
|---|
 | 145 |         // analyse status | 
|---|
 | 146 |         error = ( (status == BLOCK_DEVICE_READ_ERROR) || | 
|---|
 | 147 |                   (status == BLOCK_DEVICE_WRITE_ERROR) ); | 
|---|
| [529] | 148 |     } | 
|---|
| [283] | 149 |  | 
|---|
| [529] | 150 |     ///////////////////////////////////////////////////////////////// | 
|---|
 | 151 |     // in descheduling mode, we deschedule the task | 
|---|
 | 152 |     // and use an interrupt to reschedule the task. | 
|---|
| [295] | 153 |     // We need a critical section, because we must reset the RUN bit | 
|---|
| [529] | 154 |         // before to launch the transfer, and we don't want to be  | 
|---|
 | 155 |     // descheduled between these two operations.  | 
|---|
 | 156 |     ///////////////////////////////////////////////////////////////// | 
|---|
| [295] | 157 |     else | 
|---|
| [283] | 158 |     { | 
|---|
| [295] | 159 |         unsigned int save_sr; | 
|---|
 | 160 |         unsigned int ltid = _get_current_task_id(); | 
|---|
| [283] | 161 |  | 
|---|
| [529] | 162 |         // activates BDV interrupt | 
|---|
| [295] | 163 |         _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 1 ); | 
|---|
 | 164 |  | 
|---|
| [545] | 165 |         // set _bdv_gtid  | 
|---|
 | 166 |         _bdv_gtid = (procid<<16) + ltid; | 
|---|
 | 167 |  | 
|---|
| [295] | 168 |         // enters critical section | 
|---|
 | 169 |         _it_disable( &save_sr );  | 
|---|
| [320] | 170 |  | 
|---|
| [630] | 171 |         // Set NORUN_MASK_IOC bit  | 
|---|
 | 172 |         static_scheduler_t* psched  = (static_scheduler_t*)_schedulers[x][y][p]; | 
|---|
| [657] | 173 |         _atomic_or( &psched->context[ltid][CTX_NORUN_ID] , NORUN_MASK_IOC ); | 
|---|
| [283] | 174 |          | 
|---|
| [295] | 175 |         // launch transfer | 
|---|
 | 176 |         if (to_mem == 0) _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_WRITE ); | 
|---|
 | 177 |         else             _bdv_set_register( BLOCK_DEVICE_OP, BLOCK_DEVICE_READ  ); | 
|---|
| [283] | 178 |  | 
|---|
| [593] | 179 | #if GIET_DEBUG_IOC | 
|---|
 | 180 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 181 | _printf("\n[BDV DEBUG] _bdv_access() : P[%d,%d,%d] launch transfer" | 
|---|
 | 182 |         " in descheduling mode at cycle %d\n", | 
|---|
 | 183 |         x , y , p , _get_proctime() ); | 
|---|
| [333] | 184 | #endif | 
|---|
| [437] | 185 |  | 
|---|
| [283] | 186 |         // deschedule task | 
|---|
 | 187 |         _ctx_switch();                       | 
|---|
 | 188 |  | 
|---|
| [593] | 189 | #if GIET_DEBUG_IOC | 
|---|
 | 190 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 191 | _printf("\n[BDV DEBUG] _bdv_access() : P[%d,%d,%d] resume execution at cycle %d\n", | 
|---|
 | 192 |         x , y , p , _get_proctime() ); | 
|---|
| [426] | 193 | #endif | 
|---|
| [529] | 194 |  | 
|---|
| [295] | 195 |         // restore SR | 
|---|
 | 196 |         _it_restore( &save_sr ); | 
|---|
 | 197 |  | 
|---|
| [283] | 198 |         // analyse status | 
|---|
| [295] | 199 |         error = ( (_bdv_status == BLOCK_DEVICE_READ_ERROR) || | 
|---|
 | 200 |                   (_bdv_status == BLOCK_DEVICE_WRITE_ERROR) ); | 
|---|
| [283] | 201 |     } | 
|---|
 | 202 |  | 
|---|
| [529] | 203 |     // release lock | 
|---|
 | 204 |     _spin_lock_release( &_bdv_lock );       | 
|---|
 | 205 |  | 
|---|
| [593] | 206 | #if GIET_DEBUG_IOC | 
|---|
 | 207 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [529] | 208 | _printf("\n[BDV DEBUG] _bdv_access() : P[%d,%d,%d] exit at cycle %d\n", | 
|---|
 | 209 |         x , y , p , _get_proctime() ); | 
|---|
| [283] | 210 | #endif | 
|---|
 | 211 |  | 
|---|
 | 212 |     return error; | 
|---|
 | 213 | } // end _bdv_access() | 
|---|
 | 214 |  | 
|---|
| [437] | 215 | //////////////////////// | 
|---|
| [295] | 216 | unsigned int _bdv_init() | 
|---|
| [283] | 217 | { | 
|---|
| [295] | 218 |     if ( _bdv_get_register( BLOCK_DEVICE_BLOCK_SIZE ) != 512 ) | 
|---|
| [283] | 219 |     { | 
|---|
| [437] | 220 |         _puts("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n"); | 
|---|
| [283] | 221 |         return 1;  | 
|---|
 | 222 |     } | 
|---|
 | 223 |  | 
|---|
| [295] | 224 |     _bdv_set_register( BLOCK_DEVICE_IRQ_ENABLE, 0 ); | 
|---|
| [283] | 225 |     return 0; | 
|---|
 | 226 | } | 
|---|
 | 227 |  | 
|---|
| [437] | 228 | ///////////////////////////////////// | 
|---|
| [295] | 229 | void _bdv_isr( unsigned int irq_type,   // HWI / WTI | 
|---|
 | 230 |                unsigned int irq_id,     // index returned by ICU | 
|---|
 | 231 |                unsigned int channel )   // unused  | 
|---|
 | 232 | { | 
|---|
| [529] | 233 |     // get BDV status and reset BDV_IRQ | 
|---|
| [297] | 234 |     unsigned int status =  _bdv_get_register( BLOCK_DEVICE_STATUS );  | 
|---|
| [295] | 235 |  | 
|---|
| [297] | 236 |     // check status: does nothing if IDLE or BUSY | 
|---|
 | 237 |     if ( (status == BLOCK_DEVICE_IDLE) || | 
|---|
 | 238 |          (status == BLOCK_DEVICE_BUSY) )   return; | 
|---|
 | 239 |   | 
|---|
| [529] | 240 |     // register status in global variable | 
|---|
 | 241 |     _bdv_status = status; | 
|---|
| [295] | 242 |  | 
|---|
 | 243 |     // identify task waiting on BDV | 
|---|
| [630] | 244 |     unsigned int procid  = _bdv_gtid>>16; | 
|---|
 | 245 |     unsigned int ltid    = _bdv_gtid & 0xFFFF; | 
|---|
 | 246 |     unsigned int cluster = procid >> P_WIDTH; | 
|---|
 | 247 |     unsigned int x       = cluster >> Y_WIDTH; | 
|---|
 | 248 |     unsigned int y       = cluster & ((1<<Y_WIDTH)-1); | 
|---|
 | 249 |     unsigned int p       = procid & ((1<<P_WIDTH)-1); | 
|---|
| [295] | 250 |  | 
|---|
| [630] | 251 |     // Reset NORUN_MASK_IOC bit  | 
|---|
 | 252 |     static_scheduler_t* psched  = (static_scheduler_t*)_schedulers[x][y][p]; | 
|---|
 | 253 |     unsigned int*       ptr     = &psched->context[ltid][CTX_NORUN_ID]; | 
|---|
 | 254 |     _atomic_and( ptr , ~NORUN_MASK_IOC ); | 
|---|
| [297] | 255 |  | 
|---|
| [529] | 256 |     // send a WAKUP WTI to processor running the sleeping task  | 
|---|
| [630] | 257 |     _xcu_send_wti( cluster,    | 
|---|
 | 258 |                    p,  | 
|---|
| [529] | 259 |                    0 );          // don't force context switch  | 
|---|
| [297] | 260 |  | 
|---|
| [593] | 261 | #if GIET_DEBUG_IOC   | 
|---|
| [630] | 262 | unsigned int pid  = _get_procid(); | 
|---|
 | 263 | unsigned int c_x  = pid >> (Y_WIDTH + P_WIDTH); | 
|---|
 | 264 | unsigned int c_y  = (pid >> P_WIDTH) & ((1<<Y_WIDTH)-1); | 
|---|
 | 265 | unsigned int c_p  = pid & ((1<<P_WIDTH)-1); | 
|---|
| [593] | 266 | if ( _get_proctime() > GIET_DEBUG_IOC ) | 
|---|
| [563] | 267 | _printf("\n[BDV DEBUG] Processor[%d,%d,%d] enters _bdv_isr() at cycle %d\n" | 
|---|
| [529] | 268 |         "  for task %d running on P[%d,%d,%d] / bdv_status = %x\n", | 
|---|
| [630] | 269 |         c_x , c_y , c_p , _get_proctime() , | 
|---|
 | 270 |         ltid , x , y , p , status ); | 
|---|
| [295] | 271 | #endif | 
|---|
 | 272 |  | 
|---|
| [529] | 273 | } // end bdv_isr() | 
|---|
| [295] | 274 |  | 
|---|
 | 275 |  | 
|---|
| [283] | 276 | // Local Variables: | 
|---|
 | 277 | // tab-width: 4 | 
|---|
 | 278 | // c-basic-offset: 4 | 
|---|
 | 279 | // c-file-offsets:((innamespace . 0)(inline-open . 0)) | 
|---|
 | 280 | // indent-tabs-mode: nil | 
|---|
 | 281 | // End: | 
|---|
 | 282 | // vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4 | 
|---|
 | 283 |  | 
|---|