blob: 8809635b05ef0546d7966774ef319e00ca5c20cb [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright 2021 Foundries.io Ltd.
* Jorge Ramirez-Ortiz <jorge@foundries.io>
*/
#include <config.h>
#include <drivers/zynqmp_csudma.h>
#include <io.h>
#include <kernel/cache_helpers.h>
#include <kernel/delay.h>
#include <mm/core_memprot.h>
#include <util.h>
#define CSUDMA_ADDR_OFFSET 0x00
#define CSUDMA_SIZE_OFFSET 0x04
#define CSUDMA_STS_OFFSET 0x08
#define CSUDMA_CTRL_OFFSET 0x0C
#define CSUDMA_CRC_OFFSET 0x10
#define CSUDMA_I_STS_OFFSET 0x14
#define CSUDMA_I_EN_OFFSET 0x18
#define CSUDMA_I_DIS_OFFSET 0x1C
#define CSUDMA_I_MASK_OFFSET 0x20
#define CSUDMA_CTRL2_OFFSET 0x24
#define CSUDMA_ADDR_MSB_OFFSET 0x28
#define CSUDMA_OFFSET_DIFF 0x0800
#define CSUDMA_ADDR_MASK GENMASK_32(31, 2)
#define CSUDMA_ADDR_LSB_MASK (BIT(0) | BIT(1))
#define CSUDMA_ADDR_MSB_MASK GENMASK_32(16, 0)
#define CSUDMA_ADDR_MSB_SHIFT 32
#define CSUDMA_SIZE_SHIFT 2
#define CSUDMA_STS_BUSY_MASK BIT(0)
#define CSUDMA_CTRL_ENDIAN_MASK BIT(23)
#define CSUDMA_LAST_WORD_MASK BIT(0)
#define CSUDMA_IXR_DONE_MASK BIT(1)
#define CSUDMA_IXR_SRC_MASK GENMASK_32(6, 0)
#define CSUDMA_IXR_DST_MASK GENMASK_32(7, 1)
#define CSUDMA_DONE_TIMEOUT_USEC 3000000
register_phys_mem_pgdir(MEM_AREA_IO_SEC, CSUDMA_BASE, CSUDMA_SIZE);
static void csudma_clear_intr(enum zynqmp_csudma_channel channel, uint32_t mask)
{
vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
CSUDMA_SIZE);
uint32_t val = CSUDMA_IXR_SRC_MASK;
if (channel == ZYNQMP_CSUDMA_DST_CHANNEL) {
dma += CSUDMA_OFFSET_DIFF;
val = CSUDMA_IXR_DST_MASK;
}
io_write32(dma + CSUDMA_I_STS_OFFSET, val & mask);
}
TEE_Result zynqmp_csudma_sync(enum zynqmp_csudma_channel channel)
{
vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
CSUDMA_SIZE);
uint64_t tref = timeout_init_us(CSUDMA_DONE_TIMEOUT_USEC);
uint32_t status = 0;
if (!dma)
return TEE_ERROR_GENERIC;
if (channel == ZYNQMP_CSUDMA_DST_CHANNEL)
dma = dma + CSUDMA_OFFSET_DIFF;
while (!timeout_elapsed(tref)) {
status = io_read32(dma + CSUDMA_I_STS_OFFSET);
if ((status & CSUDMA_IXR_DONE_MASK) == CSUDMA_IXR_DONE_MASK) {
csudma_clear_intr(channel, CSUDMA_IXR_DONE_MASK);
return TEE_SUCCESS;
}
}
return TEE_ERROR_GENERIC;
}
TEE_Result zynqmp_csudma_prepare(void)
{
vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
CSUDMA_SIZE);
if (!dma)
return TEE_ERROR_GENERIC;
io_setbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
dma = dma + CSUDMA_OFFSET_DIFF;
io_setbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
return TEE_SUCCESS;
}
void zynqmp_csudma_unprepare(void)
{
vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
CSUDMA_SIZE);
io_clrbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
dma = dma + CSUDMA_OFFSET_DIFF;
io_clrbits32(dma + CSUDMA_CTRL_OFFSET, CSUDMA_CTRL_ENDIAN_MASK);
}
TEE_Result zynqmp_csudma_transfer(enum zynqmp_csudma_channel channel,
void *addr, size_t len, uint8_t notify)
{
vaddr_t dma = core_mmu_get_va(CSUDMA_BASE, MEM_AREA_IO_SEC,
CSUDMA_SIZE);
paddr_t phys = virt_to_phys(addr);
uint32_t addr_offset = 0;
if (!dma)
return TEE_ERROR_GENERIC;
if (len % sizeof(uint32_t))
return TEE_ERROR_BAD_PARAMETERS;
if (!IS_ALIGNED(phys, ZYNQMP_CSUDMA_ALIGN)) {
EMSG("Invalid alignment");
return TEE_ERROR_BAD_PARAMETERS;
}
/* convert to 32 bit word transfers */
len = len / sizeof(uint32_t);
if (channel == ZYNQMP_CSUDMA_DST_CHANNEL) {
dma = dma + CSUDMA_OFFSET_DIFF;
dcache_inv_range(addr, SHIFT_U64(len, CSUDMA_SIZE_SHIFT));
} else {
dcache_clean_range(addr, SHIFT_U64(len, CSUDMA_SIZE_SHIFT));
}
addr_offset = phys & CSUDMA_ADDR_MASK;
io_write32(dma + CSUDMA_ADDR_OFFSET, addr_offset);
addr_offset = phys >> CSUDMA_ADDR_MSB_SHIFT;
io_write32(dma + CSUDMA_ADDR_MSB_OFFSET, addr_offset);
io_write32(dma + CSUDMA_SIZE_OFFSET,
SHIFT_U32(len, CSUDMA_SIZE_SHIFT) | notify);
return TEE_SUCCESS;
}