blob: 0cd62cbdb2ec4f2fe69dfbf5b7c43b51e9f89e02 [file] [log] [blame]
/*
* Copyright (C) 2018 Marvell International Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
* https://spdx.org/licenses
*/
#include <a8k_common.h>
#include <ap_setup.h>
#include <aro.h>
#include <ccu.h>
#include <cp110_setup.h>
#include <debug.h>
#include <io_win.h>
#include <mv_ddr_if.h>
#include <mvebu_def.h>
#include <plat_marvell.h>
/* Register for skip image use */
#define SCRATCH_PAD_REG2 0xF06F00A8
#define SCRATCH_PAD_SKIP_VAL 0x01
#define NUM_OF_GPIO_PER_REG 32
#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 SAR_CLOCK_FREQ_MODE_OFFSET (0)
#define SAR_CLOCK_FREQ_MODE_MASK (0x1f << SAR_CLOCK_FREQ_MODE_OFFSET)
#define SAR_PIDI_LOW_SPEED_OFFSET (20)
#define SAR_PIDI_LOW_SPEED_MASK (1 << SAR_PIDI_LOW_SPEED_OFFSET)
#define SAR_PIDI_LOW_SPEED_SHIFT (15)
#define SAR_PIDI_LOW_SPEED_SET (1 << SAR_PIDI_LOW_SPEED_SHIFT)
#define FREQ_MODE_AP_SAR_REG_NUM (0)
#define SAR_CLOCK_FREQ_MODE(v) (((v) & SAR_CLOCK_FREQ_MODE_MASK) >> \
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)
#define AVS_VDD_LOW_LIMIT_MASK (0xFF << AVS_LOW_VDD_LIMIT_OFFSET)
#define AVS_VDD_HIGH_LIMIT_MASK (0xFF << AVS_HIGH_VDD_LIMIT_OFFSET)
/* 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))
#define AVS_A3900_CLK_VALUE ((0x80 << 24) | \
(0x2c2 << 13) | \
(0x2c2 << 3) | \
(0x1 << AVS_SOFT_RESET_OFFSET) | \
(0x1 << AVS_ENABLE_OFFSET))
#define MVEBU_AP_EFUSE_SRV_CTRL_REG (MVEBU_AP_GEN_MGMT_BASE + 0x8)
#define EFUSE_SRV_CTRL_LD_SELECT_OFFS 6
#define EFUSE_SRV_CTRL_LD_SEL_USER_MASK (1 << EFUSE_SRV_CTRL_LD_SELECT_OFFS)
/* Notify bootloader on DRAM setup */
#define AP807_CPU_ARO_0_CTRL_0 (MVEBU_RFU_BASE + 0x82A8)
#define AP807_CPU_ARO_1_CTRL_0 (MVEBU_RFU_BASE + 0x8D00)
/* 0 - ARO clock is enabled, 1 - ARO clock is disabled */
#define AP807_CPU_ARO_CLK_EN_OFFSET 0
#define AP807_CPU_ARO_CLK_EN_MASK (0x1 << AP807_CPU_ARO_CLK_EN_OFFSET)
/* 0 - ARO is the clock source, 1 - PLL is the clock source */
#define AP807_CPU_ARO_SEL_PLL_OFFSET 5
#define AP807_CPU_ARO_SEL_PLL_MASK (0x1 << AP807_CPU_ARO_SEL_PLL_OFFSET)
/*
* - AVS work points in the LD0 eFuse:
* SVC1 work point: LD0[88:81]
* SVC2 work point: LD0[96:89]
* SVC3 work point: LD0[104:97]
* SVC4 work point: LD0[112:105]
* - Identification information in the LD-0 eFuse:
* DRO: LD0[74:65] - Not used by the SW
* Revision: LD0[78:75] - Not used by the SW
* Bin: LD0[80:79] - Not used by the SW
* SW Revision: LD0[115:113]
* Cluster 1 PWR: LD0[193] - if set to 1, power down CPU Cluster-1
* resulting in 2 CPUs active only (7020)
*/
#define MVEBU_AP_LD_EFUSE_BASE (MVEBU_AP_GEN_MGMT_BASE + 0xF00)
/* Bits [94:63] - 32 data bits total */
#define MVEBU_AP_LD0_94_63_EFUSE_OFFS (MVEBU_AP_LD_EFUSE_BASE + 0x8)
/* Bits [125:95] - 31 data bits total, 32nd bit is parity for bits [125:63] */
#define MVEBU_AP_LD0_125_95_EFUSE_OFFS (MVEBU_AP_LD_EFUSE_BASE + 0xC)
/* Bits [220:189] - 32 data bits total */
#define MVEBU_AP_LD0_220_189_EFUSE_OFFS (MVEBU_AP_LD_EFUSE_BASE + 0x18)
/* Offsets for the above 2 fields combined into single 64-bit value [125:63] */
#define EFUSE_AP_LD0_DRO_OFFS 2 /* LD0[74:65] */
#define EFUSE_AP_LD0_DRO_MASK 0x3FF
#define EFUSE_AP_LD0_REVID_OFFS 12 /* LD0[78:75] */
#define EFUSE_AP_LD0_REVID_MASK 0xF
#define EFUSE_AP_LD0_BIN_OFFS 16 /* LD0[80:79] */
#define EFUSE_AP_LD0_BIN_MASK 0x3
#define EFUSE_AP_LD0_SWREV_OFFS 50 /* LD0[115:113] */
#define EFUSE_AP_LD0_SWREV_MASK 0x7
#define EFUSE_AP_LD0_SVC1_OFFS 18 /* LD0[88:81] */
#define EFUSE_AP_LD0_SVC2_OFFS 26 /* LD0[96:89] */
#define EFUSE_AP_LD0_SVC3_OFFS 34 /* LD0[104:97] */
#define EFUSE_AP_LD0_SVC4_OFFS 42 /* LD0[112:105] */
#define EFUSE_AP_LD0_WP_MASK 0xFF
#define EFUSE_AP_LD0_CLUSTER_DOWN_OFFS 4
/* Return the AP revision of the chip */
static unsigned int ble_get_ap_type(void)
{
unsigned int chip_rev_id;
chip_rev_id = mmio_read_32(MVEBU_CSS_GWD_CTRL_IIDR2_REG);
chip_rev_id = ((chip_rev_id & GWD_IIDR2_CHIP_ID_MASK) >>
GWD_IIDR2_CHIP_ID_OFFSET);
return chip_rev_id;
}
/******************************************************************************
* 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 required since not all default settings coming
* from the HW and the BootROM allow access to peripherals 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 peripherals during the BLE stage by setting the CCU IO window to all
* CPnph 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)
{
if (restore == MMAP_RESTORE_SAVED) {
/* Restore all orig. settings that were modified by BLE stage */
ccu_restore_win_all(MVEBU_AP0);
/* Restore CCU */
iow_restore_win_all(MVEBU_AP0);
return;
}
/* Store original values */
ccu_save_win_all(MVEBU_AP0);
/* Save CCU */
iow_save_win_all(MVEBU_AP0);
init_ccu(MVEBU_AP0);
/* The configuration saved, now all the changes can be done */
init_io_win(MVEBU_AP0);
}
/****************************************************************************
* Setup Adaptive Voltage Switching - this is required for some platforms
****************************************************************************
*/
static void ble_plat_avs_config(void)
{
uint32_t reg_val, device_id;
/* Check which SoC is running and act accordingly */
if (ble_get_ap_type() == CHIP_ID_AP807) {
VERBOSE("AVS: Setting AP807 AVS CTRL to 0x%x\n",
AVS_A3900_CLK_VALUE);
mmio_write_32(AVS_EN_CTRL_REG, AVS_A3900_CLK_VALUE);
return;
}
/* Check which SoC is running and act accordingly */
device_id = cp110_device_id_get(MVEBU_CP_REGS_BASE(0));
switch (device_id) {
case MVEBU_80X0_DEV_ID:
case MVEBU_80X0_CP115_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:
case MVEBU_70X0_CP115_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);
}
}
/****************************************************************************
* SVC flow - v0.10
* The feature is intended to configure AVS value according to eFuse values
* that are burned individually for each SoC during the test process.
* Primary AVS value is stored in HD efuse and processed on power on
* by the HW engine
* Secondary AVS value is located in LD efuse and contains 4 work points for
* various CPU frequencies.
* The Secondary AVS value is only taken into account if the SW Revision stored
* in the efuse is greater than 0 and the CPU is running in a certain speed.
****************************************************************************
*/
static void ble_plat_svc_config(void)
{
uint32_t reg_val, avs_workpoint, freq_pidi_mode;
uint64_t efuse;
uint32_t device_id, single_cluster;
uint8_t svc[4], perr[4], i, sw_ver;
/* Due to a bug in A3900 device_id skip SVC config
* TODO: add SVC config once it is decided for a3900
*/
if (ble_get_ap_type() == CHIP_ID_AP807) {
NOTICE("SVC: SVC is not supported on AP807\n");
ble_plat_avs_config();
return;
}
/* Set access to LD0 */
reg_val = mmio_read_32(MVEBU_AP_EFUSE_SRV_CTRL_REG);
reg_val &= ~EFUSE_SRV_CTRL_LD_SELECT_OFFS;
mmio_write_32(MVEBU_AP_EFUSE_SRV_CTRL_REG, reg_val);
/* Obtain the value of LD0[125:63] */
efuse = mmio_read_32(MVEBU_AP_LD0_125_95_EFUSE_OFFS);
efuse <<= 32;
efuse |= mmio_read_32(MVEBU_AP_LD0_94_63_EFUSE_OFFS);
/* SW Revision:
* Starting from SW revision 1 the SVC flow is supported.
* SW version 0 (efuse not programmed) should follow the
* regular AVS update flow.
*/
sw_ver = (efuse >> EFUSE_AP_LD0_SWREV_OFFS) & EFUSE_AP_LD0_SWREV_MASK;
if (sw_ver < 1) {
NOTICE("SVC: SW Revision 0x%x. SVC is not supported\n", sw_ver);
ble_plat_avs_config();
return;
}
/* Frequency mode from SAR */
freq_pidi_mode = SAR_CLOCK_FREQ_MODE(
mmio_read_32(
MVEBU_AP_SAR_REG_BASE(
FREQ_MODE_AP_SAR_REG_NUM)));
/* Decode all SVC work points */
svc[0] = (efuse >> EFUSE_AP_LD0_SVC1_OFFS) & EFUSE_AP_LD0_WP_MASK;
svc[1] = (efuse >> EFUSE_AP_LD0_SVC2_OFFS) & EFUSE_AP_LD0_WP_MASK;
svc[2] = (efuse >> EFUSE_AP_LD0_SVC3_OFFS) & EFUSE_AP_LD0_WP_MASK;
svc[3] = (efuse >> EFUSE_AP_LD0_SVC4_OFFS) & EFUSE_AP_LD0_WP_MASK;
INFO("SVC: Efuse WP: [0]=0x%x, [1]=0x%x, [2]=0x%x, [3]=0x%x\n",
svc[0], svc[1], svc[2], svc[3]);
/* Validate parity of SVC workpoint values */
for (i = 0; i < 4; i++) {
uint8_t parity, bit;
perr[i] = 0;
for (bit = 1, parity = svc[i] & 1; bit < 7; bit++)
parity ^= (svc[i] >> bit) & 1;
/* Starting from SW version 2, the parity check is mandatory */
if ((sw_ver > 1) && (parity != ((svc[i] >> 7) & 1)))
perr[i] = 1; /* register the error */
}
single_cluster = mmio_read_32(MVEBU_AP_LD0_220_189_EFUSE_OFFS);
single_cluster = (single_cluster >> EFUSE_AP_LD0_CLUSTER_DOWN_OFFS) & 1;
device_id = cp110_device_id_get(MVEBU_CP_REGS_BASE(0));
if (device_id == MVEBU_80X0_DEV_ID ||
device_id == MVEBU_80X0_CP115_DEV_ID) {
/* A8040/A8020 */
NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n",
single_cluster == 0 ? "8040" : "8020", freq_pidi_mode);
switch (freq_pidi_mode) {
case CPU_1800_DDR_1200_RCLK_1200:
case CPU_1800_DDR_1050_RCLK_1050:
if (perr[1])
goto perror;
avs_workpoint = svc[1];
break;
case CPU_1600_DDR_1050_RCLK_1050:
case CPU_1600_DDR_900_RCLK_900_2:
if (perr[2])
goto perror;
avs_workpoint = svc[2];
break;
case CPU_1300_DDR_800_RCLK_800:
case CPU_1300_DDR_650_RCLK_650:
if (perr[3])
goto perror;
avs_workpoint = svc[3];
break;
case CPU_2000_DDR_1200_RCLK_1200:
case CPU_2000_DDR_1050_RCLK_1050:
default:
if (perr[0])
goto perror;
avs_workpoint = svc[0];
break;
}
} else if (device_id == MVEBU_70X0_DEV_ID ||
device_id == MVEBU_70X0_CP115_DEV_ID) {
/* A7040/A7020/A6040 */
NOTICE("SVC: DEV ID: %s, FREQ Mode: 0x%x\n",
single_cluster == 0 ? "7040" : "7020", freq_pidi_mode);
switch (freq_pidi_mode) {
case CPU_1400_DDR_800_RCLK_800:
if (single_cluster) {/* 7020 */
if (perr[1])
goto perror;
avs_workpoint = svc[1];
} else {
if (perr[0])
goto perror;
avs_workpoint = svc[0];
}
break;
case CPU_1200_DDR_800_RCLK_800:
if (single_cluster) {/* 7020 */
if (perr[2])
goto perror;
avs_workpoint = svc[2];
} else {
if (perr[1])
goto perror;
avs_workpoint = svc[1];
}
break;
case CPU_800_DDR_800_RCLK_800:
case CPU_1000_DDR_800_RCLK_800:
if (single_cluster) {/* 7020 */
if (perr[3])
goto perror;
avs_workpoint = svc[3];
} else {
if (perr[2])
goto perror;
avs_workpoint = svc[2];
}
break;
case CPU_600_DDR_800_RCLK_800:
if (perr[3])
goto perror;
avs_workpoint = svc[3]; /* Same for 6040 and 7020 */
break;
case CPU_1600_DDR_800_RCLK_800: /* 7020 only */
default:
if (single_cluster) {/* 7020 */
if (perr[0])
goto perror;
avs_workpoint = svc[0];
} else
avs_workpoint = 0;
break;
}
} else {
ERROR("SVC: Unsupported Device ID 0x%x\n", device_id);
return;
}
/* Set AVS control if needed */
if (avs_workpoint == 0) {
ERROR("SVC: AVS work point not changed\n");
return;
}
/* Remove parity bit */
avs_workpoint &= 0x7F;
reg_val = mmio_read_32(AVS_EN_CTRL_REG);
NOTICE("SVC: AVS work point changed from 0x%x to 0x%x\n",
(reg_val & AVS_VDD_LOW_LIMIT_MASK) >> AVS_LOW_VDD_LIMIT_OFFSET,
avs_workpoint);
reg_val &= ~(AVS_VDD_LOW_LIMIT_MASK | AVS_VDD_HIGH_LIMIT_MASK);
reg_val |= 0x1 << AVS_ENABLE_OFFSET;
reg_val |= avs_workpoint << AVS_HIGH_VDD_LIMIT_OFFSET;
reg_val |= avs_workpoint << AVS_LOW_VDD_LIMIT_OFFSET;
mmio_write_32(AVS_EN_CTRL_REG, reg_val);
return;
perror:
ERROR("Failed SVC WP[%d] parity check!\n", i);
ERROR("Ignoring the WP values\n");
}
#if PLAT_RECOVERY_IMAGE_ENABLE
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_marvell_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;
}
#endif
/* Switch to ARO from PLL in ap807 */
static void aro_to_pll(void)
{
unsigned int reg;
/* switch from ARO to PLL */
reg = mmio_read_32(AP807_CPU_ARO_0_CTRL_0);
reg |= AP807_CPU_ARO_SEL_PLL_MASK;
mmio_write_32(AP807_CPU_ARO_0_CTRL_0, reg);
reg = mmio_read_32(AP807_CPU_ARO_1_CTRL_0);
reg |= AP807_CPU_ARO_SEL_PLL_MASK;
mmio_write_32(AP807_CPU_ARO_1_CTRL_0, reg);
mdelay(1000);
/* disable ARO clk driver */
reg = mmio_read_32(AP807_CPU_ARO_0_CTRL_0);
reg |= (AP807_CPU_ARO_CLK_EN_MASK);
mmio_write_32(AP807_CPU_ARO_0_CTRL_0, reg);
reg = mmio_read_32(AP807_CPU_ARO_1_CTRL_0);
reg |= (AP807_CPU_ARO_CLK_EN_MASK);
mmio_write_32(AP807_CPU_ARO_1_CTRL_0, reg);
}
int ble_plat_setup(int *skip)
{
int ret;
/* Power down unused CPUs */
plat_marvell_early_cpu_powerdown();
/*
* Save the current CCU configuration and make required changes:
* - Allow access to DRAM larger than 4GB
* - Open memory access to all CPn peripherals
*/
ble_plat_mmap_config(MMAP_SAVE_AND_CONFIG);
#if PLAT_RECOVERY_IMAGE_ENABLE
/* Check if there's a skip request to bootRom recovery Image */
if (ble_skip_current_image()) {
/* close memory access to all CPn peripherals. */
ble_plat_mmap_config(MMAP_RESTORE_SAVED);
*skip = 1;
return 0;
}
#endif
/* Do required CP-110 setups for BLE stage */
cp110_ble_init(MVEBU_CP_REGS_BASE(0));
/* Setup AVS */
ble_plat_svc_config();
/* work with PLL clock driver in AP807 */
if (ble_get_ap_type() == CHIP_ID_AP807)
aro_to_pll();
/* Do required AP setups for BLE stage */
ap_ble_init();
/* Update DRAM topology (scan DIMM SPDs) */
plat_marvell_dram_update_topology();
/* Kick it in */
ret = dram_init();
/* Restore the original CCU configuration before exit from BLE */
ble_plat_mmap_config(MMAP_RESTORE_SAVED);
return ret;
}