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

Last change on this file since 680 was 657, checked in by alain, 9 years ago

Cosmetic.

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