source: trunk/kernel/drivers/soclib/soclib_dma.c @ 2

Last change on this file since 2 was 1, checked in by alain, 8 years ago

First import

File size: 7.9 KB
Line 
1/*
2 * soclib_dma.c - soclib DMA driver
3 *
4 * Copyright (c) 2008,2009,2010,2011,2012 Ghassan Almaless
5 * Copyright (c) 2011,2012 UPMC Sorbonne Universites
6 *
7 * This file is part of ALMOS-kernel.
8 *
9 * ALMOS-kernel is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2.0 of the License.
12 *
13 * ALMOS-kernel is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 * General Public License for more details.
17 *
18 * 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 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22
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>
30#include <thread.h>
31#include <task.h>
32#include <list.h>
33#include <event.h>
34#include <soclib_dma.h>
35#include <string.h>
36
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
43
44struct dma_context_s
45{
46        struct list_entry request_queue;
47        struct wait_queue_s pending;
48};
49
50static void dma_start_request(struct device_s *dma, dev_request_t *ptr)
51{
52        volatile uint32_t *base = dma->base;
53        dev_request_t *rq;
54
55        rq = (ptr->flags & DEV_RQ_NESTED) ? ptr->data : ptr;
56
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}
62
63static 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;
73
74        dma  = action->dev;
75        base = dma->base;
76        ctx  = (struct dma_context_s*)dma->data;
77
78        cpu_spinlock_lock(&dma->lock.val);
79 
80        err = base[DMA_LEN_REG];
81        base[DMA_RESET_REG] = 0;
82
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        }
89
90        rq = list_first(&ctx->request_queue, dev_request_t, list);
91
92        if(rq->flags & DEV_RQ_NESTED)
93        {
94                frag = rq->data;
95
96                event_set_error(&frag->event, err);
97                event_set_senderId(&frag->event, dma);
98                event_send(&frag->event, current_cpu->gid);
99
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
134static 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
151static 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
175static 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
192static 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
276fail_init:
277fail_nomem:
278        dma_fraglist_destroy(rq->data);
279        return err;
280}
281
282
283static 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
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        else
305                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
320static sint_t dma_open(struct device_s *dma, dev_request_t *rq)
321{
322        return EPERM;
323}
324
325struct device_s *__sys_dma;
326static uint_t dma_count = 0;
327
328error_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_VFAT
345                "DMA%d"
346#else
347                "dma%d"
348#endif
349                ,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
377driver_t soclib_dma_driver = { .init = &soclib_dma_init };
Note: See TracBrowser for help on using the repository browser.