Home | History | Annotate | Download | only in common
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Generic bounce buffer implementation
      4  *
      5  * Copyright (C) 2012 Marek Vasut <marex (at) denx.de>
      6  */
      7 
      8 #include <common.h>
      9 #include <malloc.h>
     10 #include <errno.h>
     11 #include <bouncebuf.h>
     12 
     13 static int addr_aligned(struct bounce_buffer *state)
     14 {
     15 	const ulong align_mask = ARCH_DMA_MINALIGN - 1;
     16 
     17 	/* Check if start is aligned */
     18 	if ((ulong)state->user_buffer & align_mask) {
     19 		debug("Unaligned buffer address %p\n", state->user_buffer);
     20 		return 0;
     21 	}
     22 
     23 	/* Check if length is aligned */
     24 	if (state->len != state->len_aligned) {
     25 		debug("Unaligned buffer length %zu\n", state->len);
     26 		return 0;
     27 	}
     28 
     29 	/* Aligned */
     30 	return 1;
     31 }
     32 
     33 int bounce_buffer_start(struct bounce_buffer *state, void *data,
     34 			size_t len, unsigned int flags)
     35 {
     36 	state->user_buffer = data;
     37 	state->bounce_buffer = data;
     38 	state->len = len;
     39 	state->len_aligned = roundup(len, ARCH_DMA_MINALIGN);
     40 	state->flags = flags;
     41 
     42 	if (!addr_aligned(state)) {
     43 		state->bounce_buffer = memalign(ARCH_DMA_MINALIGN,
     44 						state->len_aligned);
     45 		if (!state->bounce_buffer)
     46 			return -ENOMEM;
     47 
     48 		if (state->flags & GEN_BB_READ)
     49 			memcpy(state->bounce_buffer, state->user_buffer,
     50 				state->len);
     51 	}
     52 
     53 	/*
     54 	 * Flush data to RAM so DMA reads can pick it up,
     55 	 * and any CPU writebacks don't race with DMA writes
     56 	 */
     57 	flush_dcache_range((unsigned long)state->bounce_buffer,
     58 				(unsigned long)(state->bounce_buffer) +
     59 					state->len_aligned);
     60 
     61 	return 0;
     62 }
     63 
     64 int bounce_buffer_stop(struct bounce_buffer *state)
     65 {
     66 	if (state->flags & GEN_BB_WRITE) {
     67 		/* Invalidate cache so that CPU can see any newly DMA'd data */
     68 		invalidate_dcache_range((unsigned long)state->bounce_buffer,
     69 					(unsigned long)(state->bounce_buffer) +
     70 						state->len_aligned);
     71 	}
     72 
     73 	if (state->bounce_buffer == state->user_buffer)
     74 		return 0;
     75 
     76 	if (state->flags & GEN_BB_WRITE)
     77 		memcpy(state->user_buffer, state->bounce_buffer, state->len);
     78 
     79 	free(state->bounce_buffer);
     80 
     81 	return 0;
     82 }
     83