source: branch/giet_vm_ioc_drivers/giet_drivers/bdv_driver.c @ 285

Last change on this file since 285 was 284, checked in by cfuguet, 11 years ago

Modification of comments format on SPI-SDCARD driver to respect
GIET-VM format

File size: 11.4 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File      : bdv_driver.c
3// Date      : 23/05/2013
4// Author    : alain greiner
5// Maintainer: cesar fuguet
6// Copyright (c) UPMC-LIP6
7///////////////////////////////////////////////////////////////////////////////////
8// The bdv_driver.c and bdv_driver.h files are part ot the GIET-VM kernel.
9// This driver supports the SocLib vci_block_device component, that is
10// a single channel, block oriented, external storage contrÃŽler.
11//
12// It can exist only one block-device controler in the architecture.
13//
14// The _bdv_read() and _bdv_write() functions use the _bdv_access() function,
15// that is always blocking, but can be called in 4 modes:
16//
17// - In BOOT_PA mode, the _bdv_access() function uses a polling policy on the
18//   IOC_STATUS register to detect transfer completion, as hardware interrupts
19//   are not activated. This mode is used by the boot code to load the map.bin
20//   file into memory.
21//
22// - In BOOT_VA mode, the _bdv_access() function uses a polling policy on
23//   IOC_STATUS register to detect transfer completion. This mode is used by
24//   the boot code to load the various .elf files into memory.
25//
26// - In KERNEL mode, the _bdv_access() function uses a descheduling strategy:
27//   The ISR executed when transfer completes should restart the calling task.
28//   There is no checking of user access right to the memory buffer. This mode
29//   must be used to access IOC, for an "open" system call.
30//
31// - In USER mode, the _bdv_access() function uses a descheduling strategy:
32//   The ISR executed when transfer completes should restart the calling task,
33//   The user access right to the memory buffer must be checked.
34//   This mode must be used to access IOC, for a "read/write" system call.
35//
36// As the BDV component can be used by several programs running in parallel,
37// the _ioc_lock variable guaranties exclusive access to the device.  The
38// _bdv_read() and _bdv_write() functions use atomic LL/SC to get the lock.
39//
40// Finally, the memory buffer must fulfill the following conditions:
41// - The buffer must be word aligned,
42// - The buffer must be mapped in user space for an user access,
43// - The buffer must be writable in case of (to_mem) access,
44// - The total number of physical pages occupied by the user buffer cannot
45//   be larger than 512 pages if the IOMMU is activated,
46// - All physical pages occupied by the user buffer must be contiguous
47//   if the IOMMU is not activated.
48// An error code is returned if these conditions are not verified.
49///////////////////////////////////////////////////////////////////////////////////
50// The seg_ioc_base virtual base addresses must be defined in giet_vsegs.ld file.
51///////////////////////////////////////////////////////////////////////////////////
52
53#include <giet_config.h>
54#include <ioc_driver.h>
55#include <bdv_driver.h>
56#include <utils.h>
57#include <tty_driver.h>
58#include <ctx_handler.h>
59
60///////////////////////////////////////////////////////////////////////////////
61//      _bdv_access()
62// This function transfer data between a memory buffer and the block device.
63// The buffer lentgth is (count*block_size) bytes.
64// Arguments are:
65// - to_mem     : from external storage to memory when non 0.
66// - kernel     : kernel buffer with identity mapping when non 0.
67// - lba        : first block index on the external storage.
68// - buf_vaddr  : virtual base address of the memory buffer.
69// - count      : number of blocks to be transfered.
70// Returns 0 if success, > 0 if error.
71///////////////////////////////////////////////////////////////////////////////
72static unsigned int _bdv_access( unsigned int to_mem,
73                                 unsigned int mode,
74                                 unsigned int lba,
75                                 paddr_t buf_paddr,
76                                 unsigned int count) 
77{
78
79#if GIET_DEBUG_IOC_DRIVER
80_tty_get_lock( 0 );
81_puts("\n[IOC DEBUG] Enter _bdv_access() at cycle ");
82_putd( _get_proctime() );
83_puts(" for processor ");
84_putd( _get_procid() );
85_puts("\n - mode    = ");
86_putd( mode );
87_puts("\n - paddr   = ");
88_putx( buf_paddr );
89_puts("\n - sectors = ");
90_putd( count );
91_puts("\n - lba     = ");
92_putx( lba );
93_puts("\n");
94_tty_release_lock( 0 );
95#endif
96
97    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
98    unsigned int error = 0;
99
100    // get the lock protecting IOC
101    _get_lock(&_ioc_lock);
102
103    // set the _ioc_status polling variable
104    _ioc_status = BLOCK_DEVICE_BUSY;
105
106    ioc_address[BLOCK_DEVICE_BUFFER]     = (unsigned int)buf_paddr;
107    ioc_address[BLOCK_DEVICE_BUFFER_EXT] = (unsigned int)(buf_paddr>>32);
108    ioc_address[BLOCK_DEVICE_COUNT]      = count;
109    ioc_address[BLOCK_DEVICE_LBA]        = lba;
110
111    // There is two policies for transfer completion
112        // detection, depending on the mode argument:
113
114    if ( (mode == IOC_BOOT_PA_MODE) ||    // We poll directly the IOC_STATUS register
115         (mode == IOC_BOOT_VA_MODE) )     // as IRQs are masked.
116    {
117        // Launch transfert
118        if (to_mem == 0) ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
119        else             ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
120
121        unsigned int status;
122        if ( _bdv_get_status(0, &status) ) return 1;
123
124        while( (status != BLOCK_DEVICE_READ_SUCCESS)  &&
125               (status != BLOCK_DEVICE_READ_ERROR)    &&
126               (status != BLOCK_DEVICE_WRITE_SUCCESS) &&
127               (status != BLOCK_DEVICE_WRITE_ERROR) ) 
128        {
129            if ( _bdv_get_status(0, &status) ) return 1;
130
131#if GIET_DEBUG_IOC_DRIVER
132_tty_get_lock( 0 );
133_puts("\n[IOC DEBUG] _bdv_access() : ... waiting on IOC_STATUS register ...\n");
134_tty_release_lock( 0 );
135#endif
136
137        }
138        // analyse status
139        error = ( (status == BLOCK_DEVICE_READ_ERROR) ||
140                  (status == BLOCK_DEVICE_WRITE_ERROR) );
141
142        // release lock
143        _release_lock(&_ioc_lock);     
144    }
145    else                           // in USER or KERNEL mode, we deschedule the task.
146                                   // When the task is rescheduled by the ISR, we reset
147                                   // the _ioc_status variable, and release the lock
148    {
149        // We need a critical section, because we must reset the RUN bit
150                // before to launch the transfer, and we want to avoid to be descheduled
151                // between these two operations.
152
153        // Enter critical section
154        _it_disable(); 
155       
156        // set _ioc_gtid and reset runnable
157        unsigned int ltid = _get_proc_task_id();
158        unsigned int pid = _get_procid();
159        _ioc_gtid = (pid<<16) + ltid;
160        _set_task_slot( pid, ltid, CTX_RUN_ID, 0 ); 
161       
162        // Launch transfert
163        if (to_mem == 0) ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_WRITE;
164        else             ioc_address[BLOCK_DEVICE_OP] = BLOCK_DEVICE_READ;
165
166        // deschedule task
167        _ctx_switch();                     
168
169        // analyse status
170        error = ( (_ioc_status == BLOCK_DEVICE_READ_ERROR) ||
171                  (_ioc_status == BLOCK_DEVICE_WRITE_ERROR) );
172
173        // reset _ioc_status and release lock
174        _ioc_status = BLOCK_DEVICE_IDLE; 
175        _release_lock(&_ioc_lock);     
176    }
177
178#if GIET_DEBUG_IOC_DRIVER
179_tty_get_lock( 0 );
180_puts("\n[IOC DEBUG] _bdv_access completed at cycle ");
181_putd( _get_proctime() );
182_puts(" for processor ");
183_putd( _get_procid() );
184_puts(" : error = ");
185_putd( (unsigned int)error );
186_puts("\n");
187_tty_release_lock( 0 );
188#endif
189
190    return error;
191} // end _bdv_access()
192
193///////////////////////////////////////////////////////////////////////////////
194//       _bdv_init()
195// This function cheks block size, and activates the IOC interrupts.
196// Return 0 for success, > 0 if error
197///////////////////////////////////////////////////////////////////////////////
198unsigned int _bdv_init( unsigned int channel )
199{
200    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base ;
201   
202    if ( ioc_address[BLOCK_DEVICE_BLOCK_SIZE] != 512 )
203    {
204        _tty_get_lock( 0 );
205        _puts("\n[GIET ERROR] in _bdv_init() : block size must be 512 bytes\n");
206        _tty_release_lock( 0 );
207        return 1; 
208    }
209
210    if ( channel != 0 )
211    {
212        _tty_get_lock( 0 );
213        _puts("\n[GIET ERROR] in _bdv_init() : illegal channel\n");
214        _tty_release_lock( 0 );
215
216        return 1;
217    }
218
219    ioc_address[BLOCK_DEVICE_IRQ_ENABLE] = 1;
220    return 0;
221}
222
223///////////////////////////////////////////////////////////////////////////////
224//     _bdv_read()
225// Transfer data from the block device to a memory buffer.
226// - mode     : BOOT / KERNEL / USER
227// - lba      : first block index on the block device
228// - buffer   : base address of the memory buffer (must be word aligned)
229// - count    : number of blocks to be transfered.
230// Returns 0 if success, > 0 if error.
231///////////////////////////////////////////////////////////////////////////////
232unsigned int _bdv_read( unsigned int mode, 
233                        unsigned int lba, 
234                        paddr_t      buffer, 
235                        unsigned int count) 
236{
237    return _bdv_access( 1,        // read access
238                        mode, 
239                        lba,
240                        buffer,
241                        count );
242}
243
244///////////////////////////////////////////////////////////////////////////////
245//     _bdv_write()
246// Transfer data from a memory buffer to the block device.
247// - mode     : BOOT / KERNEL / USER
248// - lba      : first block index on the block device
249// - buffer   : base address of the memory buffer (must be word aligned)
250// - count    : number of blocks to be transfered.
251// Returns 0 if success, > 0 if error.
252///////////////////////////////////////////////////////////////////////////////
253unsigned int _bdv_write( unsigned int mode, 
254                         unsigned int lba, 
255                         paddr_t  buffer, 
256                         unsigned int count ) 
257{
258    return _bdv_access( 0,        // write access
259                        mode, 
260                        lba,
261                        buffer,
262                        count );
263}
264
265///////////////////////////////////////////////////////////////////////////////
266//     _bdv_get_status()
267// This function returns in the status variable, the transfert status, and
268// acknowledge the IRQ if the IOC controler is not busy.
269// Returns 0 if success, > 0 if error
270///////////////////////////////////////////////////////////////////////////////
271unsigned int _bdv_get_status( unsigned int  channel,
272                              unsigned int* status )
273{
274    if ( channel != 0 )
275    {
276        _tty_get_lock( 0 );
277        _puts("\n[GIET ERROR] in _bdv_get_status() : illegal channel\n");
278        _tty_release_lock( 0 );
279
280        return 1;
281    }
282
283    // get IOC base address
284    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
285    *status = ioc_address[BLOCK_DEVICE_STATUS];
286
287    return 0;
288}
289
290///////////////////////////////////////////////////////////////////////////////
291//     _bdv_get_block_size()
292// This function returns the block_size with which the IOC has been configured.
293///////////////////////////////////////////////////////////////////////////////
294unsigned int _bdv_get_block_size() 
295{
296    // get IOC base address
297    volatile unsigned int * ioc_address = (unsigned int *) &seg_ioc_base;
298   
299    return  ioc_address[BLOCK_DEVICE_BLOCK_SIZE];
300}
301
302
303// Local Variables:
304// tab-width: 4
305// c-basic-offset: 4
306// c-file-offsets:((innamespace . 0)(inline-open . 0))
307// indent-tabs-mode: nil
308// End:
309// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
310
Note: See TracBrowser for help on using the repository browser.