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 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 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 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 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 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