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

Last change on this file since 315 was 295, checked in by alain, 11 years ago

Introducing a major release, to suppoort the tsar_generic_leti platform
and the various (external or internal) peripherals configurations.
The map.xml format has been modified, in order to support the new
vci_iopic componentand a new policy for peripherals initialisation.
The IRQs are nom described in the XICU and IOPIC components
(and not anymore in the processors).
To enforce this major change, the map.xml file signature changed:
The signature value must be: 0xDACE2014

This new release has been tested on the tsar_generic_leti platform
for the following mappings:

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