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

Last change on this file since 492 was 481, checked in by alain, 10 years ago

1) The NIC, IOC, DMA and HBA drivers have been adapted to support the new _v2p_translate() function prototype (returns void).
2) The _mmc_inval() and _mmc_sync() functions does not use anymore the hard lock in the MMC, but use a soft spin_lock.
3) The NIC driver does not use anymore the GIET_NIC_BUFSIZE, GIET_NIC_NBUFS, and GIET_NIC_TIMEOUT parameters (removed from giet_config.h file).
4) The NIC driver registers map has been modified to support 64 bytes buffer descriptors for chained buffers.

File size: 12.3 KB
RevLine 
[258]1///////////////////////////////////////////////////////////////////////////////////
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:
8// 1. In order to share code, the two _hba_read() and _hba_write() functions
9//    call the same _hba_set_cmd() function.
10// 2. All accesses to HBA registers are done by the two
11//    _hba_set_register() and _hba_get_register() low-level functions,
12//    that are handling virtual / physical extended addressing.
[258]13///////////////////////////////////////////////////////////////////////////////////
14
15#include <giet_config.h>
16#include <ioc_driver.h>
17#include <utils.h>
[456]18#include <tty0.h>
[258]19#include <iob_driver.h>
20#include <ctx_handler.h>
21#include <mmc_driver.h>
[289]22#include <hba_driver.h>
[258]23#include <vmem.h>
24
[295]25#if !defined( NB_IOC_CHANNELS )
26# error: You must define NB_IOC_CHANNELS in the hard_config.h file
[258]27#endif
28
[295]29#if ( NB_IOC_CHANNELS > 8 )
30# error: NB_IOC_CHANNELS cannot be larger than 8
[258]31#endif
32
33#define in_unckdata __attribute__((section (".unckdata")))
34
35//////////////////////////////////////////////////////////////////
[437]36//               Global variables
[258]37//////////////////////////////////////////////////////////////////
38
39// command list array (one per channel)
[295]40hba_cmd_list_t   hba_cmd_list[NB_IOC_CHANNELS] __attribute__((aligned(0x1000)));   
[258]41
42// command tables array (32 command tables per channel)
[295]43hba_cmd_table_t  hba_cmd_table[NB_IOC_CHANNELS][32] __attribute__((aligned(0x1000))); 
[258]44
45// command list physical addresses array (one per channel)
[295]46paddr_t          hba_cmd_list_paddr[NB_IOC_CHANNELS];
[258]47
48// command tables physical addresses array (32 command tables per channel)
[295]49paddr_t          hba_cmd_table_paddr[NB_IOC_CHANNELS][32];
[258]50
51// command list pointer array (one per channel)
[295]52unsigned int     hba_cmd_slot[NB_IOC_CHANNELS];
[258]53
[295]54//////////////////////////////////////////////////////////////////////////////
55// This low level function returns the value of register (channel / index)
56//////////////////////////////////////////////////////////////////////////////
57unsigned int _hba_get_register( unsigned int channel,
58                                unsigned int index )
[258]59{
[320]60    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + channel*HBA_SPAN + index;
[295]61    return _io_extended_read( vaddr );
62}
[258]63
[295]64//////////////////////////////////////////////////////////////////////////////
65// This low level function set a new value in register (channel / index) 
66//////////////////////////////////////////////////////////////////////////////
67void _hba_set_register( unsigned int channel,
68                        unsigned int index,
69                        unsigned int value )
70{
[320]71    unsigned int* vaddr = (unsigned int*)SEG_IOC_BASE + channel*HBA_SPAN + index;
[295]72    _io_extended_write( vaddr, value );
[258]73}
74
[295]75
[258]76///////////////////////////////////////////////////////////////////////////////
77// This function register a command in both the command list
78// and the command table, and updates the HBA_PXCI register.
79// It uses the AHCI Scatter/Gather mechanisme to split the user
80// buffer in several physical buffers, with the constraint that each physical
81// buffer must be an integer number of blocks entirely contained in a single
82// page frame.
[437]83// return 0 if success, -1 if error
[258]84///////////////////////////////////////////////////////////////////////////////
[295]85unsigned int _hba_cmd_set( unsigned int  channel,     // channel index
86                           unsigned int  is_read,     // to memory
[258]87                           unsigned int  lba,         // logic block address
[295]88                           paddr_t       buffer,      // buffer physical address
[258]89                           unsigned int  count )      // number of blocks
90{
91    unsigned int       block_size;     // defined by the block device (bytes)
92    unsigned int       pxci;           // command list status
93    unsigned int       cmd_id;         // command index in command list
[295]94
[258]95    hba_cmd_desc_t*    cmd_desc;       // command descriptor pointer   
96    hba_cmd_table_t*   cmd_table;      // command table pointer
97
[289]98    block_size = _hba_get_block_size();
[258]99
100    // check buffer alignment
[295]101    if( buffer & (block_size-1) )
[258]102    {
[437]103        _puts("\n[GIET ERROR] in _hba_set_cmd() : user buffer not block aligned\n");
104        return -1;
[258]105    }
106
[295]107    // get command list status from PXCI register
108    pxci = _hba_get_register( channel, HBA_PXCI );
[258]109
110    // get command index and return error if command list full
[295]111    cmd_id = hba_cmd_slot[channel];
[258]112    if( pxci & (1<<cmd_id ) ) 
113    {
[437]114        _puts("\n[GIET ERROR] in _hba_set_cmd() : command list full for channel ");
115        _putd( channel );
116        _puts("\n");
117        return -1;
[258]118    }
119
120    // compute pointers on command descriptor and command table   
[295]121    cmd_desc  = (hba_cmd_desc_t*)(&(hba_cmd_list[channel].desc[cmd_id]));
122    cmd_table = (hba_cmd_table_t*)(&(hba_cmd_table[channel][cmd_id]));
[258]123
[295]124    // set  buffer descriptor in command table
125    cmd_table->entry[0].dba  = (unsigned int)(buffer);
126    cmd_table->entry[0].dbau = (unsigned int)(buffer >> 32);
127    cmd_table->entry[0].dbc  = count * block_size;
128
129    // initialize command table header
130    cmd_table->header.lba0 = (char)lba;
131    cmd_table->header.lba1 = (char)(lba>>8);
132    cmd_table->header.lba2 = (char)(lba>>16);
133    cmd_table->header.lba3 = (char)(lba>>24);
134    cmd_table->header.lba4 = 0;
135    cmd_table->header.lba5 = 0;
136
137    // initialise command descriptor
138    cmd_desc->prdtl[0] = 1;
139    cmd_desc->prdtl[1] = 0;
140    cmd_desc->ctba     = (unsigned int)(hba_cmd_table_paddr[channel][cmd_id]);
141    cmd_desc->ctbau    = (unsigned int)(hba_cmd_table_paddr[channel][cmd_id]>>32);
142    if( is_read ) cmd_desc->flag[0] = 0x00;
143    else          cmd_desc->flag[0] = 0x40;     
144   
145    // update PXCI register
146    _hba_set_register( channel, HBA_PXCI, (1<<cmd_id) );
147
148    // update command pointer
149    hba_cmd_slot[channel] = (cmd_id + 1)%32;
150
151    return  0;
152} 
153
154/* This can be used for a future use with buffer in virtual space
155
[258]156    // get user space page table virtual address
157    user_pt_vbase     = _get_context_slot(CTX_PTAB_ID);
158    vpn_min           = buf_vaddr >> 12;
159    vpn_max           = (buf_vaddr + (block_size*count) - 1) >> 12;
160    offset            = buf_vaddr & 0xFFF;
161    offset_last       = (buf_vaddr + (block_size*count) - 1) & 0xFFF;
162
163    // initialize all buffer descriptors in command table
164    // (loop on all virtual pages covering the user buffer)
165    for( vpn = vpn_min, buf_id = 0 ; vpn <= vpn_max ; vpn++ )
166    {
167        paddr_t      paddr;
168        unsigned int count;
169        unsigned int ppn;
170        unsigned int flags;
171        unsigned int ko;
172        unsigned int buf_id = 0;
173
174        // get ppn and flags
[481]175        _v2p_translate( (page_table_t*)user_pt_vbase,
176                        vpn,
177                        &ppn,
178                        &flags );
[258]179
180        // check access rights
181        if ((flags & PTE_U) == 0)
182        {
[437]183            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not in user space\n");
184            return -1;
[258]185        }
186        if (((flags & PTE_W) == 0 ) && (is_read == 0) )
187        {
[437]188            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not writable\n");
189            return -1;
[258]190        }
191
192        // check buffer index overflow
193        if( buf_id > 245 )
194        {
[437]195            _puts("[GIET ERROR] in _hba_set_cmd() : max number of buffers is 248\n");
196            return -1;   
[258]197        }
198
199        // buffer allocation
200        if( vpn == vpn_min )       // first page: one single buffer
201        {
202            paddr = (((paddr_t)ppn) << 12) + offset;
203            count = 0x1000 - offset;
204            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
205            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
206            cmd_table->entry[buf_id].dbc  = count;
207
208            buf_id++;
209        }
210        else if( vpn == vpn_max )  // last page: one single buffer
211        {
212            paddr = (((paddr_t)ppn) << 12);
213            count = offset_last;
214            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
215            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
216            cmd_table->entry[buf_id].dbc  = count;
217
218            buf_id++;
219        }
220        else if( offset )          // midle page and offset != 0: two buffers 
221        {
222            paddr = (((paddr_t)ppn) << 12);
223           
224            count = offset;
225            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
226            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
227            cmd_table->entry[buf_id].dbc  = count;
228
229            buf_id++;
230
231            paddr = (((paddr_t)ppn) << 12) + offset;
232            count = 0x1000 - offset;
233            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
234            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
235            cmd_table->entry[buf_id].dbc  = count;
236
237            buf_id++;
238        }
239        else                      // middle page and offset == 0: one buffer
240        {
241            paddr = (((paddr_t)ppn) << 12);
242            count = 0x1000;
243            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
244            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
245            cmd_table->entry[buf_id].dbc  = count;
246
247            buf_id++;
248        }
249    }
[295]250*/
[258]251
252
[437]253//////////////////////////////////////////////
[289]254unsigned int _hba_init( unsigned int channel )
[258]255{
256    unsigned int ppn;
257    unsigned int flags;
258    unsigned int vbase;
259    unsigned int c;               // c == command index
260
261    // get page_table pointer
262    unsigned int pt = _get_context_slot(CTX_PTAB_ID);
263
[295]264    // HBA registers TODO: ne faut_il pas un V2P pour PXCLB/PXCLBU ? (AG)
265    _hba_set_register( channel, HBA_PXCLB , (unsigned int)&hba_cmd_list[channel] );
266    _hba_set_register( channel, HBA_PXCLBU, 0 );
267    _hba_set_register( channel, HBA_PXIE  , 0x40000001 );
268    _hba_set_register( channel, HBA_PXIS  , 0 );
269    _hba_set_register( channel, HBA_PXCI  , 0 );
270    _hba_set_register( channel, HBA_PXCMD , 1 );
[258]271
272    // command list pointer       
[289]273    hba_cmd_slot[channel] = 0;
[258]274
275    // Command list physical addresse
[289]276    vbase = (unsigned int)(&hba_cmd_list[channel]);
[481]277    _v2p_translate( (page_table_t*)pt,
278                     vbase>>12,
279                     &ppn,
280                     &flags );
[289]281    hba_cmd_list_paddr[channel] = ((paddr_t)ppn) | (vbase & 0xFFF);
[258]282
283    // Command tables physical addresses
284    for( c=0 ; c<32 ; c++ )
285    {
[289]286        vbase = (unsigned int)(&hba_cmd_table[channel][c]);
[481]287        _v2p_translate( (page_table_t*)pt,
288                         vbase>>12,
289                         &ppn,
290                         &flags );
[289]291        hba_cmd_table_paddr[channel][c] = ((paddr_t)ppn) | (vbase & 0xFFF);
[258]292    }
[289]293
294    return 0;
[258]295}
296
[437]297///////////////////////////////////////////////
298unsigned int _hba_write( unsigned int  channel,
299                         unsigned int  mode,
300                         unsigned int  lba,
301                         paddr_t       buffer, 
302                         unsigned int  count )
303{
304    return _hba_cmd_set( channel, 
305                         0,         // write
306                         lba, 
307                         buffer, 
308                         count );
309}
310
311//////////////////////////////////////////////
312unsigned int _hba_read( unsigned int  channel,
313                        unsigned int  mode,
314                        unsigned int  lba, 
315                        paddr_t       buffer, 
316                        unsigned int  count )
317{
318    return _hba_cmd_set( channel,
319                         1,          // read
320                         lba, 
321                         buffer, 
322                         count );
323}
324
325//////////////////////////////////
[289]326unsigned int _hba_get_block_size()
327{
328    // TODO The block size must be obtained from the hardware...
329    return 512;
330}
[258]331
[437]332////////////////////////////////////////////////////
[295]333unsigned int _hba_get_status( unsigned int channel ) 
334{
[258]335
[295]336    if( channel >= NB_IOC_CHANNELS )
337    {
[437]338        _puts("\n[GIET ERROR] in _hba_get_status() : illegal channel\n");
[295]339        _exit();
340    }
341
342    // get HBA_PXIS value
343    unsigned int status = _hba_get_register( channel, HBA_PXIS );
344
345    // reset HBA_PXIS
346    _hba_set_register( channel, HBA_PXIS, 0 );
347
348    return status;
349}
350
[258]351// Local Variables:
352// tab-width: 4
353// c-basic-offset: 4
354// c-file-offsets:((innamespace . 0)(inline-open . 0))
355// indent-tabs-mode: nil
356// End:
357// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
358
Note: See TracBrowser for help on using the repository browser.