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

Last change on this file since 455 was 437, checked in by alain, 10 years ago

Introducing dynamic allocation of peripheral channel(TTY, NIC, TIM, CMA)
Removint the ICU driver : ICU component not supported anymore.
Removing the FBF driver.

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>
18#include <tty_driver.h>
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.