/* * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include /* DMA channel registers */ #define DMA_CH_CSR U(0x0) #define DMA_CH_CSR_WEIGHT_SHIFT U(10) #define DMA_CH_CSR_XFER_MODE_SHIFT U(21) #define DMA_CH_CSR_DMA_MODE_MEM2MEM U(4) #define DMA_CH_CSR_DMA_MODE_FIXEDPATTERN U(6) #define DMA_CH_CSR_IRQ_MASK_ENABLE (U(1) << 15) #define DMA_CH_CSR_RUN_ONCE (U(1) << 27) #define DMA_CH_CSR_ENABLE (U(1) << 31) #define DMA_CH_STAT U(0x4) #define DMA_CH_STAT_BUSY (U(1) << 31) #define DMA_CH_SRC_PTR U(0xC) #define DMA_CH_DST_PTR U(0x10) #define DMA_CH_HI_ADR_PTR U(0x14) #define DMA_CH_HI_ADR_PTR_SRC_MASK U(0xFF) #define DMA_CH_HI_ADR_PTR_DST_SHIFT U(16) #define DMA_CH_HI_ADR_PTR_DST_MASK U(0xFF) #define DMA_CH_MC_SEQ U(0x18) #define DMA_CH_MC_SEQ_REQ_CNT_SHIFT U(25) #define DMA_CH_MC_SEQ_REQ_CNT_VAL U(0x10) #define DMA_CH_MC_SEQ_BURST_SHIFT U(23) #define DMA_CH_MC_SEQ_BURST_16_WORDS U(0x3) #define DMA_CH_WORD_COUNT U(0x20) #define DMA_CH_FIXED_PATTERN U(0x34) #define DMA_CH_TZ U(0x38) #define DMA_CH_TZ_ACCESS_ENABLE U(0) #define DMA_CH_TZ_ACCESS_DISABLE U(3) #define MAX_TRANSFER_SIZE (1U*1024U*1024U*1024U) /* 1GB */ #define GPCDMA_TIMEOUT_MS U(100) #define GPCDMA_RESET_BIT (U(1) << 1) static bool init_done; static void tegra_gpcdma_write32(uint32_t offset, uint32_t val) { mmio_write_32(TEGRA_GPCDMA_BASE + offset, val); } static uint32_t tegra_gpcdma_read32(uint32_t offset) { return mmio_read_32(TEGRA_GPCDMA_BASE + offset); } static void tegra_gpcdma_init(void) { /* assert reset for DMA engine */ mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_GPCDMA_RST_SET_REG_OFFSET, GPCDMA_RESET_BIT); udelay(2); /* de-assert reset for DMA engine */ mmio_write_32(TEGRA_CAR_RESET_BASE + TEGRA_GPCDMA_RST_CLR_REG_OFFSET, GPCDMA_RESET_BIT); } static void tegra_gpcdma_memcpy_priv(uint64_t dst_addr, uint64_t src_addr, uint32_t num_bytes, uint32_t mode) { uint32_t val, timeout = 0; int32_t ret = 0; /* sanity check byte count */ if ((num_bytes > MAX_TRANSFER_SIZE) || ((num_bytes & 0x3U) != U(0))) { ret = -EINVAL; } /* initialise GPCDMA block */ if (!init_done) { tegra_gpcdma_init(); init_done = true; } /* make sure channel isn't busy */ val = tegra_gpcdma_read32(DMA_CH_STAT); if ((val & DMA_CH_STAT_BUSY) == DMA_CH_STAT_BUSY) { ERROR("DMA channel is busy\n"); ret = -EBUSY; } if (ret == 0) { /* disable any DMA transfers */ tegra_gpcdma_write32(DMA_CH_CSR, 0); /* enable DMA access to TZDRAM */ tegra_gpcdma_write32(DMA_CH_TZ, DMA_CH_TZ_ACCESS_ENABLE); /* configure MC sequencer */ val = (DMA_CH_MC_SEQ_REQ_CNT_VAL << DMA_CH_MC_SEQ_REQ_CNT_SHIFT) | (DMA_CH_MC_SEQ_BURST_16_WORDS << DMA_CH_MC_SEQ_BURST_SHIFT); tegra_gpcdma_write32(DMA_CH_MC_SEQ, val); /* reset fixed pattern */ tegra_gpcdma_write32(DMA_CH_FIXED_PATTERN, 0); /* populate src and dst address registers */ tegra_gpcdma_write32(DMA_CH_SRC_PTR, (uint32_t)src_addr); tegra_gpcdma_write32(DMA_CH_DST_PTR, (uint32_t)dst_addr); val = (uint32_t)((src_addr >> 32) & DMA_CH_HI_ADR_PTR_SRC_MASK); val |= (uint32_t)(((dst_addr >> 32) & DMA_CH_HI_ADR_PTR_DST_MASK) << DMA_CH_HI_ADR_PTR_DST_SHIFT); tegra_gpcdma_write32(DMA_CH_HI_ADR_PTR, val); /* transfer size (in words) */ tegra_gpcdma_write32(DMA_CH_WORD_COUNT, ((num_bytes >> 2) - 1U)); /* populate value for CSR */ val = (mode << DMA_CH_CSR_XFER_MODE_SHIFT) | DMA_CH_CSR_RUN_ONCE | (U(1) << DMA_CH_CSR_WEIGHT_SHIFT) | DMA_CH_CSR_IRQ_MASK_ENABLE; tegra_gpcdma_write32(DMA_CH_CSR, val); /* enable transfer */ val = tegra_gpcdma_read32(DMA_CH_CSR); val |= DMA_CH_CSR_ENABLE; tegra_gpcdma_write32(DMA_CH_CSR, val); /* wait till transfer completes */ do { /* read the status */ val = tegra_gpcdma_read32(DMA_CH_STAT); if ((val & DMA_CH_STAT_BUSY) != DMA_CH_STAT_BUSY) { break; } mdelay(1); timeout++; } while (timeout < GPCDMA_TIMEOUT_MS); /* flag timeout error */ if (timeout == GPCDMA_TIMEOUT_MS) { ERROR("DMA transfer timed out\n"); } dsbsy(); /* disable DMA access to TZDRAM */ tegra_gpcdma_write32(DMA_CH_TZ, DMA_CH_TZ_ACCESS_DISABLE); isb(); } } /******************************************************************************* * Memcpy using GPCDMA block (Mem2Mem copy) ******************************************************************************/ void tegra_gpcdma_memcpy(uint64_t dst_addr, uint64_t src_addr, uint32_t num_bytes) { tegra_gpcdma_memcpy_priv(dst_addr, src_addr, num_bytes, DMA_CH_CSR_DMA_MODE_MEM2MEM); } /******************************************************************************* * Memset using GPCDMA block (Fixed pattern write) ******************************************************************************/ void tegra_gpcdma_zeromem(uint64_t dst_addr, uint32_t num_bytes) { tegra_gpcdma_memcpy_priv(dst_addr, 0, num_bytes, DMA_CH_CSR_DMA_MODE_FIXEDPATTERN); }