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