source: rtems-libbsd/rtems/freebsd/rtems/rtems-bsd-bus-dma.c @ a9153ec

4.1155-freebsd-126-freebsd-12freebsd-9.3
Last change on this file since a9153ec was a9153ec, checked in by Joel Sherrill <joel.sherrill@…>, on 03/07/12 at 15:52:04

Initial import

Code is based on FreeBSD 8.2 with USB support from Sebastian Huber
and Thomas Doerfler. Initial TCP/IP stack work is from Kevel Kirspel.

  • Property mode set to 100644
File size: 11.5 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup rtems_bsd_rtems
5 *
6 * @brief TODO.
7 *
8 * File origin from FreeBSD 'sys/powerpc/powerpc/busdma_machdep.c'.
9 */
10
11/*-
12 * Copyright (c) 2009, 2010 embedded brains GmbH.  All rights reserved.
13 *
14 *  embedded brains GmbH
15 *  Obere Lagerstr. 30
16 *  82178 Puchheim
17 *  Germany
18 *  <rtems@embedded-brains.de>
19 *
20 * Copyright (c) 2004 Olivier Houchard
21 * Copyright (c) 2002 Peter Grehan
22 * Copyright (c) 1997, 1998 Justin T. Gibbs.
23 * All rights reserved.
24 *
25 * Redistribution and use in source and binary forms, with or without
26 * modification, are permitted provided that the following conditions
27 * are met:
28 * 1. Redistributions of source code must retain the above copyright
29 *    notice, this list of conditions, and the following disclaimer,
30 *    without modification, immediately at the beginning of the file.
31 * 2. The name of the author may not be used to endorse or promote products
32 *    derived from this software without specific prior written permission.
33 *
34 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
35 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
38 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 */
46
47#include <rtems/freebsd/machine/rtems-bsd-config.h>
48#include <rtems/freebsd/machine/rtems-bsd-cache.h>
49#include <rtems/malloc.h>
50
51#include <rtems/freebsd/sys/param.h>
52#include <rtems/freebsd/sys/types.h>
53#include <rtems/freebsd/sys/lock.h>
54#include <rtems/freebsd/sys/mutex.h>
55#include <rtems/freebsd/sys/systm.h>
56#include <rtems/freebsd/sys/malloc.h>
57#include <rtems/freebsd/machine/atomic.h>
58#include <rtems/freebsd/machine/bus.h>
59
60#ifdef CPU_DATA_CACHE_ALIGNMENT
61  #define CLSZ ((uintptr_t) CPU_DATA_CACHE_ALIGNMENT)
62  #define CLMASK (CLSZ - (uintptr_t) 1)
63#endif
64
65struct bus_dma_tag {
66        bus_dma_tag_t     parent;
67        bus_size_t      alignment;
68        bus_size_t      boundary;
69        bus_addr_t      lowaddr;
70        bus_addr_t      highaddr;
71        bus_dma_filter_t *filter;
72        void         *filterarg;
73        bus_size_t      maxsize;
74        int            nsegments;
75        bus_size_t      maxsegsz;
76        int            flags;
77        int            ref_count;
78        int            map_count;
79        bus_dma_lock_t   *lockfunc;
80        void             *lockfuncarg;
81};
82
83struct bus_dmamap {
84        void *buffer_begin;
85        bus_size_t buffer_size;
86};
87
88/*
89 * Convenience function for manipulating driver locks from busdma (during
90 * busdma_swi, for example).  Drivers that don't provide their own locks
91 * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
92 * non-mutex locking scheme don't have to use this at all.
93 */
94void
95busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
96{
97        struct mtx *dmtx;
98
99        dmtx = (struct mtx *)arg;
100        switch (op) {
101        case BUS_DMA_LOCK:
102                mtx_lock(dmtx);
103                break;
104        case BUS_DMA_UNLOCK:
105                mtx_unlock(dmtx);
106                break;
107        default:
108                panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
109        }
110}
111
112/*
113 * dflt_lock should never get called.  It gets put into the dma tag when
114 * lockfunc == NULL, which is only valid if the maps that are associated
115 * with the tag are meant to never be defered.
116 * XXX Should have a way to identify which driver is responsible here.
117 */
118static void
119dflt_lock(void *arg, bus_dma_lock_op_t op)
120{
121        panic("driver error: busdma dflt_lock called");
122}
123
124/*
125 * Allocate a device specific dma_tag.
126 */
127int
128bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
129    bus_size_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
130    bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
131    int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
132    void *lockfuncarg, bus_dma_tag_t *dmat)
133{
134        bus_dma_tag_t newtag;
135        int error = 0;
136
137        /* Return a NULL tag on failure */
138        *dmat = NULL;
139
140        newtag = malloc(sizeof(*newtag), M_DEVBUF, M_NOWAIT | M_ZERO);
141        if (newtag == NULL)
142                return (ENOMEM);
143
144        newtag->parent = parent;
145        newtag->alignment = alignment;
146        newtag->boundary = boundary;
147        newtag->lowaddr = lowaddr;
148        newtag->highaddr = highaddr;
149        newtag->filter = filter;
150        newtag->filterarg = filterarg;
151        newtag->maxsize = maxsize;
152        newtag->nsegments = nsegments;
153        newtag->maxsegsz = maxsegsz;
154        newtag->flags = flags;
155        newtag->ref_count = 1; /* Count ourself */
156        newtag->map_count = 0;
157        if (lockfunc != NULL) {
158                newtag->lockfunc = lockfunc;
159                newtag->lockfuncarg = lockfuncarg;
160        } else {
161                newtag->lockfunc = dflt_lock;
162                newtag->lockfuncarg = NULL;
163        }
164
165        /*
166         * Take into account any restrictions imposed by our parent tag
167         */
168        if (parent != NULL) {
169                newtag->lowaddr = min(parent->lowaddr, newtag->lowaddr);
170                newtag->highaddr = max(parent->highaddr, newtag->highaddr);
171                if (newtag->boundary == 0)
172                        newtag->boundary = parent->boundary;
173                else if (parent->boundary != 0)
174                        newtag->boundary = MIN(parent->boundary,
175                                               newtag->boundary);
176                if (newtag->filter == NULL) {
177                        /*
178                         * Short circuit looking at our parent directly
179                         * since we have encapsulated all of its information
180                         */
181                        newtag->filter = parent->filter;
182                        newtag->filterarg = parent->filterarg;
183                        newtag->parent = parent->parent;
184                }
185                if (newtag->parent != NULL)
186                        atomic_add_int(&parent->ref_count, 1);
187        }
188
189        *dmat = newtag;
190        return (error);
191}
192
193int
194bus_dma_tag_destroy(bus_dma_tag_t dmat)
195{
196        if (dmat != NULL) {
197
198                if (dmat->map_count != 0)
199                        return (EBUSY);
200
201                while (dmat != NULL) {
202                        bus_dma_tag_t parent;
203
204                        parent = dmat->parent;
205                        atomic_subtract_int(&dmat->ref_count, 1);
206                        if (dmat->ref_count == 0) {
207                                free(dmat, M_DEVBUF);
208                                /*
209                                 * Last reference count, so
210                                 * release our reference
211                                 * count on our parent.
212                                 */
213                                dmat = parent;
214                        } else
215                                dmat = NULL;
216                }
217        }
218        return (0);
219}
220
221/*
222 * Allocate a handle for mapping from kva/uva/physical
223 * address space into bus device space.
224 */
225int
226bus_dmamap_create(bus_dma_tag_t dmat, int flags, bus_dmamap_t *mapp)
227{
228        *mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO);
229        if (*mapp == NULL) {
230                return ENOMEM;
231        }
232
233        dmat->map_count++;
234
235        return (0);
236}
237
238/*
239 * Destroy a handle for mapping from kva/uva/physical
240 * address space into bus device space.
241 */
242int
243bus_dmamap_destroy(bus_dma_tag_t dmat, bus_dmamap_t map)
244{
245        free(map, M_DEVBUF);
246
247        dmat->map_count--;
248
249        return (0);
250}
251
252/*
253 * Allocate a piece of memory that can be efficiently mapped into
254 * bus device space based on the constraints lited in the dma tag.
255 * A dmamap to for use with dmamap_load is also allocated.
256 */
257int
258bus_dmamem_alloc(bus_dma_tag_t dmat, void** vaddr, int flags,
259    bus_dmamap_t *mapp)
260{
261        *mapp = malloc(sizeof(**mapp), M_DEVBUF, M_NOWAIT | M_ZERO);
262        if (*mapp == NULL) {
263                return ENOMEM;
264        }
265
266        *vaddr = rtems_heap_allocate_aligned_with_boundary(dmat->maxsize, dmat->alignment, dmat->boundary);
267        if (*vaddr == NULL) {
268                free(*mapp, M_DEVBUF);
269
270                return ENOMEM;
271        }
272
273        (*mapp)->buffer_begin = *vaddr;
274        (*mapp)->buffer_size = dmat->maxsize;
275
276        if ((flags & BUS_DMA_ZERO) != 0) {
277                memset(*vaddr, 0, dmat->maxsize);
278        }
279
280        return (0);
281}
282
283/*
284 * Free a piece of memory and it's allocated dmamap, that was allocated
285 * via bus_dmamem_alloc.  Make the same choice for free/contigfree.
286 */
287void
288bus_dmamem_free(bus_dma_tag_t dmat, void *vaddr, bus_dmamap_t map)
289{
290        free(vaddr, M_RTEMS_HEAP);
291        free(map, M_DEVBUF);
292}
293
294/*
295 * Utility function to load a linear buffer.  lastaddrp holds state
296 * between invocations (for multiple-buffer loads).  segp contains
297 * the starting segment on entrance, and the ending segment on exit.
298 * first indicates if this is the first invocation of this function.
299 */
300static int
301bus_dmamap_load_buffer(bus_dma_tag_t dmat, bus_dma_segment_t segs[],
302    void *buf, bus_size_t buflen, struct thread *td, int flags,
303    vm_offset_t *lastaddrp, int *segp, int first)
304{
305        bus_size_t sgsize;
306        bus_addr_t curaddr, lastaddr, baddr, bmask;
307        vm_offset_t vaddr = (vm_offset_t)buf;
308        int seg;
309
310        lastaddr = *lastaddrp;
311        bmask = ~(dmat->boundary - 1);
312
313        for (seg = *segp; buflen > 0 ; ) {
314                /*
315                 * Get the physical address for this segment.
316                 */
317                curaddr = vaddr;
318
319                /*
320                 * Compute the segment size, and adjust counts.
321                 */
322                sgsize = PAGE_SIZE - ((u_long)curaddr & PAGE_MASK);
323                if (sgsize > dmat->maxsegsz)
324                        sgsize = dmat->maxsegsz;
325                if (buflen < sgsize)
326                        sgsize = buflen;
327
328                /*
329                 * Make sure we don't cross any boundaries.
330                 */
331                if (dmat->boundary > 0) {
332                        baddr = (curaddr + dmat->boundary) & bmask;
333                        if (sgsize > (baddr - curaddr))
334                                sgsize = (baddr - curaddr);
335                }
336
337                /*
338                 * Insert chunk into a segment, coalescing with
339                 * the previous segment if possible.
340                 */
341                if (first) {
342                        segs[seg].ds_addr = curaddr;
343                        segs[seg].ds_len = sgsize;
344                        first = 0;
345                } else {
346                        if (curaddr == lastaddr &&
347                            (segs[seg].ds_len + sgsize) <= dmat->maxsegsz &&
348                            (dmat->boundary == 0 ||
349                             (segs[seg].ds_addr & bmask) == (curaddr & bmask)))
350                                segs[seg].ds_len += sgsize;
351                        else {
352                                if (++seg >= dmat->nsegments)
353                                        break;
354                                segs[seg].ds_addr = curaddr;
355                                segs[seg].ds_len = sgsize;
356                        }
357                }
358
359                lastaddr = curaddr + sgsize;
360                vaddr += sgsize;
361                buflen -= sgsize;
362        }
363
364        *segp = seg;
365        *lastaddrp = lastaddr;
366
367        /*
368         * Did we fit?
369         */
370        return (buflen != 0 ? EFBIG : 0); /* XXX better return value here? */
371}
372
373/*
374 * Map the buffer buf into bus space using the dmamap map.
375 */
376int
377bus_dmamap_load(bus_dma_tag_t dmat, bus_dmamap_t map, void *buf,
378    bus_size_t buflen, bus_dmamap_callback_t *callback,
379    void *callback_arg, int flags)
380{
381        bus_dma_segment_t       dm_segments[dmat->nsegments];
382        vm_offset_t             lastaddr;
383        int                     error, nsegs;
384
385        map->buffer_begin = buf;
386        map->buffer_size = buflen;
387
388        lastaddr = (vm_offset_t)0;
389        nsegs = 0;
390        error = bus_dmamap_load_buffer(dmat, dm_segments, buf, buflen,
391            NULL, flags, &lastaddr, &nsegs, 1);
392
393        if (error == 0)
394                (*callback)(callback_arg, dm_segments, nsegs + 1, 0);
395        else
396                (*callback)(callback_arg, NULL, 0, error);
397
398        return (0);
399}
400
401/*
402 * Release the mapping held by map. A no-op on PowerPC.
403 */
404void
405_bus_dmamap_unload(bus_dma_tag_t dmat, bus_dmamap_t map)
406{
407
408        return;
409}
410
411void
412_bus_dmamap_sync(bus_dma_tag_t dmat, bus_dmamap_t map, bus_dmasync_op_t op)
413{
414#ifdef CPU_DATA_CACHE_ALIGNMENT
415        uintptr_t size = map->buffer_size;
416        uintptr_t begin = (uintptr_t) map->buffer_begin;
417        uintptr_t end = begin + size;
418
419        if ((op & BUS_DMASYNC_PREWRITE) != 0 && (op & BUS_DMASYNC_PREREAD) == 0) {
420                rtems_cache_flush_multiple_data_lines((void *) begin, size);
421        }
422        if ((op & BUS_DMASYNC_PREREAD) != 0) {
423                if ((op & BUS_DMASYNC_PREWRITE) != 0 || ((begin | size) & CLMASK) != 0) {
424                        rtems_cache_flush_multiple_data_lines((void *) begin, size);
425                }
426                rtems_cache_invalidate_multiple_data_lines((void *) begin, size);
427        }
428        if ((op & BUS_DMASYNC_POSTREAD) != 0) {
429                char first_buf [CLSZ];
430                char last_buf [CLSZ];
431                bool first_is_aligned = (begin & CLMASK) == 0;
432                bool last_is_aligned = (end & CLMASK) == 0;
433                void *first_begin = (void *) (begin & ~CLMASK);
434                size_t first_size = begin & CLMASK;
435                void *last_begin = (void *) end;
436                size_t last_size = CLSZ - (end & CLMASK);
437
438                if (!first_is_aligned) {
439                        memcpy(first_buf, first_begin, first_size);
440                }
441                if (!last_is_aligned) {
442                        memcpy(last_buf, last_begin, last_size);
443                }
444
445                rtems_cache_invalidate_multiple_data_lines((void *) begin, size);
446
447                if (!first_is_aligned) {
448                        memcpy(first_begin, first_buf, first_size);
449                }
450                if (!last_is_aligned) {
451                        memcpy(last_begin, last_buf, last_size);
452                }
453        }
454#endif /* CPU_DATA_CACHE_ALIGNMENT */
455}
Note: See TracBrowser for help on using the repository browser.