blob: 40d1bab067335711c6680e1d0723abf8e888ef98 [file] [log] [blame]
/*
* Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch_helpers.h>
#include <assert.h>
#include <debug.h>
#include <mmio.h>
#include <memctrl.h>
#include <string.h>
#include <tegra_def.h>
#include <xlat_tables.h>
extern void zeromem16(void *mem, unsigned int length);
#define TEGRA_GPU_RESET_REG_OFFSET 0x28c
#define GPU_RESET_BIT (1 << 24)
/* Video Memory base and size (live values) */
static uintptr_t video_mem_base;
static uint64_t video_mem_size;
/*
* Init SMMU.
*/
void tegra_memctrl_setup(void)
{
/*
* Setup the Memory controller to allow only secure accesses to
* the TZDRAM carveout
*/
INFO("Configuring SMMU\n");
/* allow translations for all MC engines */
tegra_mc_write_32(MC_SMMU_TRANSLATION_ENABLE_0_0,
(unsigned int)MC_SMMU_TRANSLATION_ENABLE);
tegra_mc_write_32(MC_SMMU_TRANSLATION_ENABLE_1_0,
(unsigned int)MC_SMMU_TRANSLATION_ENABLE);
tegra_mc_write_32(MC_SMMU_TRANSLATION_ENABLE_2_0,
(unsigned int)MC_SMMU_TRANSLATION_ENABLE);
tegra_mc_write_32(MC_SMMU_TRANSLATION_ENABLE_3_0,
(unsigned int)MC_SMMU_TRANSLATION_ENABLE);
tegra_mc_write_32(MC_SMMU_TRANSLATION_ENABLE_4_0,
(unsigned int)MC_SMMU_TRANSLATION_ENABLE);
tegra_mc_write_32(MC_SMMU_ASID_SECURITY_0, MC_SMMU_ASID_SECURITY);
tegra_mc_write_32(MC_SMMU_TLB_CONFIG_0, MC_SMMU_TLB_CONFIG_0_RESET_VAL);
tegra_mc_write_32(MC_SMMU_PTC_CONFIG_0, MC_SMMU_PTC_CONFIG_0_RESET_VAL);
/* flush PTC and TLB */
tegra_mc_write_32(MC_SMMU_PTC_FLUSH_0, MC_SMMU_PTC_FLUSH_ALL);
(void)tegra_mc_read_32(MC_SMMU_CONFIG_0); /* read to flush writes */
tegra_mc_write_32(MC_SMMU_TLB_FLUSH_0, MC_SMMU_TLB_FLUSH_ALL);
/* enable SMMU */
tegra_mc_write_32(MC_SMMU_CONFIG_0,
MC_SMMU_CONFIG_0_SMMU_ENABLE_ENABLE);
(void)tegra_mc_read_32(MC_SMMU_CONFIG_0); /* read to flush writes */
/* video memory carveout */
tegra_mc_write_32(MC_VIDEO_PROTECT_BASE, video_mem_base);
tegra_mc_write_32(MC_VIDEO_PROTECT_SIZE_MB, video_mem_size);
}
/*
* Secure the BL31 DRAM aperture.
*
* phys_base = physical base of TZDRAM aperture
* size_in_bytes = size of aperture in bytes
*/
void tegra_memctrl_tzdram_setup(uint64_t phys_base, uint32_t size_in_bytes)
{
/*
* Setup the Memory controller to allow only secure accesses to
* the TZDRAM carveout
*/
INFO("Configuring TrustZone DRAM Memory Carveout\n");
tegra_mc_write_32(MC_SECURITY_CFG0_0, phys_base);
tegra_mc_write_32(MC_SECURITY_CFG1_0, size_in_bytes >> 20);
}
static void tegra_clear_videomem(uintptr_t non_overlap_area_start,
unsigned long long non_overlap_area_size)
{
/*
* Perform cache maintenance to ensure that the non-overlapping area is
* zeroed out. The first invalidation of this range ensures that
* possible evictions of dirty cache lines do not interfere with the
* 'zeromem16' operation. Other CPUs could speculatively prefetch the
* main memory contents of this area between the first invalidation and
* the 'zeromem16' operation. The second invalidation ensures that any
* such cache lines are removed as well.
*/
inv_dcache_range(non_overlap_area_start, non_overlap_area_size);
zeromem16((void *)non_overlap_area_start, non_overlap_area_size);
inv_dcache_range(non_overlap_area_start, non_overlap_area_size);
}
/*
* Program the Video Memory carveout region
*
* phys_base = physical base of aperture
* size_in_bytes = size of aperture in bytes
*/
void tegra_memctrl_videomem_setup(uint64_t phys_base, uint32_t size_in_bytes)
{
uintptr_t vmem_end_old = video_mem_base + (video_mem_size << 20);
uintptr_t vmem_end_new = phys_base + size_in_bytes;
uint32_t regval;
unsigned long long non_overlap_area_size;
/*
* The GPU is the user of the Video Memory region. In order to
* transition to the new memory region smoothly, we program the
* new base/size ONLY if the GPU is in reset mode.
*/
regval = mmio_read_32(TEGRA_CAR_RESET_BASE + TEGRA_GPU_RESET_REG_OFFSET);
if ((regval & GPU_RESET_BIT) == 0) {
ERROR("GPU not in reset! Video Memory setup failed\n");
return;
}
/*
* Setup the Memory controller to restrict CPU accesses to the Video
* Memory region
*/
INFO("Configuring Video Memory Carveout\n");
/*
* Configure Memory Controller directly for the first time.
*/
if (video_mem_base == 0)
goto done;
/*
* Clear the old regions now being exposed. The following cases
* can occur -
*
* 1. clear whole old region (no overlap with new region)
* 2. clear old sub-region below new base
* 3. clear old sub-region above new end
*/
INFO("Cleaning previous Video Memory Carveout\n");
disable_mmu_el3();
if (phys_base > vmem_end_old || video_mem_base > vmem_end_new) {
tegra_clear_videomem(video_mem_base, video_mem_size << 20);
} else {
if (video_mem_base < phys_base) {
non_overlap_area_size = phys_base - video_mem_base;
tegra_clear_videomem(video_mem_base, non_overlap_area_size);
}
if (vmem_end_old > vmem_end_new) {
non_overlap_area_size = vmem_end_old - vmem_end_new;
tegra_clear_videomem(vmem_end_new, non_overlap_area_size);
}
}
enable_mmu_el3(0);
done:
tegra_mc_write_32(MC_VIDEO_PROTECT_BASE, phys_base);
tegra_mc_write_32(MC_VIDEO_PROTECT_SIZE_MB, size_in_bytes >> 20);
/* store new values */
video_mem_base = phys_base;
video_mem_size = size_in_bytes >> 20;
}