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

Last change on this file since 355 was 320, checked in by alain, 10 years ago

All drivers have been modified to use only the information
contained in the hard_config.h file

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