1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * Generic bounce buffer implementation
4 */
5
6 #include <arch/cache.h>
7 #include "bouncebuf.h"
8 #include "storage.h"
9 #include <string.h>
10 #include <commonlib/bsd/stdlib.h>
11
addr_aligned(struct bounce_buffer * state)12 static int addr_aligned(struct bounce_buffer *state)
13 {
14 const uint32_t align_mask = ARCH_DMA_MINALIGN - 1;
15
16 // Check if start is aligned
17 if ((uintptr_t)state->user_buffer & align_mask) {
18 sdhc_debug("Unaligned buffer address %p\n", state->user_buffer);
19 return 0;
20 }
21
22 // Check if length is aligned
23 if (state->len != state->len_aligned) {
24 sdhc_debug("Unaligned buffer length %zd\n", state->len);
25 return 0;
26 }
27
28 // Aligned
29 return 1;
30 }
31
bounce_buffer_start(struct bounce_buffer * state,void * data,size_t len,unsigned int flags)32 int bounce_buffer_start(struct bounce_buffer *state, void *data,
33 size_t len, unsigned int flags)
34 {
35 state->user_buffer = data;
36 state->bounce_buffer = data;
37 state->len = len;
38 state->len_aligned = ROUND(len, ARCH_DMA_MINALIGN);
39 state->flags = flags;
40
41 if (!addr_aligned(state)) {
42 state->bounce_buffer = memalign(ARCH_DMA_MINALIGN,
43 state->len_aligned);
44 if (!state->bounce_buffer)
45 return -1;
46
47 if (state->flags & GEN_BB_READ)
48 memcpy(state->bounce_buffer, state->user_buffer,
49 state->len);
50 }
51
52 /*
53 * Flush data to RAM so DMA reads can pick it up,
54 * and any CPU writebacks don't race with DMA writes
55 */
56 dcache_clean_invalidate_by_mva(state->bounce_buffer,
57 state->len_aligned);
58 return 0;
59 }
60
bounce_buffer_stop(struct bounce_buffer * state)61 int bounce_buffer_stop(struct bounce_buffer *state)
62 {
63 if (state->flags & GEN_BB_WRITE) {
64 // Invalidate cache so that CPU can see any newly DMA'd data
65 dcache_invalidate_by_mva(state->bounce_buffer,
66 state->len_aligned);
67 }
68
69 if (state->bounce_buffer == state->user_buffer)
70 return 0;
71
72 if (state->flags & GEN_BB_WRITE)
73 memcpy(state->user_buffer, state->bounce_buffer, state->len);
74
75 free(state->bounce_buffer);
76
77 return 0;
78 }
79