Changeset 4 for trunk/kernel/drivers/soclib/soclib_dma.c
- Timestamp:
- Apr 26, 2017, 2:10:21 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/kernel/drivers/soclib/soclib_dma.c
r1 r4 1 1 /* 2 * soclib_dma.c - soclib DMA driver2 * soclib_dma.c - soclib Multi Channels DMA driver implementation 3 3 * 4 * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless 5 * Copyright (c) 2011,2012 UPMC Sorbonne Universites 4 * Author Alain Greiner (2017) 5 6 * Copyright (c) UPMC Sorbonne Universites 6 7 * 7 * This file is part of ALMOS- kernel.8 * This file is part of ALMOS-MKH. 8 9 * 9 * ALMOS- kernelis free software; you can redistribute it and/or modify it10 * ALMOS-MKH is free software; you can redistribute it and/or modify it 10 11 * under the terms of the GNU General Public License as published by 11 12 * the Free Software Foundation; version 2.0 of the License. 12 13 * 13 * ALMOS- kernelis distributed in the hope that it will be useful, but14 * ALMOS-MKH is distributed in the hope that it will be useful, but 14 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU … … 17 18 * 18 19 * You should have received a copy of the GNU General Public License 19 * along with ALMOS- kernel; if not, write to the Free Software Foundation,20 * along with ALMOS-MKH; if not, write to the Free Software Foundation, 20 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 22 */ 22 23 23 #include <errno.h> 24 #include <cpu.h> 25 #include <device.h> 26 #include <driver.h> 27 #include <drvdb.h> 28 #include <kmem.h> 29 #include <scheduler.h> 24 #include <hal_types.h> 25 #include <chdev.h> 26 #include <dev_dma.h> 30 27 #include <thread.h> 31 #include <task.h>32 #include <list.h>33 #include <event.h>34 28 #include <soclib_dma.h> 35 #include <string.h>36 29 37 / * DMA mapped registers offset */38 #define DMA_SRC_REG 0 39 #define DMA_DST_REG 1 40 #define DMA_LEN_REG 2 41 #define DMA_RESET_REG 3 42 #define DMA_IRQ_DISABLED 4 30 /////////////////////////////////////// 31 void soclib_dma_init( chdev_t * chdev ) 32 { 33 // get hardware device cluster and local pointer 34 cxy_t dma_cxy = GET_CXY( chdev->base ); 35 uint32_t * dma_ptr = (uint32_t *)GET_PTR( chdev->base ); 43 36 44 struct dma_context_s 37 // enable interrupts 38 hal_remote_sw( XPTR( dma_cxy , dma_ptr + DMA_IRQ_DISABLED ) , 0 ); 39 40 } // soclib_dma_init() 41 42 ////////////////////////////////////////////////////////////////// 43 void __attribute__ ((noinline)) soclib_dma_cmd( xptr_t thread_xp ) 45 44 { 46 struct list_entry request_queue; 47 struct wait_queue_s pending; 48 }; 45 xptr_t dev_xp; // extended pointer on DMA devive 46 xptr_t dst_xp; // extended pointer on destination buffer 47 xptr_t src_xp; // extended pointer on source buffer 48 uint32_t size; // buffer size 49 49 50 static void dma_start_request(struct device_s *dma, dev_request_t *ptr) 50 // get client thread cluster and local pointer 51 cxy_t thread_cxy = GET_CXY( thread_xp ); 52 thread_t * thread_ptr = (thread_t *)GET_PTR( thread_xp ); 53 54 // get command arguments and extended pointer on DMA device 55 dev_xp = (xptr_t)hal_remote_lwd( XPTR( thread_cxy , &thread_ptr->command.dma.dev_xp ) ); 56 dst_xp = (xptr_t)hal_remote_lwd( XPTR( thread_cxy , &thread_ptr->command.dma.dst_xp ) ); 57 src_xp = (xptr_t)hal_remote_lwd( XPTR( thread_cxy , &thread_ptr->command.dma.src_xp ) ); 58 size = hal_remote_lw ( XPTR( thread_cxy , &thread_ptr->command.dma.size ) ); 59 60 // get DMA device cluster and local pointer 61 cxy_t dev_cxy = GET_CXY( dev_xp ); 62 chdev_t * dev_ptr = (chdev_t *)GET_PTR( dev_xp ); 63 64 // get extended pointer on SOCLIB-DMA peripheral 65 xptr_t dma_xp = hal_remote_lw( XPTR( dev_cxy , &dev_ptr->base ) ); 66 67 // get SOCLIB_DMA device cluster and local pointer 68 cxy_t dma_cxy = GET_CXY( dma_xp ); 69 uint32_t * dma_ptr = (uint32_t *)GET_PTR( dma_xp ); 70 71 // get DMA channel index and channel base address 72 uint32_t * base = dma_ptr + DMA_SPAN * dev_ptr->channel; 73 74 // split dst and src buffers addresses in two 32 bits words 75 uint32_t dst_lsb = (uint32_t)(dst_xp); 76 uint32_t dst_msb = (uint32_t)(dst_xp>>32); 77 uint32_t src_lsb = (uint32_t)(src_xp); 78 uint32_t src_msb = (uint32_t)(src_xp>>32); 79 80 // set SOCLIB_DMA registers to start tranfer operation 81 hal_remote_sw( XPTR( dma_cxy , base + DMA_SRC ) , src_lsb ); 82 hal_remote_sw( XPTR( dma_cxy , base + DMA_SRC_EXT ) , src_msb ); 83 hal_remote_sw( XPTR( dma_cxy , base + DMA_DST ) , dst_lsb ); 84 hal_remote_sw( XPTR( dma_cxy , base + DMA_DST_EXT ) , dst_msb ); 85 hal_remote_sw( XPTR( dma_cxy , base + DMA_LEN ) , size ); 86 87 // Block and deschedule server thread 88 thread_block( CURRENT_THREAD , THREAD_BLOCKED_DEV_ISR ); 89 sched_yield(); 90 91 } // soclib_dma_cmd() 92 93 ///////////////////////////////////////////////////////////////// 94 void __attribute__ ((noinline)) soclib_dma_isr( chdev_t * chdev ) 51 95 { 52 volatile uint32_t *base = dma->base; 53 dev_request_t *rq; 96 // get extended pointer on client thread 97 xptr_t root = XPTR( local_cxy , &chdev->wait_root ); 98 xptr_t client_xp = XLIST_FIRST_ELEMENT( root , thread_t , wait_list ); 54 99 55 rq = (ptr->flags & DEV_RQ_NESTED) ? ptr->data : ptr; 100 // get extended pointer on server thread 101 xptr_t server_xp = XPTR( local_cxy , &chdev->server ); 56 102 57 base[DMA_SRC_REG] = (unsigned int)rq->src; 58 base[DMA_DST_REG] = (unsigned int)rq->dst; 59 base[DMA_IRQ_DISABLED] = 0; 60 base[DMA_LEN_REG] = rq->count; 61 } 103 // get client thread cluster and local pointer 104 cxy_t client_cxy = GET_CXY( client_xp ); 105 thread_t * client_ptr = (thread_t *)GET_PTR( client_xp ); 62 106 63 static void dma_irq_handler(struct irq_action_s *action) 64 { 65 struct dma_context_s *ctx; 66 struct device_s *dma; 67 dev_request_t *rq; 68 dev_request_t *frag; 69 dev_request_t *new_rq; 70 error_t err; 71 volatile uint32_t *base; 72 struct thread_s *this; 107 // get SOCLIB_DMA peripheral cluster and local pointer 108 cxy_t dma_cxy = GET_CXY( chdev->base ); 109 uint32_t * dma_ptr = (uint32_t *)GET_PTR( chdev->base ); 73 110 74 dma = action->dev; 75 base = dma->base; 76 ctx = (struct dma_context_s*)dma->data; 111 // get DMA channel base address 112 uint32_t * base = dma_ptr + (DMA_SPAN * chdev->channel); 77 113 78 cpu_spinlock_lock(&dma->lock.val); 79 80 err = base[DMA_LEN_REG]; 81 base[DMA_RESET_REG] = 0; 114 // get DMA status register 115 uint32_t status = hal_remote_lw( XPTR( dma_cxy , base + DMA_LEN ) ); 82 116 83 /* even if this case is realy low probable, we have to check for empty list ! */ 84 if(list_empty(&ctx->request_queue)) 85 { 86 cpu_spinlock_unlock(&dma->lock.val); 87 return; 88 } 117 // acknowledge IRQ 118 hal_remote_sw( XPTR( dma_cxy , base + DMA_RESET ) , 0 ); 89 119 90 rq = list_first(&ctx->request_queue, dev_request_t, list); 120 // set operation status in command 121 error_t error = ( status != DMA_SUCCESS ); 122 hal_remote_sw( XPTR( client_cxy , &client_ptr->command.dma.error ) , error ); 91 123 92 if(rq->flags & DEV_RQ_NESTED) 93 { 94 frag = rq->data; 124 // unblock server thread 125 thread_unblock( server_xp , THREAD_BLOCKED_DEV_ISR ); 95 126 96 event_set_error(&frag->event, err); 97 event_set_senderId(&frag->event, dma); 98 event_send(&frag->event, current_cpu->gid); 127 // unblock client thread 128 thread_unblock( client_xp , THREAD_BLOCKED_IO ); 99 129 100 if((err == 0) && (frag->data != NULL)) 101 { 102 rq->data = frag->data; 103 dma_start_request(dma,frag->data); 104 cpu_spinlock_unlock(&dma->lock.val); 105 return; 106 } 107 } 108 109 list_unlink(&rq->list); 110 111 if(!(list_empty(&ctx->request_queue))) 112 { 113 new_rq = list_first(&ctx->request_queue, dev_request_t, list); 114 dma_start_request(dma,new_rq); 115 } 116 117 if(!(rq->flags & DEV_RQ_NOBLOCK)) 118 { 119 rq->err = err; 120 wakeup_one(&ctx->pending, WAIT_FIRST); 121 cpu_spinlock_unlock(&dma->lock.val); 122 return; 123 } 124 125 cpu_spinlock_unlock(&dma->lock.val); 126 127 this = CURRENT_THREAD; 128 129 event_set_error(&rq->event, err); 130 event_set_senderId(&rq->event, dma); 131 event_send(&rq->event, current_cpu->gid); 132 } 133 134 static void dma_fraglist_destroy(dev_request_t *ptr) 135 { 136 kmem_req_t req; 137 dev_request_t *frag; 138 139 req.type = KMEM_DMA_REQUEST; 140 frag = ptr; 141 142 while(frag != NULL); 143 { 144 req.ptr = frag; 145 frag = frag->data; 146 147 kmem_free(&req); 148 } 149 } 150 151 static EVENT_HANDLER(dma_async_frag_event) 152 { 153 dev_request_t *frag; 154 kmem_req_t req; 155 error_t err; 156 157 frag = event_get_argument(event); 158 err = event_get_error(event); 159 160 assert(frag != NULL && "corrupted frag"); 161 162 if(err) 163 { 164 dma_fraglist_destroy(frag); 165 return 0; 166 } 167 168 req.type = KMEM_DMA_REQUEST; 169 req.ptr = frag; 170 171 kmem_free(&req); 172 return 0; 173 } 174 175 static error_t dma_frag_init(dev_request_t *frag, uint_t src_start, uint_t dst_start, uint_t count) 176 { 177 //FIXME(40) use extent registers 178 frag->src = task_vaddr2paddr(current_task, (void*)src_start); 179 frag->dst = task_vaddr2paddr(current_task, (void*)dst_start); 180 frag->count = count; 181 frag->flags = 0; 182 183 if((frag->src == NULL) || (frag->dst == NULL)) 184 return EPERM; 185 186 event_set_priority(&frag->event, E_FUNC); 187 event_set_handler(&frag->event, &dma_async_frag_event); 188 event_set_argument(&frag->event, frag); 189 return 0; 190 } 191 192 static error_t dma_fraglist_build(dev_request_t *rq) 193 { 194 kmem_req_t req; 195 dev_request_t *frag; 196 dev_request_t *parent_frag; 197 struct slist_entry *root; 198 struct slist_entry *iter; 199 uint_t src_start; 200 uint_t dst_start; 201 uint_t count; 202 error_t err; 203 204 if(rq->count == 0) 205 return EINVAL; 206 207 src_start = (uint_t)rq->src; 208 dst_start = (uint_t)rq->dst; 209 210 if((src_start + rq->count) <= ((src_start & ~PMM_PAGE_MASK) + PMM_PAGE_SIZE) && 211 (dst_start + rq->count) <= ((dst_start & ~PMM_PAGE_MASK) + PMM_PAGE_SIZE)) 212 { 213 //FIXME(40) use extent registers 214 rq->src = task_vaddr2paddr(current_task, rq->src); 215 rq->dst = task_vaddr2paddr(current_task, rq->dst); 216 217 if((rq->src == NULL) || (rq->dst == NULL)) 218 return EPERM; 219 220 return 0; 221 } 222 223 if((src_start & PMM_PAGE_MASK) || (dst_start & PMM_PAGE_MASK)) { 224 return ENOSYS; 225 } 226 227 req.type = KMEM_DMA_REQUEST; 228 req.size = sizeof(*rq); 229 req.flags = AF_KERNEL; 230 root = (struct slist_entry *)&rq->data; 231 iter = (struct slist_entry *)&rq->list; 232 count = rq->count; 233 rq->data = NULL; 234 rq->flags &= ~DEV_RQ_NESTED; 235 parent_frag = rq; 236 237 while(count) 238 { 239 frag = kmem_alloc(&req); 240 241 if(frag == NULL) 242 { 243 err = ENOMEM; 244 goto fail_nomem; 245 } 246 247 if(count >= PMM_PAGE_SIZE) 248 { 249 err = dma_frag_init(frag, src_start, dst_start, PMM_PAGE_SIZE); 250 251 if(err) 252 goto fail_init; 253 254 src_start += PMM_PAGE_SIZE; 255 dst_start += PMM_PAGE_SIZE; 256 count -= PMM_PAGE_SIZE; 257 } 258 else 259 { 260 err = dma_frag_init(frag, src_start, dst_start, count); 261 262 if(err) 263 goto fail_init; 264 265 count -= count; 266 } 267 268 parent_frag->data = frag; 269 frag->data = NULL; 270 parent_frag = frag; 271 } 272 273 rq->flags |= DEV_RQ_NESTED; 274 return 0; 275 276 fail_init: 277 fail_nomem: 278 dma_fraglist_destroy(rq->data); 279 return err; 280 } 130 } // soclib_dma_isr() 281 131 282 132 283 static sint_t dma_request(struct device_s *dma, dev_request_t *rq)284 {285 struct dma_context_s *ctx;286 uint_t irq_state;287 sint_t err;288 133 289 rq->err = 0;290 ctx = (struct dma_context_s*)dma->data;291 292 err = dma_fraglist_build(rq);293 294 if(err)295 return err;296 297 spinlock_lock_noirq(&dma->lock,&irq_state);298 299 if(list_empty(&ctx->request_queue))300 {301 list_add(&ctx->request_queue, &rq->list);302 dma_start_request(dma, rq);303 }304 else305 list_add_last(&ctx->request_queue, &rq->list);306 307 if(rq->flags & DEV_RQ_NOBLOCK)308 {309 spinlock_unlock_noirq(&dma->lock, irq_state);310 return 0;311 }312 313 wait_on(&ctx->pending, WAIT_LAST);314 spinlock_unlock_noirq(&dma->lock,irq_state);315 sched_sleep(CURRENT_THREAD);316 317 return rq->err;318 }319 320 static sint_t dma_open(struct device_s *dma, dev_request_t *rq)321 {322 return EPERM;323 }324 325 struct device_s *__sys_dma;326 static uint_t dma_count = 0;327 328 error_t soclib_dma_init(struct device_s *dma)329 {330 kmem_req_t req;331 struct dma_context_s *ctx;332 333 spinlock_init(&dma->lock,"DevDMA (SoCLib)");334 dma->type = DEV_BLK;335 336 dma->action.dev = dma;337 dma->action.irq_handler = &dma_irq_handler;338 dma->action.data = NULL;339 340 if(dma_count == 0)341 __sys_dma = dma;342 343 sprintk(dma->name,344 #if CONFIG_ROOTFS_IS_VFAT345 "DMA%d"346 #else347 "dma%d"348 #endif349 ,dma_count++);350 351 metafs_init(&dma->node, dma->name);352 353 dma->op.dev.open = &dma_open;354 dma->op.dev.read = &dma_request;355 dma->op.dev.write = &dma_request;356 dma->op.dev.close = NULL;357 dma->op.dev.lseek = NULL;358 dma->op.dev.mmap = NULL;359 dma->op.dev.munmap = NULL;360 dma->op.dev.set_params = NULL;361 dma->op.dev.get_params = NULL;362 dma->op.drvid = SOCLIB_DMA_ID;363 364 req.type = KMEM_GENERIC;365 req.size = sizeof(*ctx);366 req.flags = AF_BOOT | AF_ZERO;367 368 if((ctx = kmem_alloc(&req)) == NULL)369 return ENOMEM;370 371 wait_queue_init(&ctx->pending, dma->name);372 list_root_init(&ctx->request_queue);373 dma->data = (void *)ctx;374 return 0;375 }376 377 driver_t soclib_dma_driver = { .init = &soclib_dma_init };
Note: See TracChangeset
for help on using the changeset viewer.