1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3 * Copyright 2021 Foundries.io Ltd.
4 * Jorge Ramirez-Ortiz <jorge@foundries.io>
5 */
6
7 #include <config.h>
8 #include <drivers/zynqmp_csudma.h>
9 #include <io.h>
10 #include <kernel/cache_helpers.h>
11 #include <kernel/delay.h>
12 #include <mm/core_memprot.h>
13 #include <util.h>
14
15 #define CSUDMA_ADDR_OFFSET 0x00
16 #define CSUDMA_SIZE_OFFSET 0x04
17 #define CSUDMA_STS_OFFSET 0x08
18 #define CSUDMA_CTRL_OFFSET 0x0C
19 #define CSUDMA_CRC_OFFSET 0x10
20 #define CSUDMA_I_STS_OFFSET 0x14
21 #define CSUDMA_I_EN_OFFSET 0x18
22 #define CSUDMA_I_DIS_OFFSET 0x1C
23 #define CSUDMA_I_MASK_OFFSET 0x20
24 #define CSUDMA_CTRL2_OFFSET 0x24
25 #define CSUDMA_ADDR_MSB_OFFSET 0x28
26
27 #define CSUDMA_OFFSET_DIFF 0x0800
28
29 #define CSUDMA_ADDR_MASK GENMASK_32(31, 2)
30 #define CSUDMA_ADDR_LSB_MASK (BIT(0) | BIT(1))
31 #define CSUDMA_ADDR_MSB_MASK GENMASK_32(16, 0)
32 #define CSUDMA_ADDR_MSB_SHIFT 32
33 #define CSUDMA_SIZE_SHIFT 2
34 #define CSUDMA_STS_BUSY_MASK BIT(0)
35 #define CSUDMA_CTRL_ENDIAN_MASK BIT(23)
36 #define CSUDMA_LAST_WORD_MASK BIT(0)
37 #define CSUDMA_IXR_DONE_MASK BIT(1)
38 #define CSUDMA_IXR_SRC_MASK GENMASK_32(6, 0)
39 #define CSUDMA_IXR_DST_MASK GENMASK_32(7, 1)
40
41 #define CSUDMA_DONE_TIMEOUT_USEC 3000000
42
43 register_phys_mem_pgdir(MEM_AREA_IO_SEC, CSUDMA_BASE, CSUDMA_SIZE);
44
csudma_clear_intr(enum zynqmp_csudma_channel channel,uint32_t mask)45 static void csudma_clear_intr(enum zynqmp_csudma_channel channel, uint32_t mask)
46 {
47 vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
48 CSUDMA_SIZE);
49 uint32_t val = CSUDMA_IXR_SRC_MASK;
50
51 if (channel == ZYNQMP_CSUDMA_DST_CHANNEL) {
52 dma += CSUDMA_OFFSET_DIFF;
53 val = CSUDMA_IXR_DST_MASK;
54 }
55
56 io_write32(dma + CSUDMA_I_STS_OFFSET, val & mask);
57 }
58
zynqmp_csudma_sync(enum zynqmp_csudma_channel channel)59 TEE_Result zynqmp_csudma_sync(enum zynqmp_csudma_channel channel)
60 {
61 vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
62 CSUDMA_SIZE);
63 uint64_t tref = timeout_init_us(CSUDMA_DONE_TIMEOUT_USEC);
64 uint32_t status = 0;
65
66 if (!dma)
67 return TEE_ERROR_GENERIC;
68
69 if (channel == ZYNQMP_CSUDMA_DST_CHANNEL)
70 dma = dma + CSUDMA_OFFSET_DIFF;
71
72 while (!timeout_elapsed(tref)) {
73 status = io_read32(dma + CSUDMA_I_STS_OFFSET);
74 if ((status & CSUDMA_IXR_DONE_MASK) == CSUDMA_IXR_DONE_MASK) {
75 csudma_clear_intr(channel, CSUDMA_IXR_DONE_MASK);
76 return TEE_SUCCESS;
77 }
78 }
79
80 return TEE_ERROR_GENERIC;
81 }
82
zynqmp_csudma_prepare(void)83 TEE_Result zynqmp_csudma_prepare(void)
84 {
85 vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
86 CSUDMA_SIZE);
87
88 if (!dma)
89 return TEE_ERROR_GENERIC;
90
91 io_setbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
92 dma = dma + CSUDMA_OFFSET_DIFF;
93 io_setbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
94
95 return TEE_SUCCESS;
96 }
97
zynqmp_csudma_unprepare(void)98 void zynqmp_csudma_unprepare(void)
99 {
100 vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
101 CSUDMA_SIZE);
102
103 io_clrbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
104 dma = dma + CSUDMA_OFFSET_DIFF;
105 io_clrbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
106 }
107
zynqmp_csudma_transfer(enum zynqmp_csudma_channel channel,void * addr,size_t len,uint8_t notify)108 TEE_Result zynqmp_csudma_transfer(enum zynqmp_csudma_channel channel,
109 void *addr, size_t len, uint8_t notify)
110 {
111 vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
112 CSUDMA_SIZE);
113 paddr_t phys = virt_to_phys(addr);
114 uint32_t addr_offset = 0;
115
116 if (!dma)
117 return TEE_ERROR_GENERIC;
118
119 if (len % sizeof(uint32_t))
120 return TEE_ERROR_BAD_PARAMETERS;
121
122 if (!IS_ALIGNED(phys, ZYNQMP_CSUDMA_ALIGN)) {
123 EMSG("Invalid alignment");
124 return TEE_ERROR_BAD_PARAMETERS;
125 }
126
127 /* convert to 32 bit word transfers */
128 len = len / sizeof(uint32_t);
129
130 if (channel == ZYNQMP_CSUDMA_DST_CHANNEL) {
131 dma = dma + CSUDMA_OFFSET_DIFF;
132 dcache_inv_range(addr, SHIFT_U64(len, CSUDMA_SIZE_SHIFT));
133 } else {
134 dcache_clean_range(addr, SHIFT_U64(len, CSUDMA_SIZE_SHIFT));
135 }
136
137 addr_offset = phys & CSUDMA_ADDR_MASK;
138 io_write32(dma + CSUDMA_ADDR_OFFSET, addr_offset);
139
140 addr_offset = phys >> CSUDMA_ADDR_MSB_SHIFT;
141 io_write32(dma + CSUDMA_ADDR_MSB_OFFSET, addr_offset);
142 io_write32(dma + CSUDMA_SIZE_OFFSET,
143 SHIFT_U32(len, CSUDMA_SIZE_SHIFT) | notify);
144
145 return TEE_SUCCESS;
146 }
147