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

Last change on this file since 479 was 456, checked in by alain, 10 years ago

Defining the NIC and CMA drivers (validated by the classif application).
Updating other drivers to comply with the new tty0 common file.

File size: 12.8 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
175        ko = _v2p_translate( (page_table_t*)user_pt_vbase,
176                              vpn,
177                              &ppn,
178                              &flags );
179
180        // check access rights
181        if ( ko )
182        {
[437]183            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer unmapped\n");
184            return -1;
[258]185        }
186        if ((flags & PTE_U) == 0)
187        {
[437]188            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not in user space\n");
189            return -1;
[258]190        }
191        if (((flags & PTE_W) == 0 ) && (is_read == 0) )
192        {
[437]193            _puts("[GIET ERROR] in _hba_set_cmd() : user buffer not writable\n");
194            return -1;
[258]195        }
196
197        // check buffer index overflow
198        if( buf_id > 245 )
199        {
[437]200            _puts("[GIET ERROR] in _hba_set_cmd() : max number of buffers is 248\n");
201            return -1;   
[258]202        }
203
204        // buffer allocation
205        if( vpn == vpn_min )       // first page: one single buffer
206        {
207            paddr = (((paddr_t)ppn) << 12) + offset;
208            count = 0x1000 - offset;
209            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
210            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
211            cmd_table->entry[buf_id].dbc  = count;
212
213            buf_id++;
214        }
215        else if( vpn == vpn_max )  // last page: one single buffer
216        {
217            paddr = (((paddr_t)ppn) << 12);
218            count = offset_last;
219            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
220            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
221            cmd_table->entry[buf_id].dbc  = count;
222
223            buf_id++;
224        }
225        else if( offset )          // midle page and offset != 0: two buffers 
226        {
227            paddr = (((paddr_t)ppn) << 12);
228           
229            count = offset;
230            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
231            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
232            cmd_table->entry[buf_id].dbc  = count;
233
234            buf_id++;
235
236            paddr = (((paddr_t)ppn) << 12) + offset;
237            count = 0x1000 - offset;
238            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
239            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
240            cmd_table->entry[buf_id].dbc  = count;
241
242            buf_id++;
243        }
244        else                      // middle page and offset == 0: one buffer
245        {
246            paddr = (((paddr_t)ppn) << 12);
247            count = 0x1000;
248            cmd_table->entry[buf_id].dba  = (unsigned int)(paddr);
249            cmd_table->entry[buf_id].dbau = (unsigned int)(paddr >> 32);
250            cmd_table->entry[buf_id].dbc  = count;
251
252            buf_id++;
253        }
254    }
[295]255*/
[258]256
257
[437]258//////////////////////////////////////////////
[289]259unsigned int _hba_init( unsigned int channel )
[258]260{
261    unsigned int ppn;
262    unsigned int flags;
263    unsigned int fail;
264    unsigned int vbase;
265    unsigned int c;               // c == command index
266
267    // get page_table pointer
268    unsigned int pt = _get_context_slot(CTX_PTAB_ID);
269
[295]270    // HBA registers TODO: ne faut_il pas un V2P pour PXCLB/PXCLBU ? (AG)
271    _hba_set_register( channel, HBA_PXCLB , (unsigned int)&hba_cmd_list[channel] );
272    _hba_set_register( channel, HBA_PXCLBU, 0 );
273    _hba_set_register( channel, HBA_PXIE  , 0x40000001 );
274    _hba_set_register( channel, HBA_PXIS  , 0 );
275    _hba_set_register( channel, HBA_PXCI  , 0 );
276    _hba_set_register( channel, HBA_PXCMD , 1 );
[258]277
278    // command list pointer       
[289]279    hba_cmd_slot[channel] = 0;
[258]280
281    // Command list physical addresse
[289]282    vbase = (unsigned int)(&hba_cmd_list[channel]);
[258]283    fail = _v2p_translate( (page_table_t*)pt,
284                           vbase>>12,
285                           &ppn,
286                           &flags );
287    if ( fail )
288    {
[437]289        _puts("[GIET ERROR] in _hba_init() : command list unmapped\n");
290        return -1;
[258]291    }
[289]292    hba_cmd_list_paddr[channel] = ((paddr_t)ppn) | (vbase & 0xFFF);
[258]293
294    // Command tables physical addresses
295    for( c=0 ; c<32 ; c++ )
296    {
[289]297        vbase = (unsigned int)(&hba_cmd_table[channel][c]);
[258]298        fail = _v2p_translate( (page_table_t*)pt,
299                               vbase>>12,
300                               &ppn,
301                               &flags );
302        if ( fail )
303        {
[437]304            _puts("[GIET ERROR] in _hba_init() : command table unmapped\n");
305            return -1;
[258]306        }
[289]307        hba_cmd_table_paddr[channel][c] = ((paddr_t)ppn) | (vbase & 0xFFF);
[258]308    }
[289]309
310    return 0;
[258]311}
312
[437]313///////////////////////////////////////////////
314unsigned int _hba_write( unsigned int  channel,
315                         unsigned int  mode,
316                         unsigned int  lba,
317                         paddr_t       buffer, 
318                         unsigned int  count )
319{
320    return _hba_cmd_set( channel, 
321                         0,         // write
322                         lba, 
323                         buffer, 
324                         count );
325}
326
327//////////////////////////////////////////////
328unsigned int _hba_read( unsigned int  channel,
329                        unsigned int  mode,
330                        unsigned int  lba, 
331                        paddr_t       buffer, 
332                        unsigned int  count )
333{
334    return _hba_cmd_set( channel,
335                         1,          // read
336                         lba, 
337                         buffer, 
338                         count );
339}
340
341//////////////////////////////////
[289]342unsigned int _hba_get_block_size()
343{
344    // TODO The block size must be obtained from the hardware...
345    return 512;
346}
[258]347
[437]348////////////////////////////////////////////////////
[295]349unsigned int _hba_get_status( unsigned int channel ) 
350{
[258]351
[295]352    if( channel >= NB_IOC_CHANNELS )
353    {
[437]354        _puts("\n[GIET ERROR] in _hba_get_status() : illegal channel\n");
[295]355        _exit();
356    }
357
358    // get HBA_PXIS value
359    unsigned int status = _hba_get_register( channel, HBA_PXIS );
360
361    // reset HBA_PXIS
362    _hba_set_register( channel, HBA_PXIS, 0 );
363
364    return status;
365}
366
[258]367// Local Variables:
368// tab-width: 4
369// c-basic-offset: 4
370// c-file-offsets:((innamespace . 0)(inline-open . 0))
371// indent-tabs-mode: nil
372// End:
373// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
374
Note: See TracBrowser for help on using the repository browser.