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