arm-trusted-firmware/drivers/marvell/ccu.c

362 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (C) 2018 Marvell International Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
* https://spdx.org/licenses
*/
/* CCU unit device driver for Marvell AP807, AP807 and AP810 SoCs */
#include <a8k_common.h>
#include <ccu.h>
#include <debug.h>
#include <mmio.h>
#include <mvebu.h>
#include <mvebu_def.h>
#if LOG_LEVEL >= LOG_LEVEL_INFO
#define DEBUG_ADDR_MAP
#endif
/* common defines */
#define WIN_ENABLE_BIT (0x1)
/* Physical address of the base of the window = {AddrLow[19:0],20h0} */
#define ADDRESS_SHIFT (20 - 4)
#define ADDRESS_MASK (0xFFFFFFF0)
#define CCU_WIN_ALIGNMENT (0x100000)
#define IS_DRAM_TARGET(tgt) ((((tgt) == DRAM_0_TID) || \
((tgt) == DRAM_1_TID) || \
((tgt) == RAR_TID)) ? 1 : 0)
/* For storage of CR, SCR, ALR, AHR abd GCR */
static uint32_t ccu_regs_save[MVEBU_CCU_MAX_WINS * 4 + 1];
#ifdef DEBUG_ADDR_MAP
static void dump_ccu(int ap_index)
{
uint32_t win_id, win_cr, alr, ahr;
uint8_t target_id;
uint64_t start, end;
/* Dump all AP windows */
tf_printf("\tbank target start end\n");
tf_printf("\t----------------------------------------------------\n");
for (win_id = 0; win_id < MVEBU_CCU_MAX_WINS; win_id++) {
win_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
if (win_cr & WIN_ENABLE_BIT) {
target_id = (win_cr >> CCU_TARGET_ID_OFFSET) &
CCU_TARGET_ID_MASK;
alr = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index,
win_id));
ahr = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_index,
win_id));
start = ((uint64_t)alr << ADDRESS_SHIFT);
end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT);
tf_printf("\tccu %02x 0x%016llx 0x%016llx\n",
target_id, start, end);
}
}
win_cr = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_index));
target_id = (win_cr >> CCU_GCR_TARGET_OFFSET) & CCU_GCR_TARGET_MASK;
tf_printf("\tccu GCR %d - all other transactions\n", target_id);
}
#endif
void ccu_win_check(struct addr_map_win *win)
{
/* check if address is aligned to 1M */
if (IS_NOT_ALIGN(win->base_addr, CCU_WIN_ALIGNMENT)) {
win->base_addr = ALIGN_UP(win->base_addr, CCU_WIN_ALIGNMENT);
NOTICE("%s: Align up the base address to 0x%llx\n",
__func__, win->base_addr);
}
/* size parameter validity check */
if (IS_NOT_ALIGN(win->win_size, CCU_WIN_ALIGNMENT)) {
win->win_size = ALIGN_UP(win->win_size, CCU_WIN_ALIGNMENT);
NOTICE("%s: Aligning size to 0x%llx\n",
__func__, win->win_size);
}
}
void ccu_enable_win(int ap_index, struct addr_map_win *win, uint32_t win_id)
{
uint32_t ccu_win_reg;
uint32_t alr, ahr;
uint64_t end_addr;
if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) {
ERROR("Enabling wrong CCU window %d!\n", win_id);
return;
}
end_addr = (win->base_addr + win->win_size - 1);
alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);
ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);
mmio_write_32(CCU_WIN_ALR_OFFSET(ap_index, win_id), alr);
mmio_write_32(CCU_WIN_AHR_OFFSET(ap_index, win_id), ahr);
ccu_win_reg = WIN_ENABLE_BIT;
ccu_win_reg |= (win->target_id & CCU_TARGET_ID_MASK)
<< CCU_TARGET_ID_OFFSET;
mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), ccu_win_reg);
}
static void ccu_disable_win(int ap_index, uint32_t win_id)
{
uint32_t win_reg;
if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) {
ERROR("Disabling wrong CCU window %d!\n", win_id);
return;
}
win_reg = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
win_reg &= ~WIN_ENABLE_BIT;
mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), win_reg);
}
/* Insert/Remove temporary window for using the out-of reset default
* CPx base address to access the CP configuration space prior to
* the further base address update in accordance with address mapping
* design.
*
* NOTE: Use the same window array for insertion and removal of
* temporary windows.
*/
void ccu_temp_win_insert(int ap_index, struct addr_map_win *win, int size)
{
uint32_t win_id;
for (int i = 0; i < size; i++) {
win_id = MVEBU_CCU_MAX_WINS - 1 - i;
ccu_win_check(win);
ccu_enable_win(ap_index, win, win_id);
win++;
}
}
/*
* NOTE: Use the same window array for insertion and removal of
* temporary windows.
*/
void ccu_temp_win_remove(int ap_index, struct addr_map_win *win, int size)
{
uint32_t win_id;
for (int i = 0; i < size; i++) {
uint64_t base;
uint32_t target;
win_id = MVEBU_CCU_MAX_WINS - 1 - i;
target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
target >>= CCU_TARGET_ID_OFFSET;
target &= CCU_TARGET_ID_MASK;
base = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index, win_id));
base <<= ADDRESS_SHIFT;
if ((win->target_id != target) || (win->base_addr != base)) {
ERROR("%s: Trying to remove bad window-%d!\n",
__func__, win_id);
continue;
}
ccu_disable_win(ap_index, win_id);
win++;
}
}
/* Returns current DRAM window target (DRAM_0_TID, DRAM_1_TID, RAR_TID)
* NOTE: Call only once for each AP.
* The AP0 DRAM window is located at index 2 only at the BL31 execution start.
* Then it relocated to index 1 for matching the rest of APs DRAM settings.
* Calling this function after relocation will produce wrong results on AP0
*/
static uint32_t ccu_dram_target_get(int ap_index)
{
/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
* All the rest of detected APs will use window at index 1.
* The AP0 DRAM window is moved from index 2 to 1 during
* init_ccu() execution.
*/
const uint32_t win_id = (ap_index == 0) ? 2 : 1;
uint32_t target;
target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
target >>= CCU_TARGET_ID_OFFSET;
target &= CCU_TARGET_ID_MASK;
return target;
}
void ccu_dram_target_set(int ap_index, uint32_t target)
{
/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
* All the rest of detected APs will use window at index 1.
* The AP0 DRAM window is moved from index 2 to 1
* during init_ccu() execution.
*/
const uint32_t win_id = (ap_index == 0) ? 2 : 1;
uint32_t dram_cr;
dram_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
dram_cr &= ~(CCU_TARGET_ID_MASK << CCU_TARGET_ID_OFFSET);
dram_cr |= (target & CCU_TARGET_ID_MASK) << CCU_TARGET_ID_OFFSET;
mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), dram_cr);
}
/* Setup CCU DRAM window and enable it */
void ccu_dram_win_config(int ap_index, struct addr_map_win *win)
{
#if IMAGE_BLE /* BLE */
/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
* Since the BootROM is not accessing DRAM at BLE stage,
* the DRAM window can be temporarely disabled.
*/
const uint32_t win_id = (ap_index == 0) ? 2 : 1;
#else /* end of BLE */
/* At the ccu_init() execution stage, DRAM windows of all APs
* are arranged at index 1.
* The AP0 still has the old window BootROM DRAM at index 2, so
* the window-1 can be safely disabled without breaking the DRAM access.
*/
const uint32_t win_id = 1;
#endif
ccu_disable_win(ap_index, win_id);
/* enable write secure (and clear read secure) */
mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id),
CCU_WIN_ENA_WRITE_SECURE);
ccu_win_check(win);
ccu_enable_win(ap_index, win, win_id);
}
/* Save content of CCU window + GCR */
static void ccu_save_win_range(int ap_id, int win_first,
int win_last, uint32_t *buffer)
{
int win_id, idx;
/* Save CCU */
for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
buffer[idx++] = mmio_read_32(CCU_WIN_CR_OFFSET(ap_id, win_id));
buffer[idx++] = mmio_read_32(CCU_WIN_SCR_OFFSET(ap_id, win_id));
buffer[idx++] = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_id, win_id));
buffer[idx++] = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_id, win_id));
}
buffer[idx] = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_id));
}
/* Restore content of CCU window + GCR */
static void ccu_restore_win_range(int ap_id, int win_first,
int win_last, uint32_t *buffer)
{
int win_id, idx;
/* Restore CCU */
for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
mmio_write_32(CCU_WIN_CR_OFFSET(ap_id, win_id), buffer[idx++]);
mmio_write_32(CCU_WIN_SCR_OFFSET(ap_id, win_id), buffer[idx++]);
mmio_write_32(CCU_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]);
mmio_write_32(CCU_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]);
}
mmio_write_32(CCU_WIN_GCR_OFFSET(ap_id), buffer[idx]);
}
void ccu_save_win_all(int ap_id)
{
ccu_save_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save);
}
void ccu_restore_win_all(int ap_id)
{
ccu_restore_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save);
}
int init_ccu(int ap_index)
{
struct addr_map_win *win, *dram_win;
uint32_t win_id, win_reg;
uint32_t win_count, array_id;
uint32_t dram_target;
#if IMAGE_BLE
/* In BootROM context CCU Window-1
* has SRAM_TID target and should not be disabled
*/
const uint32_t win_start = 2;
#else
const uint32_t win_start = 1;
#endif
INFO("Initializing CCU Address decoding\n");
/* Get the array of the windows and fill the map data */
marvell_get_ccu_memory_map(ap_index, &win, &win_count);
if (win_count <= 0) {
INFO("No windows configurations found\n");
} else if (win_count > (MVEBU_CCU_MAX_WINS - 1)) {
ERROR("CCU mem map array > than max available windows (%d)\n",
MVEBU_CCU_MAX_WINS);
win_count = MVEBU_CCU_MAX_WINS;
}
/* Need to set GCR to DRAM before all CCU windows are disabled for
* securing the normal access to DRAM location, which the ATF is running
* from. Once all CCU windows are set, which have to include the
* dedicated DRAM window as well, the GCR can be switched to the target
* defined by the platform configuration.
*/
dram_target = ccu_dram_target_get(ap_index);
win_reg = (dram_target & CCU_GCR_TARGET_MASK) << CCU_GCR_TARGET_OFFSET;
mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg);
/* If the DRAM window was already configured at the BLE stage,
* only the window target considered valid, the address range should be
* updated according to the platform configuration.
*/
for (dram_win = win, array_id = 0; array_id < win_count;
array_id++, dram_win++) {
if (IS_DRAM_TARGET(dram_win->target_id)) {
dram_win->target_id = dram_target;
break;
}
}
/* Disable all AP CCU windows
* Window-0 is always bypassed since it already contains
* data allowing the internal configuration space access
*/
for (win_id = win_start; win_id < MVEBU_CCU_MAX_WINS; win_id++) {
ccu_disable_win(ap_index, win_id);
/* enable write secure (and clear read secure) */
mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id),
CCU_WIN_ENA_WRITE_SECURE);
}
/* win_id is the index of the current ccu window
* array_id is the index of the current memory map window entry
*/
for (win_id = win_start, array_id = 0;
((win_id < MVEBU_CCU_MAX_WINS) && (array_id < win_count));
win_id++) {
ccu_win_check(win);
ccu_enable_win(ap_index, win, win_id);
win++;
array_id++;
}
/* Get & set the default target according to board topology */
win_reg = (marvell_get_ccu_gcr_target(ap_index) & CCU_GCR_TARGET_MASK)
<< CCU_GCR_TARGET_OFFSET;
mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg);
#ifdef DEBUG_ADDR_MAP
dump_ccu(ap_index);
#endif
INFO("Done CCU Address decoding Initializing\n");
return 0;
}