bouncebuf.c 2.2 KB

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