source: soft/giet_vm/giet_drivers/dma_driver.c @ 262

Last change on this file since 262 was 258, checked in by alain, 11 years ago

This is a major release, including a deep restructuration of code.
The main evolutions are

  • use of the Tsar preloader to load the GIET boot-loader from disk
  • introduction of a FAT32 file system library,
  • use of this fat32 library by the boot-loader to load the map.bin data structure, and the various .elf files
  • reorganisation of drivers (one file per peripheral).
  • introduction of drivers for new peripherals: vci_chbuf_dma and vci_multi_ahci.
  • introduction of a new physical memory allocator in the boot code.

This release has been tested on the tsar_generic_iob architecture,
for the two following mappings: 4c_1p_iob_four.xml and 4c_1p_iob_sort.xml

File size: 11.7 KB
Line 
1///////////////////////////////////////////////////////////////////////////////////
2// File     : dma_driver.c
3// Date     : 23/11/2013
4// Author   : alain greiner
5// Copyright (c) UPMC-LIP6
6///////////////////////////////////////////////////////////////////////////////////
7// The dma_driver.c and dma_driver.h files are part ot the GIET-VM nano-kernel.
8// This driver supports the SoCLib vci_multi_dma component.
9//
10// It can exist several DMA controlers in the architecture (one per cluster),
11// and each controller can contain several channels.
12//
13// There is  (NB_CLUSTERS * NB_DMA_CHANNELS) channels, indexed by a global index:
14//        dma_id = cluster_id * NB_DMA_CHANNELS + loc_id
15//
16// A DMA channel is a private ressource allocated to a given processor.
17// It is exclusively used by the kernet to speedup data transfers, and
18// there is no lock protecting exclusive access to the channel.
19// As the kernel uses a polling policy on the DMA_STATUS register to detect
20// transfer completion, the DMA IRQ is not used, and there is no DMA_ISR.
21////////////////////////////////////////////////////////////////////////////////////
22// The virtual base address of the segment associated to a channel is:
23//
24//    seg_dma_base + cluster_id * vseg_cluster_increment + DMA_SPAN * channel_id
25//
26////////////////////////////////////////////////////////////////////////////////////
27
28#include <giet_config.h>
29#include <dma_driver.h>
30#include <utils.h>
31#include <tty_driver.h>
32#include <vmem.h>
33
34#if !defined(NB_CLUSTERS)
35# error: You must define NB_CLUSTERS in the hard_config.h file
36#endif
37
38#if (NB_CLUSTERS > 256)
39# error: NB_CLUSTERS cannot be larger than 256!
40#endif
41
42#if !defined(NB_PROCS_MAX)
43# error: You must define NB_PROCS_MAX in the hard_config.h file
44#endif
45
46#if (NB_PROCS_MAX > 8)
47# error: NB_PROCS_MAX cannot be larger than 8!
48#endif
49
50#if (NB_DMA_CHANNELS > 8)
51# error: NB_DMA_CHANNELS cannot be larger than 8!
52#endif
53
54extern unsigned int _ptabs_vaddr[];
55
56//////////////////////////////////////////////////////////////////////////////////
57// AS the GIET-VM uses a polling policy to detect transfer completion,
58// The DMA component initialisation must disable interrupts.
59// This function disables interrupts for one DMA channel in one cluster.
60// Returns 0 if success, returns > 0 if error.
61//////////////////////////////////////////////////////////////////////////////////
62unsigned int _dma_init( unsigned int cluster_id,
63                        unsigned int channel_id )
64{
65#if NB_DMA_CHANNELS > 0
66    // parameters checking
67    if (cluster_id >= NB_CLUSTERS)      return 1;
68    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
69
70    // compute DMA base address
71    unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + 
72                                (cluster_id * (unsigned int)&vseg_cluster_increment));
73
74    // disable interrupt for selected channel
75    dma_address[channel_id * DMA_SPAN + DMA_IRQ_DISABLE] = 1;           
76    return 0;
77#else
78    return 1;
79#endif
80}
81
82//////////////////////////////////////////////////////////////////////////////////
83// This function re-initialises one DMA channel in one cluster after a transfer
84// completion. It actually forces the channel to return in iDLE state.
85//////////////////////////////////////////////////////////////////////////////////
86unsigned int _dma_reset( unsigned int cluster_id, 
87                         unsigned int channel_id ) 
88{
89#if NB_DMA_CHANNELS > 0
90    // parameters checking
91    if (cluster_id >= NB_CLUSTERS)      return 1;
92    if (channel_id >= NB_DMA_CHANNELS)  return 1; 
93
94    // compute DMA base address
95    unsigned int* dma_address = (unsigned int*) ((unsigned int)&seg_dma_base + 
96                                (cluster_id * (unsigned int)&vseg_cluster_increment));
97
98    // reset selected channel
99    dma_address[channel_id * DMA_SPAN + DMA_RESET] = 0;           
100    return 0;
101#else
102    return 1;
103#endif
104}
105
106//////////////////////////////////////////////////////////////////////////////////
107// This function returns the status of a DMA channel in a given cluster
108//////////////////////////////////////////////////////////////////////////////////
109unsigned int _dma_get_status( unsigned int cluster_id, 
110                              unsigned int channel_id ) 
111{
112#if NB_DMA_CHANNELS > 0
113    // parameters checking
114    if (cluster_id >= NB_CLUSTERS)      return 1;
115    if (channel_id >= NB_DMA_CHANNELS)  return 1;
116
117    // compute DMA base address
118    unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + 
119                                 (cluster_id * (unsigned int)&vseg_cluster_increment));
120
121    // get selected channel status
122    return dma_address[channel_id * DMA_SPAN + DMA_LEN];
123#else
124    return DMA_ERROR;
125#endif
126}
127
128//////////////////////////////////////////////////////////////////////////////////
129// This function sets the physical address (including 64 bits extension)
130// for the source and destination buffers in a DMA channel in a given cluster
131// and sets the transfer size to lauch the transfer.
132//////////////////////////////////////////////////////////////////////////////////
133unsigned int _dma_start_transfer( unsigned int       cluster_id,
134                                  unsigned int       channel_id,
135                                  unsigned long long dst_paddr,   // physical address
136                                  unsigned long long src_paddr,   // physical address
137                                  unsigned int       size )       // bytes
138{
139#if NB_DMA_CHANNELS > 0
140    // parameters checking
141    if (cluster_id >= NB_CLUSTERS)      return 1;
142    if (channel_id >= NB_DMA_CHANNELS)  return 1;
143
144    // compute DMA base address
145    unsigned int * dma_address = (unsigned int *) ((unsigned int)&seg_dma_base + 
146                                 (cluster_id * (unsigned int)&vseg_cluster_increment));
147
148    // selected channel configuration and lauching
149    dma_address[channel_id * DMA_SPAN + DMA_SRC]     = (unsigned int)(src_paddr);
150    dma_address[channel_id * DMA_SPAN + DMA_SRC_EXT] = (unsigned int)(src_paddr>>32);
151    dma_address[channel_id * DMA_SPAN + DMA_DST]     = (unsigned int)(dst_paddr);
152    dma_address[channel_id * DMA_SPAN + DMA_DST_EXT] = (unsigned int)(dst_paddr>>32);
153    dma_address[channel_id * DMA_SPAN + DMA_LEN]     = (unsigned int)size;
154    return 0;
155#else
156    return 1;
157#endif
158}
159///////////////////////////////////////////////////////////////////////////////////
160// This function copies a source memory buffer to a destnation memory buffer,
161// using the distributed DMA. As it makes virtual to physical address translation,
162// the MMU should be activated. As there is one DMA channel per processor,
163// the DMA cluster and channel indexes are obtained from the processor index.
164// The source and destination buffers base addresses must be word aligned,
165// and the buffer's size must be multiple of 4.
166// In case of error (buffer unmapped, unaligned, or DMA_STATUS error), an error
167// message is displayed on TTY0, and the system crash.
168///////////////////////////////////////////////////////////////////////////////////
169// Note: this blocking function is supposed to be used by the kernel only,
170// and uses a polling policy on DMA_STATUS register to detect completion.
171// Therefore, the DMA_IRQ is NOT used.
172///////////////////////////////////////////////////////////////////////////////////
173inline void  _dma_copy( unsigned int vspace_id, // vspace index for V2P
174                        void*        dest,      // dest buffer vbase
175                        const void*  source,    // source buffer vbase
176                        unsigned int size )     // bytes
177{
178#if NB_DMA_CHANNELS > 0
179
180    unsigned int procid    = _get_procid();
181    unsigned int cluster_id = procid/NB_PROCS_MAX;
182    unsigned int channel_id = procid%NB_PROCS_MAX;
183
184    unsigned int ko;
185    unsigned int ppn;
186    unsigned int flags;
187
188#if GIET_DEBUG_DMA_DRIVER
189_tty_get_lock( 0 );
190_puts("\n[DMA DEBUG] Enter _dma_copy() at cycle ");
191_putd( _get_proctime() );
192_puts("\n - vspace_id  = ");
193_putx( vspace_id );
194_puts("\n - cluster_id = ");
195_putx( cluster_id );
196_puts("\n - channel_id = ");
197_putx( channel_id );
198_puts("\n - dest       = ");
199_putx( (unsigned int)dest );
200_puts("\n - source     = ");
201_putx( (unsigned int)source );
202_puts("\n - bytes      = ");
203_putd( size );
204_tty_release_lock( 0 );
205#endif
206
207    // checking alignment constraints
208    if ( (((unsigned int)dest) & 0x3)   ||
209         (((unsigned int)source) & 0x3) ||
210         (size & 0x3) )
211    {
212        _tty_get_lock( 0 );
213        _puts("\n[GIET ERROR] in _dma_copy() : buffer unaligned\n");
214        _tty_release_lock( 0 );
215        _exit();
216    }
217
218    // get vspace 0 page table pointer
219    unsigned int pt =  _ptabs_vaddr[vspace_id];
220
221    // get source buffer physical addresse
222    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
223                         ((unsigned int)source)>>12,     // vpn
224                         &ppn,                           // ppn
225                         &flags );                       // flags
226    if ( ko ) 
227    {
228        _tty_get_lock( 0 );
229        _puts("\n[GIET ERROR] in _dma_copy() : source buffer unmapped\n");
230        _tty_release_lock( 0 );
231        _exit();
232    }
233    unsigned long long src_paddr = (((unsigned long long)ppn) << 12) | 
234                                   ((unsigned int)source & 0x00000FFF);
235
236    // get dest buffer physical addresse
237    ko = _v2p_translate( (page_table_t*)pt,              // page table pointer
238                         ((unsigned int)dest)>>12,       // vpn
239                         &ppn,                           // ppn
240                         &flags );                       // flags
241    if ( ko ) 
242    {
243        _tty_get_lock( 0 );
244        _puts("\n[GIET ERROR] in _dma_copy() : dest buffer unmapped\n");
245        _tty_release_lock( 0 );
246        _exit();
247    }
248    unsigned long long dst_paddr = (((unsigned long long)ppn) << 12) | 
249                                   ((unsigned int)dest & 0x00000FFF);
250
251#if GIET_DEBUG_DMA_DRIVER
252_tty_get_lock( 0 );
253_puts("\n - src_paddr  = ");
254_putl( src_paddr );
255_puts("\n - dst_paddr  = ");
256_putl( dst_paddr );
257_puts("\n");
258_tty_release_lock( 0 );
259#endif
260
261    // invalidate L1 cache if no hardware cache coherence
262    if ( GIET_NO_HARD_CC ) _dcache_buf_invalidate( dest, size );
263
264    // dma channel configuration & lauching
265    ko = _dma_start_transfer(  cluster_id, channel_id, dst_paddr, src_paddr, size ); 
266    if ( ko ) 
267    {
268        _tty_get_lock( 0 );
269        _puts("\n[GIET ERROR] in _dma_copy() : cannot start transfer\n");
270        _tty_release_lock( 0 );
271        _exit();
272    }
273
274    // scan dma channel status
275    unsigned int status = _dma_get_status( cluster_id, channel_id );
276    while( (status != DMA_SUCCESS) && 
277           (status != DMA_READ_ERROR) &&
278           (status != DMA_WRITE_ERROR) )
279    {
280        status = _dma_get_status( cluster_id, channel_id );
281
282#if GIET_DEBUG_DMA_DRIVER
283_tty_get_lock( 0 );
284_puts("\n[DMA DEBUG] _dma_copy() : ... waiting on DMA_STATUS register ...\n");
285_tty_release_lock( 0 );
286#endif
287
288    }
289   
290    // analyse status
291    if( status != DMA_SUCCESS )
292    {
293        _tty_get_lock( 0 );
294        _puts("\n[GIET ERROR] in _dma_copy() : DMA_STATUS error = ");
295        _putd( status );
296        _puts("\n");
297        _tty_release_lock( 0 );
298        _exit();
299    }
300    // reset dma channel
301    _dma_reset( cluster_id, channel_id );
302
303#if GIET_DEBUG_DMA_DRIVER
304_tty_get_lock( 0 );
305_puts("\n[DMA DEBUG] _dma_copy() completed at cycle ");
306_putd( _get_proctime() );
307_puts("\n");
308_tty_release_lock( 0 );
309#endif
310
311#else // NB_DMA_CHANNELS == 0
312    _tty_get_lock( 0 );
313    _puts("\n[GIET ERROR] in _dma_copy() : NB_DMA_CHANNELS = 0 !\n");
314    _tty_release_lock( 0 );
315    _exit();
316#endif
317} // end _dma_copy
318
319
320
321
322// tab-width: 4
323// c-basic-offset: 4
324// c-file-offsets:((innamespace . 0)(inline-open . 0))
325// indent-tabs-mode: nil
326// End:
327// vim: filetype=c:expandtab:shiftwidth=4:tabstop=4:softtabstop=4
328
Note: See TracBrowser for help on using the repository browser.