diff options
Diffstat (limited to '')
-rw-r--r-- | drivers/marvell/io_win.c | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/drivers/marvell/io_win.c b/drivers/marvell/io_win.c new file mode 100644 index 0000000..124382a --- /dev/null +++ b/drivers/marvell/io_win.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2018 Marvell International Ltd. + * + * SPDX-License-Identifier: BSD-3-Clause + * https://spdx.org/licenses + */ + +/* IO Window unit device driver for Marvell AP807, AP807 and AP810 SoCs */ + +#include <inttypes.h> +#include <stdint.h> + +#include <common/debug.h> +#include <drivers/marvell/io_win.h> +#include <lib/mmio.h> + +#include <armada_common.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 = {Addr[19:0],20`h0} */ +#define ADDRESS_SHIFT (20 - 4) +#define ADDRESS_MASK (0xFFFFFFF0) +#define IO_WIN_ALIGNMENT_1M (0x100000) +#define IO_WIN_ALIGNMENT_64K (0x10000) + +/* AP registers */ +#define IO_WIN_ALR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0x0 + \ + (0x10 * win)) +#define IO_WIN_AHR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0x8 + \ + (0x10 * win)) +#define IO_WIN_CR_OFFSET(ap, win) (MVEBU_IO_WIN_BASE(ap) + 0xC + \ + (0x10 * win)) + +/* For storage of CR, ALR, AHR abd GCR */ +static uint32_t io_win_regs_save[MVEBU_IO_WIN_MAX_WINS * 3 + 1]; + +static void io_win_check(struct addr_map_win *win) +{ + /* for IO The base is always 1M aligned */ + /* check if address is aligned to 1M */ + if (IS_NOT_ALIGN(win->base_addr, IO_WIN_ALIGNMENT_1M)) { + win->base_addr = ALIGN_UP(win->base_addr, IO_WIN_ALIGNMENT_1M); + NOTICE("%s: Align up the base address to 0x%" PRIx64 "\n", + __func__, win->base_addr); + } + + /* size parameter validity check */ + if (IS_NOT_ALIGN(win->win_size, IO_WIN_ALIGNMENT_1M)) { + win->win_size = ALIGN_UP(win->win_size, IO_WIN_ALIGNMENT_1M); + NOTICE("%s: Aligning size to 0x%" PRIx64 "\n", + __func__, win->win_size); + } +} + +static void io_win_enable_window(int ap_index, struct addr_map_win *win, + uint32_t win_num) +{ + uint32_t alr, ahr; + uint64_t end_addr; + + if (win->target_id < 0 || win->target_id >= MVEBU_IO_WIN_MAX_WINS) { + ERROR("target ID = %d, is invalid\n", win->target_id); + return; + } + + if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) { + ERROR("Enabling wrong IOW window %d!\n", win_num); + return; + } + + /* calculate the end-address */ + end_addr = (win->base_addr + win->win_size - 1); + + alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + alr |= WIN_ENABLE_BIT; + ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK); + + /* write start address and end address for IO window */ + mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), alr); + mmio_write_32(IO_WIN_AHR_OFFSET(ap_index, win_num), ahr); + + /* write window target */ + mmio_write_32(IO_WIN_CR_OFFSET(ap_index, win_num), win->target_id); +} + +static void io_win_disable_window(int ap_index, uint32_t win_num) +{ + uint32_t win_reg; + + if ((win_num == 0) || (win_num > MVEBU_IO_WIN_MAX_WINS)) { + ERROR("Disabling wrong IOW window %d!\n", win_num); + return; + } + + win_reg = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_num)); + win_reg &= ~WIN_ENABLE_BIT; + mmio_write_32(IO_WIN_ALR_OFFSET(ap_index, win_num), 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 iow_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_IO_WIN_MAX_WINS - i - 1; + io_win_check(win); + io_win_enable_window(ap_index, win, win_id); + win++; + } +} + +/* + * NOTE: Use the same window array for insertion and removal of + * temporary windows. + */ +void iow_temp_win_remove(int ap_index, struct addr_map_win *win, int size) +{ + uint32_t win_id; + + /* Start from the last window and do not touch Win0 */ + for (int i = 0; i < size; i++) { + uint64_t base; + uint32_t target; + + win_id = MVEBU_IO_WIN_MAX_WINS - i - 1; + + target = mmio_read_32(IO_WIN_CR_OFFSET(ap_index, win_id)); + base = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id)); + base &= ~WIN_ENABLE_BIT; + 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; + } + io_win_disable_window(ap_index, win_id); + win++; + } +} + +#ifdef DEBUG_ADDR_MAP +static void dump_io_win(int ap_index) +{ + uint32_t trgt_id, win_id; + uint32_t alr, ahr; + uint64_t start, end; + + /* Dump all IO windows */ + printf("\tbank target start end\n"); + printf("\t----------------------------------------------------\n"); + for (win_id = 0; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++) { + alr = mmio_read_32(IO_WIN_ALR_OFFSET(ap_index, win_id)); + if (alr & WIN_ENABLE_BIT) { + alr &= ~WIN_ENABLE_BIT; + ahr = mmio_read_32(IO_WIN_AHR_OFFSET(ap_index, win_id)); + trgt_id = mmio_read_32(IO_WIN_CR_OFFSET(ap_index, + win_id)); + start = ((uint64_t)alr << ADDRESS_SHIFT); + end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT); + printf("\tio-win %d 0x%016" PRIx64 " 0x%016" PRIx64 "\n", + trgt_id, start, end); + } + } + printf("\tio-win gcr is %x\n", + mmio_read_32(MVEBU_IO_WIN_BASE(ap_index) + + MVEBU_IO_WIN_GCR_OFFSET)); +} +#endif + +static void iow_save_win_range(int ap_id, int win_first, int win_last, + uint32_t *buffer) +{ + int win_id, idx; + + /* Save IOW */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + buffer[idx++] = mmio_read_32(IO_WIN_CR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(IO_WIN_ALR_OFFSET(ap_id, win_id)); + buffer[idx++] = mmio_read_32(IO_WIN_AHR_OFFSET(ap_id, win_id)); + } + buffer[idx] = mmio_read_32(MVEBU_IO_WIN_BASE(ap_id) + + MVEBU_IO_WIN_GCR_OFFSET); +} + +static void iow_restore_win_range(int ap_id, int win_first, int win_last, + uint32_t *buffer) +{ + int win_id, idx; + + /* Restore IOW */ + for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) { + mmio_write_32(IO_WIN_CR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(IO_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]); + mmio_write_32(IO_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]); + } + mmio_write_32(MVEBU_IO_WIN_BASE(ap_id) + MVEBU_IO_WIN_GCR_OFFSET, + buffer[idx++]); +} + +void iow_save_win_all(int ap_id) +{ + iow_save_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1, + io_win_regs_save); +} + +void iow_restore_win_all(int ap_id) +{ + iow_restore_win_range(ap_id, 0, MVEBU_IO_WIN_MAX_WINS - 1, + io_win_regs_save); +} + +int init_io_win(int ap_index) +{ + struct addr_map_win *win; + uint32_t win_id, win_reg; + uint32_t win_count; + + INFO("Initializing IO WIN Address decoding\n"); + + /* Get the array of the windows and its size */ + marvell_get_io_win_memory_map(ap_index, &win, &win_count); + if (win_count <= 0) + INFO("no windows configurations found\n"); + + if (win_count > MVEBU_IO_WIN_MAX_WINS) { + INFO("number of windows is bigger than %d\n", + MVEBU_IO_WIN_MAX_WINS); + return 0; + } + + /* Get the default target id to set the GCR */ + win_reg = marvell_get_io_win_gcr_target(ap_index); + mmio_write_32(MVEBU_IO_WIN_BASE(ap_index) + MVEBU_IO_WIN_GCR_OFFSET, + win_reg); + + /* disable all IO windows */ + for (win_id = 1; win_id < MVEBU_IO_WIN_MAX_WINS; win_id++) + io_win_disable_window(ap_index, win_id); + + /* enable relevant windows, starting from win_id = 1 because + * index 0 dedicated for BootROM + */ + for (win_id = 1; win_id <= win_count; win_id++, win++) { + io_win_check(win); + io_win_enable_window(ap_index, win, win_id); + } + +#ifdef DEBUG_ADDR_MAP + dump_io_win(ap_index); +#endif + + INFO("Done IO WIN Address decoding Initializing\n"); + + return 0; +} |