blob: c8125aaa4254a5ceded8c0acdbb3fad5640bdd21 [file] [log] [blame]
Lee Leahyeef40eb2017-03-23 10:54:57 -07001/*
2 * Generic bounce buffer implementation
3 *
4 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
5 * Copyright 2013 Google Inc. All rights reserved.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#include <arch/cache.h>
19#include <console/console.h>
20#include "bouncebuf.h"
21#include <halt.h>
22#include "storage.h"
23#include <string.h>
24#include <commonlib/stdlib.h>
25
26static int addr_aligned(struct bounce_buffer *state)
27{
28 const uint32_t align_mask = ARCH_DMA_MINALIGN - 1;
29
30 // Check if start is aligned
31 if ((uintptr_t)state->user_buffer & align_mask) {
32 sdhc_debug("Unaligned buffer address %p\n", state->user_buffer);
33 return 0;
34 }
35
36 // Check if length is aligned
37 if (state->len != state->len_aligned) {
38 sdhc_debug("Unaligned buffer length %zd\n", state->len);
39 return 0;
40 }
41
42 // Aligned
43 return 1;
44}
45
46int bounce_buffer_start(struct bounce_buffer *state, void *data,
47 size_t len, unsigned int flags)
48{
49 state->user_buffer = data;
50 state->bounce_buffer = data;
51 state->len = len;
52 state->len_aligned = ROUND(len, ARCH_DMA_MINALIGN);
53 state->flags = flags;
54
55 if (!addr_aligned(state)) {
56 state->bounce_buffer = memalign(ARCH_DMA_MINALIGN,
57 state->len_aligned);
58 if (!state->bounce_buffer)
59 return -1;
60
61 if (state->flags & GEN_BB_READ)
62 memcpy(state->bounce_buffer, state->user_buffer,
63 state->len);
64 }
65
66 /*
67 * Flush data to RAM so DMA reads can pick it up,
68 * and any CPU writebacks don't race with DMA writes
69 */
70 dcache_clean_invalidate_by_mva(state->bounce_buffer,
71 state->len_aligned);
72 return 0;
73}
74
75int bounce_buffer_stop(struct bounce_buffer *state)
76{
77 if (state->flags & GEN_BB_WRITE) {
78 // Invalidate cache so that CPU can see any newly DMA'd data
79 dcache_invalidate_by_mva(state->bounce_buffer,
80 state->len_aligned);
81 }
82
83 if (state->bounce_buffer == state->user_buffer)
84 return 0;
85
86 if (state->flags & GEN_BB_WRITE)
87 memcpy(state->user_buffer, state->bounce_buffer, state->len);
88
89 free(state->bounce_buffer);
90
91 return 0;
92}