blob: 934b83f7ec3fd2147f2a49386b09e0e642a74aa8 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Marek Vasutb660df32012-08-26 15:19:06 +00002/*
3 * Generic bounce buffer implementation
4 *
5 * Copyright (C) 2012 Marek Vasut <marex@denx.de>
Marek Vasutb660df32012-08-26 15:19:06 +00006 */
7
Tom Rinid678a592024-05-18 20:20:43 -06008#include <common.h>
Simon Glass1eb69ae2019-11-14 12:57:39 -07009#include <cpu_func.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060010#include <log.h>
Marek Vasutb660df32012-08-26 15:19:06 +000011#include <malloc.h>
12#include <errno.h>
13#include <bouncebuf.h>
Simon Glass90526e92020-05-10 11:39:56 -060014#include <asm/cache.h>
Andrew Davisb75ca262023-01-06 12:02:50 -060015#include <linux/dma-mapping.h>
Marek Vasutb660df32012-08-26 15:19:06 +000016
Stephen Warren84d35b22012-11-06 11:27:29 +000017static int addr_aligned(struct bounce_buffer *state)
Marek Vasutb660df32012-08-26 15:19:06 +000018{
19 const ulong align_mask = ARCH_DMA_MINALIGN - 1;
20
21 /* Check if start is aligned */
Stephen Warren84d35b22012-11-06 11:27:29 +000022 if ((ulong)state->user_buffer & align_mask) {
23 debug("Unaligned buffer address %p\n", state->user_buffer);
Marek Vasutb660df32012-08-26 15:19:06 +000024 return 0;
25 }
26
Stephen Warren84d35b22012-11-06 11:27:29 +000027 /* Check if length is aligned */
28 if (state->len != state->len_aligned) {
Vasili Galka5d69a5d2014-08-26 13:45:48 +030029 debug("Unaligned buffer length %zu\n", state->len);
Marek Vasutb660df32012-08-26 15:19:06 +000030 return 0;
31 }
32
33 /* Aligned */
34 return 1;
35}
36
Marek Vasut8074ffe2020-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 Vasutb660df32012-08-26 15:19:06 +000041{
Stephen Warren84d35b22012-11-06 11:27:29 +000042 state->user_buffer = data;
43 state->bounce_buffer = data;
44 state->len = len;
Marek Vasut8074ffe2020-04-04 12:45:02 +020045 state->len_aligned = roundup(len, alignment);
Stephen Warren84d35b22012-11-06 11:27:29 +000046 state->flags = flags;
Marek Vasutb660df32012-08-26 15:19:06 +000047
Marek Vasut8074ffe2020-04-04 12:45:02 +020048 if (!addr_is_aligned(state)) {
49 state->bounce_buffer = memalign(alignment,
Stephen Warren84d35b22012-11-06 11:27:29 +000050 state->len_aligned);
51 if (!state->bounce_buffer)
52 return -ENOMEM;
53
54 if (state->flags & GEN_BB_READ)
55 memcpy(state->bounce_buffer, state->user_buffer,
56 state->len);
Marek Vasutb660df32012-08-26 15:19:06 +000057 }
58
Stephen Warren84d35b22012-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 Davisb75ca262023-01-06 12:02:50 -060063 dma_map_single(state->bounce_buffer,
64 state->len_aligned,
65 DMA_BIDIRECTIONAL);
Marek Vasutb660df32012-08-26 15:19:06 +000066
67 return 0;
68}
69
Marek Vasut8074ffe2020-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 Warren84d35b22012-11-06 11:27:29 +000078int bounce_buffer_stop(struct bounce_buffer *state)
Marek Vasutb660df32012-08-26 15:19:06 +000079{
Stephen Warren84d35b22012-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 */
Marek Vasut3f9cff62023-08-14 01:47:47 +020082 dma_unmap_single((dma_addr_t)(uintptr_t)state->bounce_buffer,
Andrew Davisb75ca262023-01-06 12:02:50 -060083 state->len_aligned,
84 DMA_BIDIRECTIONAL);
Stephen Warren84d35b22012-11-06 11:27:29 +000085 }
Marek Vasutb660df32012-08-26 15:19:06 +000086
Stephen Warren84d35b22012-11-06 11:27:29 +000087 if (state->bounce_buffer == state->user_buffer)
Marek Vasutb660df32012-08-26 15:19:06 +000088 return 0;
89
Stephen Warren84d35b22012-11-06 11:27:29 +000090 if (state->flags & GEN_BB_WRITE)
91 memcpy(state->user_buffer, state->bounce_buffer, state->len);
Marek Vasutb660df32012-08-26 15:19:06 +000092
Stephen Warren84d35b22012-11-06 11:27:29 +000093 free(state->bounce_buffer);
Marek Vasutb660df32012-08-26 15:19:06 +000094
95 return 0;
96}