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