diff options
Diffstat (limited to 'plat/marvell/a8k/common/plat_ble_setup.c')
-rw-r--r-- | plat/marvell/a8k/common/plat_ble_setup.c | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/plat/marvell/a8k/common/plat_ble_setup.c b/plat/marvell/a8k/common/plat_ble_setup.c new file mode 100644 index 00000000..7f55ab4a --- /dev/null +++ b/plat/marvell/a8k/common/plat_ble_setup.c @@ -0,0 +1,406 @@ +/* + * *************************************************************************** + * Copyright (C) 2016 Marvell International Ltd. + * *************************************************************************** + * + * 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 Marvell 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 <plat_marvell.h> +#include <plat_config.h> +#include <plat_def.h> +#include <debug.h> +#include <sys_info.h> +#include <dram_if.h> +#include <ccu.h> +#include <rfu.h> +#include <apn806_setup.h> +#include <cp110_setup.h> + +/* Register for skip image use */ +#define SCRATCH_PAD_REG2 0xF06F00A8 +#define SCRATCH_PAD_SKIP_VAL 0x01 +#define NUM_OF_GPIO_PER_REG 32 + +/* CCU windows configuration defines */ +#define CCU_CFG_IO_WIN_NUM (3) +#define CCU_CFG_WIN_REGS_NUM (4) /* CR + SCR + ALR + AHR */ +#define CCU_WIN_TARGET_OFFSET (8) +#define CCU_WIN_TARGET_MASK (0xf) +#define CCU_WIN_ADDR_SHIFT (20) +#define CCU_WIN_ADDR_OFFSET (4) +#define CCU_WIN_ADDR_MASK (0xFFFFFFF) +#define CCU_WIN_CR_OFFSET(w) (0x0 + (w)*0x10) +#define CCU_WIN_SCR_OFFSET(w) (0x4 + (w)*0x10) +#define CCU_WIN_ALR_OFFSET(w) (0x8 + (w)*0x10) +#define CCU_WIN_AHR_OFFSET(w) (0xC + (w)*0x10) +#define CCU_WIN_GCR_OFFSET (0xD0) + +/* IO windows configuration */ +#define IOW_CFG_IO_WIN_NUM_ST (2) +#define IOW_CFG_IO_WIN_NUM_END (IOW_CFG_IO_WIN_NUM_ST + CP_COUNT - 1) +#define IOW_GCR_OFFSET (0x70) +#define IOW_WIN_ALR_OFFSET(w) (0x10 + 0x10*(w)) + +#define MMAP_SAVE_AND_CONFIG 0 +#define MMAP_RESTORE_SAVED 1 + +/* SAR clock settings */ +#define MVEBU_AP_GEN_MGMT_BASE (MVEBU_RFU_BASE + 0x8000) +#define MVEBU_AP_SAR_REG_BASE(r) (MVEBU_AP_GEN_MGMT_BASE + 0x200 +\ + ((r) << 2)) + +#define FREQ_MODE_AP_SAR_REG_NUM (0) +#define SAR_CLOCK_FREQ_MODE_OFFSET (0) +#define SAR_CLOCK_FREQ_MODE_MASK (0x1f << SAR_CLOCK_FREQ_MODE_OFFSET) + +#define AVS_EN_CTRL_REG (MVEBU_AP_GEN_MGMT_BASE + 0x130) +#define AVS_ENABLE_OFFSET (0) +#define AVS_SOFT_RESET_OFFSET (2) +#define AVS_LOW_VDD_LIMIT_OFFSET (4) +#define AVS_HIGH_VDD_LIMIT_OFFSET (12) +#define AVS_TARGET_DELTA_OFFSET (21) +/* VDD limit is 0.9V for A70x0 @ CPU frequency < 1600MHz */ +#define AVS_A7K_LOW_CLK_VALUE ((0x80 << AVS_TARGET_DELTA_OFFSET) | \ + (0x1A << AVS_HIGH_VDD_LIMIT_OFFSET) | \ + (0x1A << AVS_LOW_VDD_LIMIT_OFFSET) | \ + (0x1 << AVS_SOFT_RESET_OFFSET) | \ + (0x1 << AVS_ENABLE_OFFSET)) +/* VDD limit is 1.0V for all A80x0 devices */ +#define AVS_A8K_CLK_VALUE ((0x80 << AVS_TARGET_DELTA_OFFSET) | \ + (0x24 << AVS_HIGH_VDD_LIMIT_OFFSET) | \ + (0x24 << AVS_LOW_VDD_LIMIT_OFFSET) | \ + (0x1 << AVS_SOFT_RESET_OFFSET) | \ + (0x1 << AVS_ENABLE_OFFSET)) + +enum cpu_clock_freq_mode { + CPU_2000_DDR_1200_RCLK_1200 = 0x0, + CPU_2000_DDR_1050_RCLK_1050 = 0x1, + CPU_1600_DDR_800_RCLK_800 = 0x4, + CPU_1800_DDR_1200_RCLK_1200 = 0x6, + CPU_1800_DDR_1050_RCLK_1050 = 0x7, + CPU_1600_DDR_900_RCLK_900 = 0x0B, + CPU_1600_DDR_1050_RCLK_1050 = 0x0D, + CPU_1600_DDR_900_RCLK_900_2 = 0x0E, + CPU_1000_DDR_650_RCLK_650 = 0x13, + CPU_1300_DDR_800_RCLK_800 = 0x14, + CPU_1300_DDR_650_RCLK_650 = 0x17, + CPU_1200_DDR_800_RCLK_800 = 0x19, + CPU_1400_DDR_800_RCLK_800 = 0x1a, + CPU_600_DDR_800_RCLK_800 = 0x1B, + CPU_800_DDR_800_RCLK_800 = 0x1C, + CPU_1000_DDR_800_RCLK_800 = 0x1D, + CPU_DDR_RCLK_INVALID +}; + +/* Notify bootloader on DRAM setup */ +void pass_dram_sys_info(struct dram_config *cfg) +{ + set_info(DRAM_BUS_WIDTH, cfg->iface[0].bus_width); + set_info(DRAM_CS0_SIZE, cfg->iface[0].size_mbytes); + set_info(DRAM_CS0, 1); + set_info(DRAM_CS1, 0); + set_info(DRAM_CS2, 0); + set_info(DRAM_CS3, 0); +} +/****************************************************************************** + * The routine allows to save the CCU and IO windows configuration during DRAM + * setup and restore them afterwards before exiting the BLE stage. + * Such window configuration is requred since not all default settings coming + * from the HW and the BootROM akkow access to periferals connected to + * all available CPn components. + * For instance, when the boot device is located on CP0, the IO window to CP1 + * is not opened automatically by the HW and if the DRAM SPD is located on CP1 + * i2c channel, it cannot be read at BLE stage. + * Therefore the DRAM init procedure have to provide access to all available + * CPn periferals during the BLE stage by setting the CCU IO window to all CPn + * addresses and by enabling the IO windows accordingly. + * Additionally this function configures the CCU GCR to DRAM, which allows + * usage or more than 4GB DRAM as it configured by the default CCU DRAM window. + * + * IN: + * MMAP_SAVE_AND_CONFIG - save the existing configuration and update it + * MMAP_RESTORE_SAVED - restore saved configuration + * OUT: + * NONE + **************************************************************************** + */ +static void ble_plat_mmap_config(int restore) +{ + static uint32_t ccu_win_regs[CCU_CFG_WIN_REGS_NUM]; + static uint32_t io_win_regs[CP_COUNT]; + static uint32_t ccu_gcr, iow_gcr; + uintptr_t ccu_base = MVEBU_CCU_BASE; + uintptr_t iow_base = MVEBU_RFU_BASE; + uint32_t reg_val, win_num; + + if (restore == MMAP_RESTORE_SAVED) { + /* Restore all orig. settings that were modified by BLE stage */ + /* Restore CCU */ + mmio_write_32(ccu_base + CCU_WIN_CR_OFFSET(CCU_CFG_IO_WIN_NUM), + ccu_win_regs[0]); + mmio_write_32(ccu_base + CCU_WIN_SCR_OFFSET(CCU_CFG_IO_WIN_NUM), + ccu_win_regs[1]); + mmio_write_32(ccu_base + CCU_WIN_ALR_OFFSET(CCU_CFG_IO_WIN_NUM), + ccu_win_regs[2]); + mmio_write_32(ccu_base + CCU_WIN_AHR_OFFSET(CCU_CFG_IO_WIN_NUM), + ccu_win_regs[3]); + mmio_write_32(ccu_base + CCU_WIN_GCR_OFFSET, ccu_gcr); + /* Restore IO Windows */ + for (win_num = IOW_CFG_IO_WIN_NUM_ST; + win_num < IOW_CFG_IO_WIN_NUM_END; win_num++) + mmio_write_32(iow_base + IOW_WIN_ALR_OFFSET(win_num), + io_win_regs[win_num - + IOW_CFG_IO_WIN_NUM_ST]); + mmio_write_32(iow_base + IOW_GCR_OFFSET, iow_gcr); + return; + } else { + /* Store original values */ + /* Save CCU */ + ccu_win_regs[0] = mmio_read_32(ccu_base + + CCU_WIN_CR_OFFSET(CCU_CFG_IO_WIN_NUM)); + ccu_win_regs[1] = mmio_read_32(ccu_base + + CCU_WIN_SCR_OFFSET(CCU_CFG_IO_WIN_NUM)); + ccu_win_regs[2] = mmio_read_32(ccu_base + + CCU_WIN_ALR_OFFSET(CCU_CFG_IO_WIN_NUM)); + ccu_win_regs[3] = mmio_read_32(ccu_base + + CCU_WIN_AHR_OFFSET(CCU_CFG_IO_WIN_NUM)); + ccu_gcr = mmio_read_32(ccu_base + CCU_WIN_GCR_OFFSET); + /* Save IO Windows */ + for (win_num = IOW_CFG_IO_WIN_NUM_ST; + win_num < IOW_CFG_IO_WIN_NUM_END; win_num++) + io_win_regs[win_num - IOW_CFG_IO_WIN_NUM_ST] = + mmio_read_32(iow_base + + IOW_WIN_ALR_OFFSET(win_num)); + iow_gcr = mmio_read_32(iow_base + IOW_GCR_OFFSET); + } + + /* The configuration saved, now all the changes can be done */ + /* Set the default CCU target ID to DRAM 0 */ + reg_val = ccu_gcr & ~(CCU_WIN_TARGET_MASK << CCU_WIN_TARGET_OFFSET); + reg_val |= (DRAM_0_TID & CCU_WIN_TARGET_MASK) << CCU_WIN_TARGET_OFFSET; + mmio_write_32(ccu_base + CCU_WIN_GCR_OFFSET, reg_val); + + /* Set CCU IO window for covering all available CP addresses */ + /* Set the CCU IO window Low Address to the start of CP0 region */ + mmio_write_32(ccu_base + CCU_WIN_CR_OFFSET(CCU_CFG_IO_WIN_NUM), 0); + reg_val = (MVEBU_CP_REGS_BASE(0) >> CCU_WIN_ADDR_SHIFT) + << CCU_WIN_ADDR_OFFSET; + mmio_write_32(ccu_base + + CCU_WIN_ALR_OFFSET(CCU_CFG_IO_WIN_NUM), reg_val); + + /* + * Set the CCU IO window High Address to the end of + * CPn region (n = CP_COUNT) + */ + reg_val = (MVEBU_CP_REGS_BASE(CP_COUNT - 1) >> CCU_WIN_ADDR_SHIFT) + << CCU_WIN_ADDR_OFFSET; + reg_val |= 0xF << CCU_WIN_ADDR_OFFSET; + mmio_write_32(ccu_base + CCU_WIN_AHR_OFFSET(CCU_CFG_IO_WIN_NUM), + reg_val); + + /* Set the CCU IO window Control target to IO and enable this window */ + reg_val = (IO_0_TID << CCU_WIN_TARGET_OFFSET) | 0x1; + mmio_write_32(ccu_base + CCU_WIN_CR_OFFSET(CCU_CFG_IO_WIN_NUM), + reg_val); + + /* Set IO windows GCR (IO decode) to PIDI as a default (CP0) */ + mmio_write_32(iow_base + IOW_GCR_OFFSET, PIDI_TID); + + /* + * Optionally enable IO windows for CP1, CP2, CP3 (depends on CP_COUNT). + * All the rest of windows default settings (ALR, AHR, CR) are already + * set by the HW + */ + for (win_num = IOW_CFG_IO_WIN_NUM_ST; + win_num < IOW_CFG_IO_WIN_NUM_END; win_num++) { + reg_val = mmio_read_32(iow_base + IOW_WIN_ALR_OFFSET(win_num)); + reg_val |= 0x1; + mmio_write_32(iow_base + IOW_WIN_ALR_OFFSET(win_num), reg_val); + } +} + +/****************************************************************************** + * Setup Adaptive Voltage Switching - this is required for some platforms + *****************************************************************************/ +static void ble_plat_avs_config(void) +{ + uint32_t reg_val, device_id; + + /* Do nothing on A0 revision SoCs */ + if (apn806_rev_id_get() == APN806_REV_ID_A0) + return; + + /* Check which SoC is running and act accordingly */ + device_id = cp110_device_id_get(); + switch (device_id) { + case MVEBU_80X0_DEV_ID: + /* Set the new AVS value - fix the default one on A80x0 */ + mmio_write_32(AVS_EN_CTRL_REG, AVS_A8K_CLK_VALUE); + break; + + case MVEBU_70X0_DEV_ID: + /* Only fix AVS for CPU clocks lower than 1600MHz on A70x0 */ + reg_val = mmio_read_32(MVEBU_AP_SAR_REG_BASE( + FREQ_MODE_AP_SAR_REG_NUM)); + reg_val &= SAR_CLOCK_FREQ_MODE_MASK; + reg_val >>= SAR_CLOCK_FREQ_MODE_OFFSET; + if ((reg_val > CPU_1600_DDR_900_RCLK_900_2) && + (reg_val < CPU_DDR_RCLK_INVALID)) + mmio_write_32(AVS_EN_CTRL_REG, AVS_A7K_LOW_CLK_VALUE); + break; + + default: + ERROR("Unsupported Device ID 0x%x\n", device_id); + } +} + +static int ble_skip_image_i2c(struct skip_image *skip_im) +{ + ERROR("skipping image using i2c is not supported\n"); + /* not supported */ + return 0; +} + +static int ble_skip_image_other(struct skip_image *skip_im) +{ + ERROR("implementation missing for skip image request\n"); + /* not supported, make your own implementation */ + return 0; +} + +static int ble_skip_image_gpio(struct skip_image *skip_im) +{ + unsigned int val; + unsigned int mpp_address = 0; + unsigned int offset = 0; + + switch (skip_im->info.test.cp_ap) { + case(CP): + mpp_address = MVEBU_CP_GPIO_DATA_IN(skip_im->info.test.cp_index, + skip_im->info.gpio.num); + if (skip_im->info.gpio.num > NUM_OF_GPIO_PER_REG) + offset = skip_im->info.gpio.num - NUM_OF_GPIO_PER_REG; + else + offset = skip_im->info.gpio.num; + break; + case(AP): + mpp_address = MVEBU_AP_GPIO_DATA_IN; + offset = skip_im->info.gpio.num; + break; + } + + val = mmio_read_32(mpp_address); + val &= (1 << offset); + if ((!val && skip_im->info.gpio.button_state == HIGH) || + (val && skip_im->info.gpio.button_state == LOW)) { + mmio_write_32(SCRATCH_PAD_REG2, SCRATCH_PAD_SKIP_VAL); + return 1; + } + + return 0; +} + +/* + * This function checks if there's a skip image request: + * return values: + * 1: (true) images request been made. + * 0: (false) no image request been made. + */ +static int ble_skip_current_image(void) +{ + struct skip_image *skip_im; + + /*fetching skip image info*/ + skip_im = (struct skip_image *)plat_get_skip_image_data(); + + if (skip_im == NULL) + return 0; + + /* check if skipping image request has already been made */ + if (mmio_read_32(SCRATCH_PAD_REG2) == SCRATCH_PAD_SKIP_VAL) + return 0; + + switch (skip_im->detection_method) { + case GPIO: + return ble_skip_image_gpio(skip_im); + case I2C: + return ble_skip_image_i2c(skip_im); + case USER_DEFINED: + return ble_skip_image_other(skip_im); + } + + return 0; +} + +int ble_plat_setup(int *skip) +{ + int ret; + struct dram_config *cfg; + + /* + * Save the current CCU configuration and make required changes: + * - Allow access to DRAM larger than 4GB + * - Open memory access to all CPn periferals + */ + ble_plat_mmap_config(MMAP_SAVE_AND_CONFIG); + + /* Check if there's a skip request to bootRom recovey Image */ + if (ble_skip_current_image()) { + /* close memory access to all CPn periferals. */ + ble_plat_mmap_config(MMAP_RESTORE_SAVED); + *skip = 1; + return 0; + } + +#if PCI_EP_SUPPORT + /* Do basic CP-110 setups */ + cp110_ble_init(0); + + /* Enable PCIe in end point mode */ + ble_plat_pcie_ep_setup(); +#endif + + /* Setup AVS */ + ble_plat_avs_config(); + + /* Get dram data from platform */ + cfg = (struct dram_config *)plat_get_dram_data(); + + /* Kick it in */ + ret = dram_init(cfg); + + /* Restore the original CCU configuration before exit from BLE */ + ble_plat_mmap_config(MMAP_RESTORE_SAVED); + + /* Pass DRAM information to bootloader */ + pass_dram_sys_info(cfg); + + return ret; +} |