source: soft/giet_vm/giet_drivers/hba_driver.c @ 739

Last change on this file since 739 was 715, checked in by alain, 9 years ago

Cosmetic.

File size: 16.5 KB
RevLine 
[529]1//////////////////////////////////////////////////////////////////////////////////
[258]2// File     : hba_driver.c
3// Date     : 23/11/2013
[295]4// Author   : alain greiner
[258]5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
[295]7// Implementation notes:
[529]8// All accesses to HBA registers are done by the two
9// _hba_set_register() and _hba_get_register() low-level functions,
10// that are handling virtual / physical extended addressing.
[258]11///////////////////////////////////////////////////////////////////////////////////
12
13#include <giet_config.h>
[529]14#include <hard_config.h>
15#include <hba_driver.h>
16#include <xcu_driver.h>
[545]17#include <mmc_driver.h>
[529]18#include <kernel_locks.h>
[258]19#include <utils.h>
[456]20#include <tty0.h>
[258]21#include <ctx_handler.h>
[529]22#include <irq_handler.h>
[258]23#include <vmem.h>
24
[630]25//////////////////////////////////////////////////////////////////////////////////
26//      Extern variables
27//////////////////////////////////////////////////////////////////////////////////
[258]28
[630]29// allocated in the boot.c or kernel_init.c files
[715]30extern static_scheduler_t*    _schedulers[X_SIZE][Y_SIZE][NB_PROCS_MAX]; 
31extern unsigned int           _hba_boot_mode; 
[630]32
[576]33//////////////////////////////////////////////////////////////////////////////////
[630]34//               Global variables
35//////////////////////////////////////////////////////////////////////////////////
[576]36// The global variable hba_boot_mode defines the way the HBA component is used
37// and must be defined in both kernel_init.c and boot.c files.
[630]38// - during the boot phase, only one processor access the HBA in synchronous
[715]39//   mode. There is no need to use a lock.
40// - during the execution phase, the HBA device can be used by several processors.
41//   The slot allocator is protected by a sqt_lock.
[576]42//////////////////////////////////////////////////////////////////////////////////
43       
[529]44__attribute__((section(".kdata")))
[715]45sqt_lock_t          _hba_lock  __attribute__((aligned(64)));
[258]46
[709]47// state of each slot (allocated to a thread or not)
[576]48// access must be protected by the allocator_lock in descheduling mode
[529]49__attribute__((section(".kdata")))
[576]50unsigned int        _hba_allocated_cmd[32];
[258]51
[576]52// state of the command (active or not), for each possible slot
53// used only in descheduling mode
[529]54__attribute__((section(".kdata")))
[576]55unsigned int        _hba_active_cmd[32]; 
[258]56
[709]57// global index of the thread, for each entry in the command list
[529]58__attribute__((section(".kdata")))
[709]59unsigned int        _hba_trid[32];
[258]60
[576]61// status of HBA commands
[529]62__attribute__((section(".kdata")))
[576]63unsigned int        _hba_status;
[258]64
[576]65// command list : up to 32 commands
[529]66__attribute__((section(".kdata")))
[576]67hba_cmd_desc_t      _hba_cmd_list[32] __attribute__((aligned(0x40)));   
[258]68
[576]69// command tables array : one command table per entry in command list
70__attribute__((section(".kdata")))
71hba_cmd_table_t     _hba_cmd_table[32] __attribute__((aligned(0x40))); 
72
73
[295]74//////////////////////////////////////////////////////////////////////////////
[529]75// This low level function returns the value of register (index)
[295]76//////////////////////////////////////////////////////////////////////////////
[529]77unsigned int _hba_get_register( unsigned int index )
[258]78{
[529]79    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
[295]80    return _io_extended_read( vaddr );
81}
[258]82
[295]83//////////////////////////////////////////////////////////////////////////////
[529]84// This low level function set a new value in register (index) 
[295]85//////////////////////////////////////////////////////////////////////////////
[529]86void _hba_set_register( unsigned int index,
[295]87                        unsigned int value )
88{
[529]89    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + index;
[295]90    _io_extended_write( vaddr, value );
[258]91}
92
[529]93///////////////////////////////////////////////////////////////////////////////
94//      Extern functions
95///////////////////////////////////////////////////////////////////////////////
[295]96
[258]97///////////////////////////////////////////////////////////////////////////////
[709]98// This blocking fonction allocates a free command index to the thread.
[715]99// The hba_lock is not used in boot mode.
[576]100// It returns the allocated command index (between 0 and 31)
101///////////////////////////////////////////////////////////////////////////////
102unsigned int _hba_cmd_alloc()
103{
104    unsigned int found = 0;
105    unsigned int c;           // command index for the loop
106    unsigned int cmd_id = -1; // allocated command index when found
107
108    while ( found == 0)
109    {
[715]110        if ( !_hba_boot_mode ) _sqt_lock_acquire(&_hba_lock);
[576]111
112        for ( c = 0; c < 32 ; c++ )
113        {
114            if (_hba_allocated_cmd[c] == 0)
115            {
116                found = 1;
117                cmd_id = c;
118                _hba_allocated_cmd[c] = 1;
119                break;
120            }
121        }
122
[715]123        if ( !_hba_boot_mode ) _sqt_lock_release(&_hba_lock);
[576]124    }
125
126    return cmd_id;
127}
128
129///////////////////////////////////////////////////////////////////////////////
130// This function releases the command index in the hba_allocated_cmd table.
[709]131// There is no need to take the lock because only the thread which owns the
[576]132// command can release it.
[437]133// return 0 if success, -1 if error
[258]134///////////////////////////////////////////////////////////////////////////////
[576]135unsigned int _hba_cmd_release(unsigned int cmd_id)
136{
137    if ( _hba_allocated_cmd[cmd_id] == 0 )
138    {
139        _printf("\n[HBA ERROR] in _hba_access() : ask to release a command which is not allocated\n");
140        return -1;
141    }
142   
143    _hba_allocated_cmd[cmd_id] = 0;
144    return 0;
145}
146
147
148///////////////////////////////////////////////////////////////////////////////
149// This function gets a command index with the hba_cmd_alloc function. Then it
150// registers a command in both the command list and the command table. It
151// updates the HBA_PXCI register and the hba_active_cmd in descheduling mode.
152// At the end the command slot is released.
153// return 0 if success, -1 if error
154///////////////////////////////////////////////////////////////////////////////
[529]155unsigned int _hba_access( unsigned int       use_irq,
156                          unsigned int       to_mem,
157                          unsigned int       lba, 
158                          unsigned long long buf_paddr,
159                          unsigned int       count )   
[258]160{
[529]161    unsigned int procid  = _get_procid();
162    unsigned int x       = procid >> (Y_WIDTH + P_WIDTH);
163    unsigned int y       = (procid >> P_WIDTH) & ((1<<Y_WIDTH) - 1);
164    unsigned int p       = procid & ((1<<P_WIDTH)-1);
[295]165
[603]166#if GIET_DEBUG_IOC
167if (_get_proctime() > GIET_DEBUG_IOC)
[545]168_printf("\n[DEBUG HBA] _hba_access() : P[%d,%d,%d] enters at cycle %d\n"
[529]169        "  use_irq = %d / to_mem = %d / lba = %x / paddr = %l / count = %d\n",
170        x , y , p , _get_proctime() , use_irq , to_mem , lba , buf_paddr, count );
171#endif
172
[576]173    unsigned int       cmd_id;            // command index
[545]174    unsigned int       pxci;              // HBA_PXCI register value
175    unsigned int       pxis;              // HBA_PXIS register value
176    hba_cmd_desc_t*    cmd_desc;          // command descriptor pointer   
177    hba_cmd_table_t*   cmd_table;         // command table pointer
[258]178
179    // check buffer alignment
[545]180    if( buf_paddr & 0x3F )
[258]181    {
[545]182        _printf("\n[HBA ERROR] in _hba_access() : buffer not 64 bytes aligned\n");
[437]183        return -1;
[258]184    }
185
[545]186    // get one entry in Command List
[576]187    cmd_id = _hba_cmd_alloc();
[258]188
189    // compute pointers on command descriptor and command table   
[576]190    cmd_desc  = &_hba_cmd_list[cmd_id];
191    cmd_table = &_hba_cmd_table[cmd_id];
[258]192
[295]193    // set  buffer descriptor in command table
[540]194    cmd_table->buffer.dba  = (unsigned int)(buf_paddr);
195    cmd_table->buffer.dbau = (unsigned int)(buf_paddr >> 32);
196    cmd_table->buffer.dbc  = count * 512;
[295]197
198    // initialize command table header
199    cmd_table->header.lba0 = (char)lba;
200    cmd_table->header.lba1 = (char)(lba>>8);
201    cmd_table->header.lba2 = (char)(lba>>16);
202    cmd_table->header.lba3 = (char)(lba>>24);
203    cmd_table->header.lba4 = 0;
204    cmd_table->header.lba5 = 0;
205
206    // initialise command descriptor
207    cmd_desc->prdtl[0] = 1;
208    cmd_desc->prdtl[1] = 0;
[529]209    if( to_mem ) cmd_desc->flag[0] = 0x00;
210    else         cmd_desc->flag[0] = 0x40;     
[295]211
[545]212#if USE_IOB    // software L2/L3 cache coherence
[295]213
[545]214    // compute physical addresses
215    unsigned long long cmd_desc_paddr;    // command descriptor physical address
216    unsigned long long cmd_table_paddr;   // command table header physical address
217    unsigned int       flags;             // unused
218
219    if ( _get_mmu_mode() & 0x4 )
220    {
221        cmd_desc_paddr  = _v2p_translate( (unsigned int)cmd_desc  , &flags );
222        cmd_table_paddr = _v2p_translate( (unsigned int)cmd_table , &flags );
223    }
224    else
225    {
226        cmd_desc_paddr  = (unsigned int)cmd_desc;
227        cmd_table_paddr = (unsigned int)cmd_table;
228    }
229
230    // update external memory for command table
231    _mmc_sync( cmd_table_paddr & (~0x3F) , sizeof(hba_cmd_table_t) );
232
233    // update external memory for command descriptor
234    _mmc_sync( cmd_desc_paddr & (~0x3F) , sizeof(hba_cmd_desc_t) );
235
236    // inval or synchronize memory buffer
237    if ( to_mem )  _mmc_inval( buf_paddr, count<<9 );
238    else           _mmc_sync( buf_paddr, count<<9 );
239
240#endif     // end software L2/L3 cache coherence
241
[529]242    /////////////////////////////////////////////////////////////////////
243    // In synchronous mode, we poll the PXCI register until completion
244    /////////////////////////////////////////////////////////////////////
245    if ( use_irq == 0 ) 
246    {
[545]247        // start HBA transfer
[576]248        _hba_set_register( HBA_PXCI, (1<<cmd_id) );
[295]249
[603]250#if GIET_DEBUG_IOC
251if (_get_proctime() > GIET_DEBUG_IOC)
[563]252_printf("\n[DEBUG HBA] _hba_access() : P[%d,%d,%d] get slot %d in Cmd List "
[545]253        " at cycle %d / polling\n",
[576]254        x , y , p , cmd_id, _get_proctime() );
[529]255#endif
256        // disable IRQs in PXIE register
257        _hba_set_register( HBA_PXIE , 0 );
[295]258
[576]259        // poll PXCI[cmd_id] until command completed by HBA
[529]260        do
261        {
[545]262            pxci = _hba_get_register( HBA_PXCI );
[258]263
[603]264#if GIET_DEBUG_IOC
265if (_get_proctime() > GIET_DEBUG_IOC)
[545]266_printf("\n[DEBUG HBA] _hba_access() : P[%d,%d,%d] wait on HBA_PXCI / pxci = %x\n",
267        x , y , p , pxci );
[529]268#endif
269        }
[576]270        while( pxci & (1<<cmd_id) ); 
[529]271             
272        // get PXIS register
273        pxis = _hba_get_register( HBA_PXIS );
274
275        // reset PXIS register
276        _hba_set_register( HBA_PXIS , 0 );
277    }
278
279    /////////////////////////////////////////////////////////////////
[709]280    // in descheduling mode, we deschedule the thread
281    // and use an interrupt to reschedule the thread.
[630]282    // We need a critical section, because we must set the NORUN bit
[529]283        // before to launch the transfer, and we don't want to be
284    // descheduled between these two operations.
285    /////////////////////////////////////////////////////////////////
286    else
[258]287    {
288
[603]289#if GIET_DEBUG_IOC
290if (_get_proctime() > GIET_DEBUG_IOC)
[563]291_printf("\n[DEBUG HBA] _hba_access() : P[%d,%d,%d] get slot %d in Cmd List "
[545]292        "at cycle %d / descheduling\n",
[576]293        x , y , p , cmd_id, _get_proctime() );
[529]294#endif
295        unsigned int save_sr;
[709]296        unsigned int ltid = _get_thread_ltid();
[258]297
[529]298        // activates HBA interrupts
299        _hba_set_register( HBA_PXIE , 0x00000001 ); 
[258]300
[709]301        // set _hba_trid[cmd_id]
302        _hba_trid[cmd_id] = (x<<24) + (y<<16) + (p<<8) + ltid;
[258]303
[529]304        // enters critical section
305        _it_disable( &save_sr ); 
[258]306
[630]307        // Set NORUN_MASK_IOC bit
308        static_scheduler_t* psched  = (static_scheduler_t*)_schedulers[x][y][p];
[709]309        unsigned int*       ptr     = &psched->context[ltid].slot[CTX_NORUN_ID];
[630]310        _atomic_or( ptr , NORUN_MASK_IOC );
311     
[545]312        // start HBA transfer
[576]313        _hba_set_register( HBA_PXCI, (1<<cmd_id) );
[545]314
[576]315        // set _hba_active_cmd[cmd_id]
316        _hba_active_cmd[cmd_id] = 1;
317
[709]318        // deschedule thread
[529]319        _ctx_switch();                     
[258]320
[603]321#if GIET_DEBUG_IOC
322if (_get_proctime() > GIET_DEBUG_IOC)
[709]323_printf("\n[DEBUG HBA] _hba_access() : thread %d on P[%d,%d,%d] resume at cycle %d\n",
[545]324        ltid , x , y , p , _get_proctime() );
[529]325#endif
[258]326
[529]327        // restore SR
328        _it_restore( &save_sr );
[258]329
[529]330        // get command status
[576]331        pxis = _hba_status;
332    }
333   
334    // release the cmd index
335    unsigned int release_success;
336    release_success = _hba_cmd_release(cmd_id);
[258]337
[603]338#if GIET_DEBUG_IOC
339if (_get_proctime() > GIET_DEBUG_IOC)
[576]340_printf("\n[DEBUG HBA] _hba_access() : P[%d,%d,%d] release slot %d in Cmd List "
341        "and exit at cycle %d\n",
342        x , y , p, cmd_id,
343        _get_proctime() );
[529]344#endif
[258]345
[576]346    if ( release_success != 0 )   return -1;
347    else if ( pxis & 0x40000000 ) return pxis;
348    else                          return 0;
[258]349
[529]350} // end _hba_access()
[258]351
352
[529]353////////////////////////
354unsigned int _hba_init()
355{
[545]356    unsigned int       cmd_list_vaddr;
357    unsigned int       cmd_table_vaddr;
358    unsigned long long cmd_list_paddr;
359    unsigned long long cmd_table_paddr;
360    unsigned int       flags;            // unused
[258]361
[545]362    // compute Command list & command table physical addresses
363    cmd_list_vaddr  = (unsigned int)(&_hba_cmd_list[0]);
364    cmd_table_vaddr = (unsigned int)(&_hba_cmd_table[0]);
365    if ( _get_mmu_mode() & 0x4 )
366    {
367        cmd_list_paddr  = _v2p_translate( cmd_list_vaddr  , &flags );
368        cmd_table_paddr = _v2p_translate( cmd_table_vaddr , &flags );
369    }
370    else
371    {
372        cmd_list_paddr  = (unsigned long long)cmd_list_vaddr;
373        cmd_table_paddr = (unsigned long long)cmd_table_vaddr;
374    }
375
[576]376    // initialise allocator lock if not in boot mode
377    if ( !_hba_boot_mode )
[715]378        _sqt_lock_init(&_hba_lock);
[258]379
[576]380    // initialise Command Descriptors in Command List, allocated command table
381    // and active command table
[545]382    unsigned int         c;     
383    unsigned long long   paddr;
[258]384    for( c=0 ; c<32 ; c++ )
385    {
[545]386        paddr = cmd_table_paddr + c * sizeof(hba_cmd_table_t);
387        _hba_cmd_list[c].ctba  = (unsigned int)(paddr);
[529]388        _hba_cmd_list[c].ctbau = (unsigned int)(paddr>>32);
[715]389        _hba_allocated_cmd[c]  = 0;
390        _hba_active_cmd[c]     = 0;
[258]391    }
[289]392
[545]393    // initialise HBA registers
394    _hba_set_register( HBA_PXCLB  , (unsigned int)(cmd_list_paddr) );
395    _hba_set_register( HBA_PXCLBU , (unsigned int)(cmd_list_paddr>>32) );
396    _hba_set_register( HBA_PXIE   , 0 );
397    _hba_set_register( HBA_PXIS   , 0 );
398    _hba_set_register( HBA_PXCI   , 0 );
399    _hba_set_register( HBA_PXCMD  , 1 );
[529]400
[289]401    return 0;
[258]402}
403
[437]404
[545]405/////////////////////////////////////////////////////
[529]406void _hba_isr( unsigned int irq_type,   // HWI / WTI
407               unsigned int irq_id,     // index returned by ICU
408               unsigned int channel )   // unused
[437]409{
[576]410    // save PXIS register if there is no previous error
411    if ( !(_hba_status & 0x40000000))
412        _hba_status = _hba_get_register( HBA_PXIS );
413
414    // reset PXIS register
415    _hba_set_register( HBA_PXIS , 0 );
416
417    unsigned int cmd_id;  // cmd index for the loops
418   
419    // save the current list of active cmd in a 32 bits word
420    unsigned int current_active_cmd = 0;
421    for ( cmd_id = 0 ; cmd_id < 32 ; cmd_id ++ )
422    {
423        if ( _hba_active_cmd[cmd_id] == 1 ) current_active_cmd += (1 << cmd_id);
424    }
425   
[529]426    // get HBA_PXCI containing commands status
[576]427    unsigned int current_pxci = _hba_get_register( HBA_PXCI );
[437]428
[576]429    for ( cmd_id = 0 ; cmd_id < 32 ; cmd_id ++ )
[529]430    {
[576]431        if ( ( (current_active_cmd & (1<<cmd_id)) != 0) && // active command
432             ( (current_pxci & (1<<cmd_id)) == 0 ) )       // completed command
[529]433        {
[576]434            // desactivate the command
435            _hba_active_cmd[cmd_id] = 0;
[258]436
[709]437            // identify waiting thread
438            unsigned int x       = (_hba_trid[cmd_id]>>24) & 0xFF;
439            unsigned int y       = (_hba_trid[cmd_id]>>16) & 0xFF;
440            unsigned int p       = (_hba_trid[cmd_id]>> 8) & 0xFF;
441            unsigned int ltid    = (_hba_trid[cmd_id]    ) & 0xFF;
[529]442 
[630]443            // Reset NORUN_MASK_IOC bit
444            static_scheduler_t* psched  = (static_scheduler_t*)_schedulers[x][y][p];
[709]445            _atomic_and( &psched->context[ltid].slot[CTX_NORUN_ID] , ~NORUN_MASK_IOC );
[295]446
[709]447            // send a WAKUP WTI to processor running the waiting thread
448            _xcu_send_wti( (x<<Y_WIDTH) + y,
[630]449                           p , 
[529]450                           0 );          // don't force context switch
[295]451
[603]452#if GIET_DEBUG_IOC 
453if (_get_proctime() > GIET_DEBUG_IOC)
[545]454_printf("\n[DEBUG HBA] _hba_isr() : command %d completed at cycle %d\n"
[709]455        "  resume thread %d running on P[%d,%d,%d]\n",
[576]456        cmd_id , _get_proctime() ,
[630]457        ltid , x , y , p );
[576]458#endif
[529]459        }
460    }
461} // end _hba_isr()
[295]462
[258]463// Local Variables:
464// tab-width: 4
465// c-basic-offset: 4
466// c-file-offsets:((innamespace . 0)(inline-open . 0))
467// indent-tabs-mode: nil
468// End:
469// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
470
Note: See TracBrowser for help on using the repository browser.