blob: 93a35668cc29dad6953c447f39d0a67d54f1243f [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Vasutae723e12012-08-26 15:19:06 +00002/*
3 * Generic bounce buffer implementation
4 *
5 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
Marek Vasutae723e12012-08-26 15:19:06 +00006 */
7
8#include <common.h>
Simon Glass63334482019-11-14 12:57:39 -07009#include <cpu_func.h>
Simon Glass0f2af882020-05-10 11:40:05 -060010#include <log.h>
Marek Vasutae723e12012-08-26 15:19:06 +000011#include <malloc.h>
12#include <errno.h>
13#include <bouncebuf.h>
Simon Glass274e0b02020-05-10 11:39:56 -060014#include <asm/cache.h>
Andrew Davis4a207b82023-01-06 12:02:50 -060015#include <linux/dma-mapping.h>
Marek Vasutae723e12012-08-26 15:19:06 +000016
Stephen Warren4a8629e2012-11-06 11:27:29 +000017static int addr_aligned(struct bounce_buffer *state)
Marek Vasutae723e12012-08-26 15:19:06 +000018{
19 const ulong align_mask = ARCH_DMA_MINALIGN - 1;
20
21 /* Check if start is aligned */
Stephen Warren4a8629e2012-11-06 11:27:29 +000022 if ((ulong)state->user_buffer & align_mask) {
23 debug("Unaligned buffer address %p\n", state->user_buffer);
Marek Vasutae723e12012-08-26 15:19:06 +000024 return 0;
25 }
26
Stephen Warren4a8629e2012-11-06 11:27:29 +000027 /* Check if length is aligned */
28 if (state->len != state->len_aligned) {
Vasili Galka38c3fff2014-08-26 13:45:48 +030029 debug("Unaligned buffer length %zu\n", state->len);
Marek Vasutae723e12012-08-26 15:19:06 +000030 return 0;
31 }
32
33 /* Aligned */
34 return 1;
35}
36
Marek Vasut393de782020-04-04 12:45:02 +020037int bounce_buffer_start_extalign(struct bounce_buffer *state, void *data,
38 size_t len, unsigned int flags,
39 size_t alignment,
40 int (*addr_is_aligned)(struct bounce_buffer *state))
Marek Vasutae723e12012-08-26 15:19:06 +000041{
Stephen Warren4a8629e2012-11-06 11:27:29 +000042 state->user_buffer = data;
43 state->bounce_buffer = data;
44 state->len = len;
Marek Vasut393de782020-04-04 12:45:02 +020045 state->len_aligned = roundup(len, alignment);
Stephen Warren4a8629e2012-11-06 11:27:29 +000046 state->flags = flags;
Marek Vasutae723e12012-08-26 15:19:06 +000047
Marek Vasut393de782020-04-04 12:45:02 +020048 if (!addr_is_aligned(state)) {
49 state->bounce_buffer = memalign(alignment,
Stephen Warren4a8629e2012-11-06 11:27:29 +000050 state->len_aligned);
51 if (!state->bounce_buffer)
52 return -ENOMEM;
Marek Vasutae723e12012-08-26 15:19:06 +000053
Stephen Warren4a8629e2012-11-06 11:27:29 +000054 if (state->flags & GEN_BB_READ)
55 memcpy(state->bounce_buffer, state->user_buffer,
56 state->len);
57 }
Marek Vasutae723e12012-08-26 15:19:06 +000058
Stephen Warren4a8629e2012-11-06 11:27:29 +000059 /*
60 * Flush data to RAM so DMA reads can pick it up,
61 * and any CPU writebacks don't race with DMA writes
62 */
Andrew Davis4a207b82023-01-06 12:02:50 -060063 dma_map_single(state->bounce_buffer,
64 state->len_aligned,
65 DMA_BIDIRECTIONAL);
Marek Vasutae723e12012-08-26 15:19:06 +000066
67 return 0;
68}
69
Marek Vasut393de782020-04-04 12:45:02 +020070int bounce_buffer_start(struct bounce_buffer *state, void *data,
71 size_t len, unsigned int flags)
72{
73 return bounce_buffer_start_extalign(state, data, len, flags,
74 ARCH_DMA_MINALIGN,
75 addr_aligned);
76}
77
Stephen Warren4a8629e2012-11-06 11:27:29 +000078int bounce_buffer_stop(struct bounce_buffer *state)
Marek Vasutae723e12012-08-26 15:19:06 +000079{
Stephen Warren4a8629e2012-11-06 11:27:29 +000080 if (state->flags & GEN_BB_WRITE) {
81 /* Invalidate cache so that CPU can see any newly DMA'd data */
Andrew Davis4a207b82023-01-06 12:02:50 -060082 dma_unmap_single((dma_addr_t)state->bounce_buffer,
83 state->len_aligned,
84 DMA_BIDIRECTIONAL);
Stephen Warren4a8629e2012-11-06 11:27:29 +000085 }
Marek Vasutae723e12012-08-26 15:19:06 +000086
Stephen Warren4a8629e2012-11-06 11:27:29 +000087 if (state->bounce_buffer == state->user_buffer)
Marek Vasutae723e12012-08-26 15:19:06 +000088 return 0;
89
Stephen Warren4a8629e2012-11-06 11:27:29 +000090 if (state->flags & GEN_BB_WRITE)
91 memcpy(state->user_buffer, state->bounce_buffer, state->len);
Marek Vasutae723e12012-08-26 15:19:06 +000092
Stephen Warren4a8629e2012-11-06 11:27:29 +000093 free(state->bounce_buffer);
Marek Vasutae723e12012-08-26 15:19:06 +000094
95 return 0;
96}