Add BL2 support for Broadcom stingray platform

Change-Id: I5daa3f2b4b9d85cb857547a588571a9aa8ad05c2
Signed-off-by: Sheetal Tigadoli <sheetal.tigadoli@broadcom.com>
This commit is contained in:
Sheetal Tigadoli 2019-12-18 19:44:43 +05:30
parent 9a40c0fba6
commit f29d1e0c72
39 changed files with 4810 additions and 5 deletions

232
drivers/brcm/iproc_gpio.c Normal file
View File

@ -0,0 +1,232 @@
/*
* Copyright (c) 2019-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <drivers/gpio.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>
#include <iproc_gpio.h>
#include <platform_def.h>
#define IPROC_GPIO_DATA_IN_OFFSET 0x00
#define IPROC_GPIO_DATA_OUT_OFFSET 0x04
#define IPROC_GPIO_OUT_EN_OFFSET 0x08
#define IPROC_GPIO_PAD_RES_OFFSET 0x34
#define IPROC_GPIO_RES_EN_OFFSET 0x38
#define PINMUX_OFFSET(gpio) ((gpio) * 4)
#define PINCONF_OFFSET(gpio) ((gpio) * 4)
#define PINCONF_PULL_UP BIT(4)
#define PINCONF_PULL_DOWN BIT(5)
/*
* iProc GPIO bank is always 0x200 per bank,
* with each bank supporting 32 GPIOs.
*/
#define GPIO_BANK_SIZE 0x200
#define NGPIOS_PER_BANK 32
#define GPIO_BANK(pin) ((pin) / NGPIOS_PER_BANK)
#define IPROC_GPIO_REG(pin, reg) (GPIO_BANK(pin) * GPIO_BANK_SIZE + (reg))
#define IPROC_GPIO_SHIFT(pin) ((pin) % NGPIOS_PER_BANK)
#define MUX_GPIO_MODE 0x3
/*
* @base: base address of the gpio controller
* @pinconf_base: base address of the pinconf
* @pinmux_base: base address of the mux controller
* @nr_gpios: maxinum number of GPIOs
*/
struct iproc_gpio {
uintptr_t base;
uintptr_t pinconf_base;
uintptr_t pinmux_base;
int nr_gpios;
};
static struct iproc_gpio iproc_gpio;
static void gpio_set_bit(uintptr_t base, unsigned int reg, int gpio, bool set)
{
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
uint32_t val;
val = mmio_read_32(base + offset);
if (set)
val |= BIT(shift);
else
val &= ~BIT(shift);
mmio_write_32(base + offset, val);
}
static bool gpio_get_bit(uintptr_t base, unsigned int reg, int gpio)
{
unsigned int offset = IPROC_GPIO_REG(gpio, reg);
unsigned int shift = IPROC_GPIO_SHIFT(gpio);
return !!(mmio_read_32(base + offset) & BIT(shift));
}
static void mux_to_gpio(struct iproc_gpio *g, int gpio)
{
/* mux pad to GPIO if IOPAD configuration is mandatory */
if (g->pinmux_base)
mmio_write_32(g->pinmux_base + PINMUX_OFFSET(gpio),
MUX_GPIO_MODE);
}
static void set_direction(int gpio, int direction)
{
struct iproc_gpio *g = &iproc_gpio;
bool dir = (direction == GPIO_DIR_OUT) ? true : false;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, dir);
}
static int get_direction(int gpio)
{
struct iproc_gpio *g = &iproc_gpio;
int dir;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
dir = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
GPIO_DIR_OUT : GPIO_DIR_IN;
return dir;
}
static int get_value(int gpio)
{
struct iproc_gpio *g = &iproc_gpio;
unsigned int offset;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
/*
* If GPIO is configured as output, read from the GPIO_OUT register;
* otherwise, read from the GPIO_IN register
*/
offset = gpio_get_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio) ?
IPROC_GPIO_DATA_OUT_OFFSET : IPROC_GPIO_DATA_IN_OFFSET;
return gpio_get_bit(g->base, offset, gpio);
}
static void set_value(int gpio, int val)
{
struct iproc_gpio *g = &iproc_gpio;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
/* make sure GPIO is configured to output, and then set the value */
gpio_set_bit(g->base, IPROC_GPIO_OUT_EN_OFFSET, gpio, true);
gpio_set_bit(g->base, IPROC_GPIO_DATA_OUT_OFFSET, gpio, !!(val));
}
static int get_pull(int gpio)
{
struct iproc_gpio *g = &iproc_gpio;
uint32_t val;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
/* when there's a valid pinconf_base, use it */
if (g->pinconf_base) {
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
if (val & PINCONF_PULL_UP)
return GPIO_PULL_UP;
else if (val & PINCONF_PULL_DOWN)
return GPIO_PULL_DOWN;
else
return GPIO_PULL_NONE;
}
/* no pinconf_base. fall back to GPIO internal pull control */
if (!gpio_get_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio))
return GPIO_PULL_NONE;
return gpio_get_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio) ?
GPIO_PULL_UP : GPIO_PULL_DOWN;
}
static void set_pull(int gpio, int pull)
{
struct iproc_gpio *g = &iproc_gpio;
uint32_t val;
assert(gpio < g->nr_gpios);
mux_to_gpio(g, gpio);
/* when there's a valid pinconf_base, use it */
if (g->pinconf_base) {
val = mmio_read_32(g->pinconf_base + PINCONF_OFFSET(gpio));
if (pull == GPIO_PULL_NONE) {
val &= ~(PINCONF_PULL_UP | PINCONF_PULL_DOWN);
} else if (pull == GPIO_PULL_UP) {
val |= PINCONF_PULL_UP;
val &= ~PINCONF_PULL_DOWN;
} else if (pull == GPIO_PULL_DOWN) {
val |= PINCONF_PULL_DOWN;
val &= ~PINCONF_PULL_UP;
} else {
return;
}
mmio_write_32(g->pinconf_base + PINCONF_OFFSET(gpio), val);
}
/* no pinconf_base. fall back to GPIO internal pull control */
if (pull == GPIO_PULL_NONE) {
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, false);
return;
}
/* enable pad register and pull up or down */
gpio_set_bit(g->base, IPROC_GPIO_RES_EN_OFFSET, gpio, true);
gpio_set_bit(g->base, IPROC_GPIO_PAD_RES_OFFSET, gpio,
!!(pull == GPIO_PULL_UP));
}
const gpio_ops_t iproc_gpio_ops = {
.get_direction = get_direction,
.set_direction = set_direction,
.get_value = get_value,
.set_value = set_value,
.get_pull = get_pull,
.set_pull = set_pull,
};
void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
uintptr_t pinconf_base)
{
iproc_gpio.base = base;
iproc_gpio.nr_gpios = nr_gpios;
/* pinmux/pinconf base is optional for some SoCs */
if (pinmux_base)
iproc_gpio.pinmux_base = pinmux_base;
if (pinconf_base)
iproc_gpio.pinconf_base = pinconf_base;
gpio_init(&iproc_gpio_ops);
}

204
drivers/brcm/ocotp.c Normal file
View File

@ -0,0 +1,204 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <ocotp.h>
#include <platform_def.h>
#define OTP_MAP 2
#define OTP_NUM_WORDS 2048
/*
* # of tries for OTP Status. The time to execute a command varies. The slowest
* commands are writes which also vary based on the # of bits turned on. Writing
* 0xffffffff takes ~3800 us.
*/
#define OTPC_RETRIES_US 5000
/* Sequence to enable OTP program */
#define OTPC_PROG_EN_SEQ { 0xf, 0x4, 0x8, 0xd }
/* OTPC Commands */
#define OTPC_CMD_READ 0x0
#define OTPC_CMD_OTP_PROG_ENABLE 0x2
#define OTPC_CMD_OTP_PROG_DISABLE 0x3
#define OTPC_CMD_PROGRAM 0x8
#define OTPC_CMD_ECC 0x10
#define OTPC_ECC_ADDR 0x1A
#define OTPC_ECC_VAL 0x00EC0000
/* OTPC Status Bits */
#define OTPC_STAT_CMD_DONE BIT(1)
#define OTPC_STAT_PROG_OK BIT(2)
/* OTPC register definition */
#define OTPC_MODE_REG_OFFSET 0x0
#define OTPC_MODE_REG_OTPC_MODE 0
#define OTPC_COMMAND_OFFSET 0x4
#define OTPC_COMMAND_COMMAND_WIDTH 6
#define OTPC_CMD_START_OFFSET 0x8
#define OTPC_CMD_START_START 0
#define OTPC_CPU_STATUS_OFFSET 0xc
#define OTPC_CPUADDR_REG_OFFSET 0x28
#define OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH 16
#define OTPC_CPU_WRITE_REG_OFFSET 0x2c
#define OTPC_CMD_MASK (BIT(OTPC_COMMAND_COMMAND_WIDTH) - 1)
#define OTPC_ADDR_MASK (BIT(OTPC_CPUADDR_REG_OTPC_CPU_ADDRESS_WIDTH) - 1)
#define OTPC_MODE_REG OCOTP_REGS_BASE
struct chip_otp_cfg {
uint32_t base;
uint32_t num_words;
};
struct chip_otp_cfg ocotp_cfg = {
.base = OTPC_MODE_REG,
.num_words = 2048,
};
struct otpc_priv {
uint32_t base;
struct otpc_map *map;
int size;
int state;
};
struct otpc_priv otpc_info;
static inline void set_command(uint32_t base, uint32_t command)
{
mmio_write_32(base + OTPC_COMMAND_OFFSET, command & OTPC_CMD_MASK);
}
static inline void set_cpu_address(uint32_t base, uint32_t addr)
{
mmio_write_32(base + OTPC_CPUADDR_REG_OFFSET, addr & OTPC_ADDR_MASK);
}
static inline void set_start_bit(uint32_t base)
{
mmio_write_32(base + OTPC_CMD_START_OFFSET, 1 << OTPC_CMD_START_START);
}
static inline void reset_start_bit(uint32_t base)
{
mmio_write_32(base + OTPC_CMD_START_OFFSET, 0);
}
static inline void write_cpu_data(uint32_t base, uint32_t value)
{
mmio_write_32(base + OTPC_CPU_WRITE_REG_OFFSET, value);
}
static int poll_cpu_status(uint32_t base, uint32_t value)
{
uint32_t status;
uint32_t retries;
for (retries = 0; retries < OTPC_RETRIES_US; retries++) {
status = mmio_read_32(base + OTPC_CPU_STATUS_OFFSET);
if (status & value)
break;
udelay(1);
}
if (retries == OTPC_RETRIES_US)
return -1;
return 0;
}
static int bcm_otpc_ecc(uint32_t enable)
{
struct otpc_priv *priv = &otpc_info;
int ret;
set_command(priv->base, OTPC_CMD_ECC);
set_cpu_address(priv->base, OTPC_ECC_ADDR);
if (!enable)
write_cpu_data(priv->base, OTPC_ECC_VAL);
else
write_cpu_data(priv->base, ~OTPC_ECC_VAL);
set_start_bit(priv->base);
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
if (ret) {
ERROR("otp ecc op error: 0x%x", ret);
return -1;
}
reset_start_bit(priv->base);
return 0;
}
/*
* bcm_otpc_read read otp data in the size of 8 byte rows.
* bytes has to be the multiple of 8.
* return -1 in error case, return read bytes in success.
*/
int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
uint32_t ecc_flag)
{
struct otpc_priv *priv = &otpc_info;
uint32_t *buf = val;
uint32_t bytes_read;
uint32_t address = offset / priv->map->word_size;
int i, ret;
if (!priv->state) {
ERROR("OCOTP read failed\n");
return -1;
}
bcm_otpc_ecc(ecc_flag);
for (bytes_read = 0; (bytes_read + priv->map->word_size) <= bytes;) {
set_command(priv->base, OTPC_CMD_READ);
set_cpu_address(priv->base, address++);
set_start_bit(priv->base);
ret = poll_cpu_status(priv->base, OTPC_STAT_CMD_DONE);
if (ret) {
ERROR("otp read error: 0x%x", ret);
return -1;
}
for (i = 0; i < priv->map->otpc_row_size; i++) {
*buf++ = mmio_read_32(priv->base +
priv->map->data_r_offset[i]);
bytes_read += sizeof(*buf);
}
reset_start_bit(priv->base);
}
return bytes_read;
}
int bcm_otpc_init(struct otpc_map *map)
{
struct otpc_priv *priv;
priv = &otpc_info;
priv->base = ocotp_cfg.base;
priv->map = map;
priv->size = 4 * ocotp_cfg.num_words;
/* Enable CPU access to OTPC. */
mmio_setbits_32(priv->base + OTPC_MODE_REG_OFFSET,
BIT(OTPC_MODE_REG_OTPC_MODE));
reset_start_bit(priv->base);
priv->state = 1;
VERBOSE("OTPC Initialization done\n");
return 0;
}

100
drivers/brcm/scp.c Normal file
View File

@ -0,0 +1,100 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <arch_helpers.h>
#include <common/debug.h>
/* MCU binary image structure: <header> <data>
*
* Header structure:
* <magic-start>
* <num-sections>
* {<src-offset> <src-size> <dst-addr>}*
* <magic-end>
*
* MCU data (<data>) consists of several sections of code/data, to be
* installed (copied) into MCU memories.
* Header (<header>) gives information about sections contained in <data>.
*
* The installer code iterates over sections in MCU binary.
* For each section, it copies the section into MCU memory.
*
* The header contains:
* - <magic-start> - 32-bit magic number to mark header start
* - <num-sections> - number of sections in <data>
* - <num-sections> tuples. Each tuple describes a section.
* A tuple contains three 32-bit words.
* - <magic-end> - 32-bit magic number to mark header end
*
* Each section is describes by a tuple, consisting of three 32-bit words:
* - offset of section within MCU binary (relative to beginning of <data>)
* - section size (in bytes) in MCU binary
* - target address (in MCU memory). Section is copied to this location.
*
* All fields are 32-bit unsigned integers in little endian format.
* All sizes are assumed to be 32-bit aligned.
*/
#define SCP_BIN_HEADER_MAGIC_START 0xfa587D01
#define SCP_BIN_HEADER_MAGIC_END 0xf3e06a85
int download_scp_patch(void *image, unsigned int image_size)
{
unsigned int *pheader = (unsigned int *)(image);
unsigned int header_size;
unsigned char *pdata;
void *dest;
unsigned int num_sections;
unsigned int section_src_offset;
unsigned int section_size;
if (pheader && (pheader[0] != SCP_BIN_HEADER_MAGIC_START)) {
ERROR("SCP: Could not find SCP header.\n");
return -1;
}
num_sections = pheader[1];
INFO("...Number of sections: %d\n", num_sections);
header_size = 4 * (1 + 1 + 3 * num_sections + 1);
if (image_size < header_size) {
ERROR("SCP: Wrong size.\n");
return -1;
}
if (*(pheader + header_size/4 - 1) != SCP_BIN_HEADER_MAGIC_END) {
ERROR("SCP: Could not find SCP footer.\n");
return -1;
}
VERBOSE("SCP image header validated successfully\n");
pdata = (unsigned char *)pheader + header_size;
for (pheader += 2; num_sections > 0; num_sections--) {
section_src_offset = pheader[0];
section_size = pheader[1];
dest = (void *)(unsigned long)pheader[2];
INFO("section: src:0x%x, size:%d, dst:0x%x\n",
section_src_offset, section_size, pheader[2]);
if ((section_src_offset + section_size) > image_size) {
ERROR("SCP: Section points to outside of patch.\n");
return -1;
}
/* copy from source to target section */
memcpy(dest, pdata + section_src_offset, section_size);
flush_dcache_range((uintptr_t)dest, section_size);
/* next section */
pheader += 3;
}
return 0;
}

323
drivers/brcm/sotp.c Normal file
View File

@ -0,0 +1,323 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <common/debug.h>
#include <lib/mmio.h>
#include <sotp.h>
#include <platform_def.h>
#include <platform_sotp.h>
#ifdef USE_SOFT_SOTP
extern uint64_t soft_sotp[];
#endif
#define SOTP_PROG_CONTROL (SOTP_REGS_OTP_BASE + 0x0000)
#define SOTP_PROG_CONTROL__OTP_CPU_MODE_EN 15
#define SOTP_PROG_CONTROL__OTP_DISABLE_ECC 9
#define SOTP_PROG_CONTROL__OTP_ECC_WREN 8
#define SOTP_WRDATA_0 (SOTP_REGS_OTP_BASE + 0x0004)
#define SOTP_WRDATA_1 (SOTP_REGS_OTP_BASE + 0x0008)
#define SOTP_ADDR (SOTP_REGS_OTP_BASE + 0x000c)
#define SOTP_ADDR__OTP_ROW_ADDR_R 6
#define SOTP_ADDR_MASK 0x3FF
#define SOTP_CTRL_0 (SOTP_REGS_OTP_BASE + 0x0010)
#define SOTP_CTRL_0__START 0
#define SOTP_CTRL_0__OTP_CMD 1
#define SOTP_STATUS_0 (SOTP_REGS_OTP_BASE + 0x0018)
#define SOTP_STATUS__FDONE 3
#define SOTP_STATUS_1 (SOTP_REGS_OTP_BASE + 0x001c)
#define SOTP_STATUS_1__CMD_DONE 1
#define SOTP_STATUS_1__ECC_DET 17
#define SOTP_RDDATA_0 (SOTP_REGS_OTP_BASE + 0x0020)
#define SOTP_RDDATA_1 (SOTP_REGS_OTP_BASE + 0x0024)
#define SOTP_READ 0
#define SOTP_PROG_WORD 10
#define SOTP_STATUS__PROGOK 2
#define SOTP_PROG_ENABLE 2
#define SOTP_ROW_DATA_MASK 0xffffffff
#define SOTP_ECC_ERR_BITS_MASK 0x1ff00000000
#define SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES 4
#define SOTP_CHIP_CTRL_SW_MANU_PROG 5
#define SOTP_CHIP_CTRL_SW_CID_PROG 6
#define SOTP_CHIP_CTRL_SW_AB_DEVICE 8
#define SOTP_CHIP_CTRL_SW_AB_DEV_MODE 9
#define CHIP_STATE_UNPROGRAMMED 0x1
#define CHIP_STATE_UNASSIGNED 0x2
uint64_t sotp_mem_read(uint32_t offset, uint32_t sotp_add_ecc)
{
#ifdef USE_SOFT_SOTP
(void)sotp_add_ecc;
return soft_sotp[offset];
#else
uint64_t read_data = 0;
uint64_t read_data1 = 0;
uint64_t read_data2 = 0;
/* Check for FDONE status */
while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
BIT(SOTP_STATUS__FDONE))
;
/* Enable OTP access by CPU */
mmio_setbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
if (sotp_add_ecc == 1) {
mmio_clrbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
}
if (sotp_add_ecc == 0) {
mmio_setbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_DISABLE_ECC));
}
mmio_write_32(SOTP_ADDR,
((offset & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
mmio_write_32(SOTP_CTRL_0, (SOTP_READ << SOTP_CTRL_0__OTP_CMD));
/* Start bit to tell SOTP to send command to the OTP controller */
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
/* Wait for SOTP command done to be set */
while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
BIT(SOTP_STATUS_1__CMD_DONE))
;
/* Clr Start bit after command done */
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
if ((offset > SOTP_DEVICE_SECURE_CFG3_ROW) &&
(mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__ECC_DET))) {
ERROR("SOTP ECC ERROR Detected row offset %d\n", offset);
read_data = SOTP_ECC_ERR_DETECT;
} else {
read_data1 = (uint64_t)mmio_read_32(SOTP_RDDATA_0);
read_data1 = read_data1 & 0xFFFFFFFF;
read_data2 = (uint64_t)mmio_read_32(SOTP_RDDATA_1);
read_data2 = (read_data2 & 0x1ff) << 32;
read_data = read_data1 | read_data2;
}
/* Command done is cleared */
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
/* disable OTP access by CPU */
mmio_clrbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
return read_data;
#endif
}
void sotp_mem_write(uint32_t addr, uint32_t sotp_add_ecc, uint64_t wdata)
{
#ifdef USE_SOFT_SOTP
(void)sotp_add_ecc;
soft_sotp[addr] = wdata;
#else
uint32_t loop;
uint8_t prog_array[4] = { 0x0F, 0x04, 0x08, 0x0D };
uint32_t chip_state_default =
(CHIP_STATE_UNASSIGNED|CHIP_STATE_UNPROGRAMMED);
uint32_t chip_state = mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES);
uint32_t chip_ctrl_default = 0;
/*
* The override settings is required to allow the customer to program
* the application specific keys into SOTP, before the conversion to
* one of the AB modes.
* At the end of write operation, the chip ctrl settings will restored
* to the state prior to write call
*/
if (chip_state & chip_state_default) {
uint32_t chip_ctrl;
chip_ctrl_default = mmio_read_32(SOTP_CHIP_CTRL);
INFO("SOTP: enable special prog mode\n");
chip_ctrl = BIT(SOTP_CHIP_CTRL_SW_OVERRIDE_CHIP_STATES) |
BIT(SOTP_CHIP_CTRL_SW_MANU_PROG) |
BIT(SOTP_CHIP_CTRL_SW_CID_PROG) |
BIT(SOTP_CHIP_CTRL_SW_AB_DEVICE);
mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl);
}
/* Check for FDONE status */
while ((mmio_read_32(SOTP_STATUS_0) & BIT(SOTP_STATUS__FDONE)) !=
BIT(SOTP_STATUS__FDONE))
;
/* Enable OTP acces by CPU */
mmio_setbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
if (addr > SOTP_DEVICE_SECURE_CFG3_ROW) {
if (sotp_add_ecc == 0) {
mmio_clrbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
}
if (sotp_add_ecc == 1) {
mmio_setbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
}
} else {
mmio_clrbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_ECC_WREN));
}
mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_ENABLE << 1));
/*
* In order to avoid unintentional writes / programming of the OTP
* array, the OTP Controller must be put into programming mode before
* it will accept program commands. This is done by writing 0xF, 0x4,
* 0x8, 0xD with program commands prior to starting the actual
* programming sequence
*/
for (loop = 0; loop < 4; loop++) {
mmio_write_32(SOTP_WRDATA_0, prog_array[loop]);
/*
* Start bit to tell SOTP to send command to the OTP controller
*/
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
/* Wait for SOTP command done to <-- be set */
while ((mmio_read_32(SOTP_STATUS_1) &
BIT(SOTP_STATUS_1__CMD_DONE)) !=
BIT(SOTP_STATUS_1__CMD_DONE))
;
/* Command done is cleared w1c */
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
/* Clr Start bit after command done */
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
}
/* Check for PROGOK */
while ((mmio_read_32(SOTP_STATUS_0) & 0x4) != BIT(SOTP_STATUS__PROGOK))
;
/* Set 10 bit row address */
mmio_write_32(SOTP_ADDR,
((addr & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R));
/* Set SOTP Row data */
mmio_write_32(SOTP_WRDATA_0, (wdata & SOTP_ROW_DATA_MASK));
/* Set SOTP ECC and error bits */
mmio_write_32(SOTP_WRDATA_1, ((wdata & SOTP_ECC_ERR_BITS_MASK) >> 32));
/* Set prog_word command */
mmio_write_32(SOTP_CTRL_0, (SOTP_PROG_WORD << 1));
/* Start bit to tell SOTP to send command to the OTP controller */
mmio_setbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
/* Wait for SOTP command done to be set */
while ((mmio_read_32(SOTP_STATUS_1) & BIT(SOTP_STATUS_1__CMD_DONE)) !=
BIT(SOTP_STATUS_1__CMD_DONE))
;
/* Command done is cleared w1c */
mmio_setbits_32(SOTP_STATUS_1, BIT(SOTP_STATUS_1__CMD_DONE));
/* disable OTP acces by CPU */
mmio_clrbits_32(SOTP_PROG_CONTROL,
BIT(SOTP_PROG_CONTROL__OTP_CPU_MODE_EN));
/* Clr Start bit after command done */
mmio_clrbits_32(SOTP_CTRL_0, BIT(SOTP_CTRL_0__START));
if (chip_state & chip_state_default)
mmio_write_32(SOTP_CHIP_CTRL, chip_ctrl_default);
#endif
}
int sotp_read_key(uint8_t *key, size_t keysize, int start_row, int end_row)
{
int row;
uint32_t status = 0;
uint32_t status2 = 0xFFFFFFFF;
uint64_t row_data;
uint32_t data;
uint32_t *temp_key = (uint32_t *)key;
row = start_row;
while ((keysize > 0) && (row <= end_row)) {
row_data = sotp_mem_read(row, SOTP_ROW_ECC);
if (!(row_data & (SOTP_ECC_ERR_DETECT | SOTP_FAIL_BITS))) {
memcpy(temp_key++, &row_data, sizeof(uint32_t));
keysize -= sizeof(uint32_t);
data = (uint32_t)(row_data & SOTP_ROW_DATA_MASK);
status |= data;
status2 &= data;
}
row++;
}
if ((status2 == 0xFFFFFFFF) || (status == 0) || (row > end_row))
return -1;
return 0;
}
int sotp_key_erased(void)
{
uint64_t row_data;
int status = 0;
row_data = sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
if (row_data & SOTP_DEVICE_SECURE_CFG0_OTP_ERASED_MASK)
status = 1;
else if (mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES) &
SOTP_REGS_SOTP_CHIP_STATES_OTP_ERASED_MASK)
status = 1;
return status;
}
/*
* This function optimise the SOTP redundancy
* by considering the 00- zero and 01,10,11 - one
*/
uint32_t sotp_redundancy_reduction(uint32_t sotp_row_data)
{
uint32_t opt_data;
uint32_t opt_loop;
uint32_t temp_data;
opt_data = 0;
for (opt_loop = 0; opt_loop < 16; opt_loop = opt_loop + 1) {
temp_data = ((sotp_row_data >> (opt_loop * 2)) & 0x3);
if (temp_data != 0x0)
opt_data = (opt_data | (1 << opt_loop));
}
return opt_data;
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2016 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SR_CHIMP_H
#define SR_CHIMP_H
#include <common/bl_common.h>
#include <common/debug.h>
#include <lib/mmio.h>
#include <platform_def.h>
#define CHIMP_WINDOW_SIZE 0x400000
#define CHIMP_ERROR_OFFSET 28
#define CHIMP_ERROR_MASK 0xf0000000
#ifndef EMULATION_SETUP
#define CHIMP_HANDSHAKE_TIMEOUT_MS 10000
#else
/*
* 1hr timeout for test in emulator
* By doing this ChiMP is given a chance to boot
* fully from the QSPI
* (on Palladium this takes upto 50 min depending on QSPI clk)
*/
#define CHIMP_HANDSHAKE_TIMEOUT_MS 3600000
#endif
#define CHIMP_BPE_MODE_ID_PATTERN (0x25000000)
#define CHIMP_BPE_MODE_ID_MASK (0x7f000000)
#define NIC_RESET_RELEASE_TIMEOUT_US (10)
/* written by M0, used by ChiMP ROM */
#define SR_IN_SMARTNIC_MODE_BIT 0
/* written by M0, used by ChiMP ROM */
#define SR_CHIMP_SECURE_BOOT_BIT 1
/* cleared by AP, set by ChiMP BC2 code */
#define SR_FLASH_ACCESS_DONE_BIT 2
#ifdef USE_CHIMP
void bcm_chimp_write(uintptr_t addr, uint32_t value);
uint32_t bcm_chimp_read(uintptr_t addr);
uint32_t bcm_chimp_read_ctrl(uint32_t offset);
void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits);
void bcm_chimp_setbits(uintptr_t addr, uint32_t bits);
int bcm_chimp_is_nic_mode(void);
void bcm_chimp_fru_prog_done(bool status);
int bcm_chimp_handshake_done(void);
int bcm_chimp_wait_handshake(void);
/* Fastboot-related*/
int bcm_chimp_initiate_fastboot(int fastboot_type);
#else
static inline void bcm_chimp_write(uintptr_t addr, uint32_t value)
{
}
static inline uint32_t bcm_chimp_read(uintptr_t addr)
{
return 0;
}
static inline uint32_t bcm_chimp_read_ctrl(uint32_t offset)
{
return 0;
}
static inline void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits)
{
}
static inline void bcm_chimp_setbits(uintptr_t addr, uint32_t bits)
{
}
static inline int bcm_chimp_is_nic_mode(void)
{
return 0;
}
static inline void bcm_chimp_fru_prog_done(bool status)
{
}
static inline int bcm_chimp_handshake_done(void)
{
return 0;
}
static inline int bcm_chimp_wait_handshake(void)
{
return 0;
}
static inline int bcm_chimp_initiate_fastboot(int fastboot_type)
{
return 0;
}
#endif /* USE_CHIMP */
#endif

144
include/drivers/brcm/fru.h Normal file
View File

@ -0,0 +1,144 @@
/*
* Copyright (c) 2019-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef FRU_H
#define FRU_H
#include <stdbool.h>
#include <stdint.h>
/* max string length */
#define FRU_MAX_STR_LEN 32
/* max number of DDR channels */
#define BCM_MAX_NR_DDR 3
/* max supported FRU table size */
#define BCM_MAX_FRU_LEN 512
/* FRU table starting offset */
#define BCM_FRU_TBL_OFFSET 0x300000
/* FRU time constants */
#define MINS_PER_DAY 1440
#define MINS_PER_HOUR 60
#define FRU_YEAR_START 1996
#define FRU_MONTH_START 1
#define FRU_DAY_START 1
#define MONTHS_PER_YEAR 12
/*
* FRU areas based on the spec
*/
enum fru_area_name {
FRU_AREA_INTERNAL = 0,
FRU_AREA_CHASSIS_INFO,
FRU_AREA_BOARD_INFO,
FRU_AREA_PRODUCT_INFO,
FRU_AREA_MRECORD_INFO,
FRU_MAX_NR_AREAS
};
/*
* FRU area information
*
* @use: indicate this area is being used
* @version: format version
* @offset: offset of this area from the beginning of the FRU table
* @len: total length of the area
*/
struct fru_area_info {
bool use;
uint8_t version;
unsigned int offset;
unsigned int len;
};
/*
* DDR MCB information
*
* @idx: DDR channel index
* @size_mb: DDR size of this channel in MB
* @ref_id: DDR MCB reference ID
*/
struct ddr_mcb {
unsigned int idx;
unsigned int size_mb;
uint32_t ref_id;
};
/*
* DDR information
*
* @ddr_info: array that contains MCB related info for each channel
*/
struct ddr_info {
struct ddr_mcb mcb[BCM_MAX_NR_DDR];
};
/*
* FRU board area information
*
* @lang: Language code
* @mfg_date: Manufacturing date
* @manufacturer: Manufacturer
* @product_name: Product name
* @serial_number: Serial number
* @part_number: Part number
* @file_id: FRU file ID
*/
struct fru_board_info {
unsigned char lang;
unsigned int mfg_date;
unsigned char manufacturer[FRU_MAX_STR_LEN];
unsigned char product_name[FRU_MAX_STR_LEN];
unsigned char serial_number[FRU_MAX_STR_LEN];
unsigned char part_number[FRU_MAX_STR_LEN];
unsigned char file_id[FRU_MAX_STR_LEN];
};
/*
* FRU manufacture date in human readable format
*/
struct fru_time {
unsigned int min;
unsigned int hour;
unsigned int day;
unsigned int month;
unsigned int year;
};
#ifdef USE_FRU
int fru_validate(uint8_t *data, struct fru_area_info *fru_area);
int fru_parse_ddr(uint8_t *data, struct fru_area_info *area,
struct ddr_info *ddr);
int fru_parse_board(uint8_t *data, struct fru_area_info *area,
struct fru_board_info *board);
void fru_format_time(unsigned int min, struct fru_time *tm);
#else
static inline int fru_validate(uint8_t *data, struct fru_area_info *fru_area)
{
return -1;
}
static inline int fru_parse_ddr(uint8_t *data, struct fru_area_info *area,
struct ddr_info *ddr)
{
return -1;
}
static inline int fru_parse_board(uint8_t *data, struct fru_area_info *area,
struct fru_board_info *board)
{
return -1;
}
static inline void fru_format_time(unsigned int min, struct fru_time *tm)
{
}
#endif /* USE_FRU */
#endif /* FRU_H */

View File

@ -0,0 +1,20 @@
/*
* Copyright (c) 2019-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef IPROC_GPIO_H
#define IPROC_GPIO_H
#ifdef USE_GPIO
void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
uintptr_t pinconf_base);
#else
static void iproc_gpio_init(uintptr_t base, int nr_gpios, uintptr_t pinmux_base,
uintptr_t pinconf_base)
{
}
#endif /* IPROC_GPIO */
#endif /* IPROC_GPIO_H */

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2016 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef OCOTP_H
#define OCOTP_H
#include <stdint.h>
struct otpc_map {
/* in words. */
uint32_t otpc_row_size;
/* 128 bit row / 4 words support. */
uint16_t data_r_offset[4];
/* 128 bit row / 4 words support. */
uint16_t data_w_offset[4];
int word_size;
int stride;
};
int bcm_otpc_init(struct otpc_map *map);
int bcm_otpc_read(unsigned int offset, void *val, uint32_t bytes,
uint32_t ecc_flag);
#endif /* OCOTP_H */

View File

@ -0,0 +1,14 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SCP_H_
#define SCP_H
#include <stdint.h>
int download_scp_patch(void *image, unsigned int image_size);
#endif /* SCP_H */

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SOTP_H
#define SOTP_H
#include <stddef.h>
#include <stdint.h>
#include <platform_sotp.h>
#define SOTP_ROW_NO_ECC 0
#define SOTP_ROW_ECC 1
#define SOTP_STATUS_1 (SOTP_REGS_OTP_BASE + 0x001c)
#define SOTP_FAIL_BITS 0x18000000000
#define SOTP_ECC_ERR_DETECT 0x8000000000000000
#define SOTP_REGS_SOTP_CHIP_STATES (SOTP_REGS_OTP_BASE + 0x0028)
#define SOTP_REGS_OTP_WR_LOCK (SOTP_REGS_OTP_BASE + 0x0038)
#define SOTP_CHIP_STATES_MANU_DEBUG_MASK (1 << 8)
#define SOTP_DEVICE_SECURE_CFG0_OTP_ERASED_MASK (3 << 16)
#define SOTP_REGS_SOTP_CHIP_STATES_OTP_ERASED_MASK (1 << 16)
#define SOTP_DEVICE_SECURE_CFG0_CID_MASK (3 << 2)
#define SOTP_DEVICE_SECURE_CFG0_AB_MASK (3 << 6)
#define SOTP_DEVICE_SECURE_CFG0_DEV_MASK (3 << 8)
#define SOTP_BOOT_SOURCE_SHIFT 8
/* bits 14 and 15 */
#define SOTP_BOOT_SOURCE_ENABLE_MASK (0xC0 << SOTP_BOOT_SOURCE_SHIFT)
/* bits 8 to 13 */
#define SOTP_BOOT_SOURCE_BITS0 (0x03 << SOTP_BOOT_SOURCE_SHIFT)
#define SOTP_BOOT_SOURCE_BITS1 (0x0C << SOTP_BOOT_SOURCE_SHIFT)
#define SOTP_BOOT_SOURCE_BITS2 (0x30 << SOTP_BOOT_SOURCE_SHIFT)
#define SOTP_BOOT_SOURCE_MASK (0x3F << SOTP_BOOT_SOURCE_SHIFT)
#define SOTP_ATF_CFG_ROW_ID SOTP_DEVICE_SECURE_CFG2_ROW
/* bits 28 and 29 */
#define SOTP_SBL_MASK (3 << 28)
/* bits 30 and 31 */
#define SOTP_ATF_NVCOUNTER_ENABLE_MASK ((uint64_t)3 << 30)
/* bits 32 and 33 */
#define SOTP_ATF_WATCHDOG_ENABLE_MASK ((uint64_t)3 << 32)
/* bits 34 and 35 */
#define SOTP_ATF_PLL_ON ((uint64_t)3 << 34)
/* bits 36 and 37 */
#define SOTP_ATF_RESET_RETRY ((uint64_t)3 << 36)
/* bits 38 to 40 */
#define SOTP_ATF_LOG_LEVEL_SHIFT 38
#define SOTP_ATF_LOG_LEVEL ((uint64_t)7 << SOTP_ATF_LOG_LEVEL_SHIFT)
#define SOTP_ATF2_CFG_ROW_ID SOTP_DEVICE_SECURE_CFG3_ROW
/* bits 16 and 17 */
#define SOTP_ROMKEY_MASK (3 << 16)
/* bits 18 and 19 */
#define SOTP_EC_EN_MASK (3 << 18)
#define SOTP_ENC_DEV_TYPE_AB_DEV ((uint64_t)0x19999800000)
#define SOTP_ENC_DEV_TYPE_MASK ((uint64_t)0x1ffff800000)
uint64_t sotp_mem_read(uint32_t offset, uint32_t sotp_add_ecc);
void sotp_mem_write(uint32_t addr, uint32_t sotp_add_ecc, uint64_t wdata);
int sotp_read_key(uint8_t *key, size_t keysize, int start_row, int end_row);
int sotp_key_erased(void);
uint32_t sotp_redundancy_reduction(uint32_t sotp_row_data);
#endif

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BCM_ELOG_H
#define BCM_ELOG_H
#ifndef __ASSEMBLER__
#include <stdint.h>
#if defined(BCM_ELOG) && (defined(IMAGE_BL2) || defined(IMAGE_BL31))
int bcm_elog_init(void *base, uint32_t size, unsigned int level);
void bcm_elog_exit(void);
int bcm_elog_copy_log(void *dst, uint32_t max_size);
void bcm_elog(const char *fmt, ...);
#else
static inline int bcm_elog_init(void *base, uint32_t size,
unsigned int level)
{
return 0;
}
static inline void bcm_elog_exit(void)
{
}
static inline int bcm_elog_copy_log(void *dst, uint32_t max_size)
{
return 0;
}
static inline void bcm_elog(const char *fmt, ...)
{
}
#endif /* BCM_ELOG */
#endif /* __ASSEMBLER__ */
#endif /* BCM_ELOG_H */

View File

@ -0,0 +1,268 @@
/*
* Copyright (c) 2018 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <plat/common/platform.h>
#include <bcm_elog.h>
/* error logging signature */
#define BCM_ELOG_SIG_OFFSET 0x0000
#define BCM_ELOG_SIG_VAL 0x75767971
/* current logging offset that points to where new logs should be added */
#define BCM_ELOG_OFF_OFFSET 0x0004
/* current logging length (excluding header) */
#define BCM_ELOG_LEN_OFFSET 0x0008
#define BCM_ELOG_HEADER_LEN 12
/*
* @base: base address of memory where log is saved
* @max_size: max size of memory reserved for logging
* @is_active: indicates logging is currently active
* @level: current logging level
*/
struct bcm_elog {
uintptr_t base;
uint32_t max_size;
unsigned int is_active;
unsigned int level;
};
static struct bcm_elog global_elog;
extern void memcpy16(void *dst, const void *src, unsigned int len);
/*
* Log one character
*/
static void elog_putchar(struct bcm_elog *elog, unsigned char c)
{
uint32_t offset, len;
offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
mmio_write_8(elog->base + offset, c);
offset++;
/* log buffer is now full and need to wrap around */
if (offset >= elog->max_size)
offset = BCM_ELOG_HEADER_LEN;
/* only increment length when log buffer is not full */
if (len < elog->max_size - BCM_ELOG_HEADER_LEN)
len++;
mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
}
static void elog_unsigned_num(struct bcm_elog *elog, unsigned long unum,
unsigned int radix)
{
/* Just need enough space to store 64 bit decimal integer */
unsigned char num_buf[20];
int i = 0, rem;
do {
rem = unum % radix;
if (rem < 0xa)
num_buf[i++] = '0' + rem;
else
num_buf[i++] = 'a' + (rem - 0xa);
} while (unum /= radix);
while (--i >= 0)
elog_putchar(elog, num_buf[i]);
}
static void elog_string(struct bcm_elog *elog, const char *str)
{
while (*str)
elog_putchar(elog, *str++);
}
/*
* Routine to initialize error logging
*/
int bcm_elog_init(void *base, uint32_t size, unsigned int level)
{
struct bcm_elog *elog = &global_elog;
uint32_t val;
elog->base = (uintptr_t)base;
elog->max_size = size;
elog->is_active = 1;
elog->level = level / 10;
/*
* If a valid signature can be found, it means logs have been copied
* into designated memory by another software. In this case, we should
* not re-initialize the entry header in the designated memory
*/
val = mmio_read_32(elog->base + BCM_ELOG_SIG_OFFSET);
if (val != BCM_ELOG_SIG_VAL) {
mmio_write_32(elog->base + BCM_ELOG_SIG_OFFSET,
BCM_ELOG_SIG_VAL);
mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET,
BCM_ELOG_HEADER_LEN);
mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, 0);
}
return 0;
}
/*
* Routine to disable error logging
*/
void bcm_elog_exit(void)
{
struct bcm_elog *elog = &global_elog;
if (!elog->is_active)
return;
elog->is_active = 0;
flush_dcache_range(elog->base, elog->max_size);
}
/*
* Routine to copy error logs from current memory to 'dst' memory and continue
* logging from the new 'dst' memory.
* dst and base addresses must be 16-bytes aligned.
*/
int bcm_elog_copy_log(void *dst, uint32_t max_size)
{
struct bcm_elog *elog = &global_elog;
uint32_t offset, len;
if (!elog->is_active || ((uintptr_t)dst == elog->base))
return -1;
/* flush cache before copying logs */
flush_dcache_range(elog->base, max_size);
/*
* If current offset exceeds the new max size, then that is considered
* as a buffer overflow situation. In this case, we reset the offset
* back to the beginning
*/
offset = mmio_read_32(elog->base + BCM_ELOG_OFF_OFFSET);
if (offset >= max_size) {
offset = BCM_ELOG_HEADER_LEN;
mmio_write_32(elog->base + BCM_ELOG_OFF_OFFSET, offset);
}
/* note payload length does not include header */
len = mmio_read_32(elog->base + BCM_ELOG_LEN_OFFSET);
if (len > max_size - BCM_ELOG_HEADER_LEN) {
len = max_size - BCM_ELOG_HEADER_LEN;
mmio_write_32(elog->base + BCM_ELOG_LEN_OFFSET, len);
}
/* Need to copy everything including the header. */
memcpy16(dst, (const void *)elog->base, len + BCM_ELOG_HEADER_LEN);
elog->base = (uintptr_t)dst;
elog->max_size = max_size;
return 0;
}
/*
* Main routine to save logs into memory
*/
void bcm_elog(const char *fmt, ...)
{
va_list args;
const char *prefix_str;
int bit64;
int64_t num;
uint64_t unum;
char *str;
struct bcm_elog *elog = &global_elog;
/* We expect the LOG_MARKER_* macro as the first character */
unsigned int level = fmt[0];
if (!elog->is_active || level > elog->level)
return;
prefix_str = plat_log_get_prefix(level);
while (*prefix_str != '\0') {
elog_putchar(elog, *prefix_str);
prefix_str++;
}
va_start(args, fmt);
fmt++;
while (*fmt) {
bit64 = 0;
if (*fmt == '%') {
fmt++;
/* Check the format specifier */
loop:
switch (*fmt) {
case 'i': /* Fall through to next one */
case 'd':
if (bit64)
num = va_arg(args, int64_t);
else
num = va_arg(args, int32_t);
if (num < 0) {
elog_putchar(elog, '-');
unum = (unsigned long)-num;
} else
unum = (unsigned long)num;
elog_unsigned_num(elog, unum, 10);
break;
case 's':
str = va_arg(args, char *);
elog_string(elog, str);
break;
case 'x':
if (bit64)
unum = va_arg(args, uint64_t);
else
unum = va_arg(args, uint32_t);
elog_unsigned_num(elog, unum, 16);
break;
case 'l':
bit64 = 1;
fmt++;
goto loop;
case 'u':
if (bit64)
unum = va_arg(args, uint64_t);
else
unum = va_arg(args, uint32_t);
elog_unsigned_num(elog, unum, 10);
break;
default:
/* Exit on any other format specifier */
goto exit;
}
fmt++;
continue;
}
elog_putchar(elog, *fmt++);
}
exit:
va_end(args);
}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2019-2020 Broadcom.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <common/debug.h>
#include <ddr_init.h>
#include <scp_cmd.h>
#include <scp_utils.h>
#include <platform_def.h>
#include "bcm_elog_ddr.h"
#include "m0_cfg.h"
#include "m0_ipc.h"
void elog_init_ddr_log(void)
{
struct elog_setup setup = {0};
struct elog_global_header global;
struct elog_meta_record rec;
unsigned int rec_idx = 0;
uint32_t log_offset;
uintptr_t metadata;
char *rec_desc[ELOG_SUPPORTED_REC_CNT] = {"SYSRESET", "THERMAL",
"DDR_ECC", "APBOOTLG",
"IDM"};
/*
* If this is warm boot, return immediately.
* We expect metadata to be initialized already
*/
if (is_warmboot()) {
WARN("Warmboot detected, skip ELOG metadata initialization\n");
return;
}
memset(&global, 0, sizeof(global));
global.sector_size = ELOG_SECTOR_SIZE;
global.signature = ELOG_GLOBAL_META_HDR_SIG;
global.rec_count = ELOG_SUPPORTED_REC_CNT;
/* Start of logging area in DDR memory */
log_offset = ELOG_STORE_OFFSET;
/* Shift to the first RECORD header */
log_offset += 2 * global.sector_size;
/* Temporary place to hold metadata */
metadata = TMP_ELOG_METADATA_BASE;
memcpy((void *)metadata, &global, sizeof(global));
metadata += sizeof(global);
while (rec_idx < global.rec_count) {
memset(&rec, 0, sizeof(rec));
rec.type = rec_idx;
if (rec_idx == ELOG_REC_UART_LOG) {
rec.format = ELOG_REC_FMT_ASCII;
rec.src_mem_type = ELOG_SRC_MEM_TYPE_DDR;
rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_FS4_SCRATCH;
rec.src_mem_addr = BCM_ELOG_BL31_BASE;
rec.alt_src_mem_addr = BCM_ELOG_BL2_BASE;
rec.rec_size = ELOG_APBOOTLG_REC_SIZE;
} else if (rec_idx == ELOG_REC_IDM_LOG) {
rec.type = IDM_ELOG_REC_TYPE;
rec.format = ELOG_REC_FMT_CUSTOM;
rec.src_mem_type = ELOG_SRC_MEM_TYPE_DDR;
rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
rec.src_mem_addr = ELOG_IDM_SRC_MEM_ADDR;
rec.alt_src_mem_addr = 0x0;
rec.rec_size = ELOG_DEFAULT_REC_SIZE;
} else {
rec.format = ELOG_REC_FMT_CUSTOM;
rec.src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
rec.alt_src_mem_type = ELOG_SRC_MEM_TYPE_CRMU_SCRATCH;
rec.src_mem_addr = ELOG_USE_DEFAULT_MEM_ADDR;
rec.alt_src_mem_addr = ELOG_USE_DEFAULT_MEM_ADDR;
rec.rec_size = ELOG_DEFAULT_REC_SIZE;
}
rec.nvm_type = LOG_MEDIA_DDR;
rec.sector_size = ELOG_SECTOR_SIZE;
rec.rec_addr = (uint64_t)log_offset;
log_offset += rec.rec_size;
/* Sanity checks */
if (rec.type > ELOG_MAX_REC_COUNT ||
rec.format > ELOG_MAX_REC_FORMAT ||
(rec.nvm_type > ELOG_MAX_NVM_TYPE &&
rec.nvm_type != ELOG_NVM_DEFAULT) ||
!rec.rec_size ||
!rec.sector_size ||
rec_idx >= ELOG_SUPPORTED_REC_CNT) {
ERROR("Invalid ELOG record(%u) detected\n", rec_idx);
return;
}
memset(rec.rec_desc, ' ', sizeof(rec.rec_desc));
memcpy(rec.rec_desc, rec_desc[rec_idx],
strlen(rec_desc[rec_idx]));
memcpy((void *)metadata, &rec, sizeof(rec));
metadata += sizeof(rec);
rec_idx++;
}
setup.params[0] = TMP_ELOG_METADATA_BASE;
setup.params[1] = (sizeof(global) + global.rec_count * sizeof(rec));
setup.cmd = ELOG_SETUP_CMD_WRITE_META;
flush_dcache_range((uintptr_t)&setup, sizeof(struct elog_setup));
flush_dcache_range((uintptr_t)setup.params[0], setup.params[1]);
/* initialize DDR Logging METADATA if this is NOT warmboot */
if (!is_warmboot()) {
if (scp_send_cmd(MCU_IPC_MCU_CMD_ELOG_SETUP,
(uint32_t)(uintptr_t)(&setup),
SCP_CMD_DEFAULT_TIMEOUT_US)) {
ERROR("scp_send_cmd: timeout/error for elog setup\n");
return;
}
}
NOTICE("MCU Error logging initialized\n");
}

View File

@ -0,0 +1,107 @@
/*
* Copyright 2019-2020 Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BCM_ELOG_DDR_H
#define BCM_ELOG_DDR_H
#define ELOG_GLOBAL_META_HDR_SIG 0x45524c47
#define ELOG_MAX_REC_COUNT 13
#define ELOG_MAX_REC_FORMAT 1
#define ELOG_MAX_NVM_TYPE 4
/* Use a default NVM, set by m0 configuration */
#define ELOG_NVM_DEFAULT 0xff
/* Max. number of cmd parameters per elog spec */
#define ELOG_PARAM_COUNT 3
/*
* Number of supported RECORD Types-
* SYSRESET, THERMAL, DDR_ECC, APBOOTLG, IDM
*/
#define ELOG_SUPPORTED_REC_CNT 5
#define ELOG_REC_DESC_LENGTH 8
#define ELOG_SECTOR_SIZE 0x1000
/* Default Record size for all record types except APBOOTLOG */
#define ELOG_DEFAULT_REC_SIZE 0x10000
/* Default record size for APBOOTLOG record */
#define ELOG_APBOOTLG_REC_SIZE 0x60000
/* Use default CRMU provided mem address */
#define ELOG_USE_DEFAULT_MEM_ADDR 0x0
/* Temporary place to hold metadata */
#define TMP_ELOG_METADATA_BASE (ELOG_AP_UART_LOG_BASE + \
BCM_ELOG_BL2_SIZE)
/* IDM ELOG source memory address */
#define ELOG_IDM_SRC_MEM_ADDR 0x8f213000
#define IDM_ELOG_REC_TYPE 5
enum elog_record_type {
ELOG_REC_SYS_RESET_EVT = 0,
ELOG_REC_THERMAL_EVT,
ELOG_REC_DDR_ECC,
ELOG_REC_UART_LOG,
ELOG_REC_IDM_LOG,
ELOG_REC_MAX
};
enum elog_record_format {
ELOG_REC_FMT_ASCII = 0,
ELOG_REC_FMT_CUSTOM
};
enum elog_src_memory_type {
ELOG_SRC_MEM_TYPE_CRMU_SCRATCH = 0,
ELOG_SRC_MEM_TYPE_FS4_SCRATCH,
ELOG_SRC_MEM_TYPE_DDR,
ELOG_SRC_MEM_TYPE_CHIMP_SCRATCH
};
enum elog_setup_cmd {
ELOG_SETUP_CMD_VALIDATE_META,
ELOG_SETUP_CMD_WRITE_META,
ELOG_SETUP_CMD_ERASE,
ELOG_SETUP_CMD_READ,
ELOG_SETUP_CMD_CHECK
};
struct elog_setup {
uint32_t cmd;
uint32_t params[ELOG_PARAM_COUNT];
uint32_t result;
uint32_t ret_code;
};
struct elog_meta_record {
uint8_t type;
uint8_t format;
uint8_t src_mem_type;
uint8_t alt_src_mem_type;
uint8_t nvm_type;
char rec_desc[ELOG_REC_DESC_LENGTH];
uint64_t src_mem_addr;
uint64_t alt_src_mem_addr;
uint64_t rec_addr;
uint32_t rec_size;
uint32_t sector_size;
uint8_t padding[3];
} __packed;
struct elog_global_header {
uint32_t signature;
uint32_t sector_size;
uint8_t revision;
uint8_t rec_count;
uint16_t padding;
} __packed;
void elog_init_ddr_log(void);
#endif /* BCM_ELOG_DDR_H */

View File

@ -0,0 +1,624 @@
/*
* Copyright 2015 - 2020 Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <common/debug.h>
#include <lib/mmio.h>
#include <plat/common/platform.h>
#include <tools_share/tbbr_oid.h>
#include <sbl_util.h>
#include <sotp.h>
/* Weak definition may be overridden in specific platform */
#pragma weak plat_match_rotpk
#pragma weak plat_get_nv_ctr
#pragma weak plat_set_nv_ctr
/* SHA256 algorithm */
#define SHA256_BYTES 32
/* ROTPK locations */
#define ARM_ROTPK_REGS_ID 1
#define ARM_ROTPK_DEVEL_RSA_ID 2
#define BRCM_ROTPK_SOTP_RSA_ID 3
#if !ARM_ROTPK_LOCATION_ID
#error "ARM_ROTPK_LOCATION_ID not defined"
#endif
static const unsigned char rotpk_hash_hdr[] =
"\x30\x31\x30\x0D\x06\x09\x60\x86\x48"
"\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20";
static const unsigned int rotpk_hash_hdr_len = sizeof(rotpk_hash_hdr) - 1;
static unsigned char rotpk_hash_der[sizeof(rotpk_hash_hdr) - 1 + SHA256_BYTES];
#if (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_DEVEL_RSA_ID)
static const unsigned char arm_devel_rotpk_hash[] =
"\xB0\xF3\x82\x09\x12\x97\xD8\x3A"
"\x37\x7A\x72\x47\x1B\xEC\x32\x73"
"\xE9\x92\x32\xE2\x49\x59\xF6\x5E"
"\x8B\x4A\x4A\x46\xD8\x22\x9A\xDA";
#endif
#pragma weak plat_rotpk_hash
const unsigned char plat_rotpk_hash[] =
"\xdb\x06\x67\x95\x4f\x88\x2b\x88"
"\x49\xbf\x70\x3f\xde\x50\x4a\x96"
"\xd8\x17\x69\xd4\xa0\x6c\xba\xee"
"\x66\x3e\x71\x82\x2d\x95\x69\xe4";
#pragma weak rom_slice
const unsigned char rom_slice[] =
"\x77\x06\xbc\x98\x40\xbe\xfd\xab"
"\x60\x4b\x74\x3c\x9a\xb3\x80\x75"
"\x39\xb6\xda\x27\x07\x2e\x5b\xbf"
"\x5c\x47\x91\xc9\x95\x26\x26\x0c";
#if (ARM_ROTPK_LOCATION_ID == BRCM_ROTPK_SOTP_RSA_ID)
static int plat_is_trusted_boot(void)
{
uint64_t section3_row0_data;
section3_row0_data = sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
if ((section3_row0_data & SOTP_DEVICE_SECURE_CFG0_AB_MASK) == 0) {
INFO("NOT AB\n");
return 0;
}
INFO("AB\n");
return TRUSTED_BOARD_BOOT;
}
/*
* FAST AUTH is enabled if all following conditions are met:
* - AB part
* - SOTP.DEV != 0
* - SOTP.CID != 0
* - SOTP.ENC_DEV_TYPE = ENC_AB_DEV
* - Manuf_debug strap set high
*/
static int plat_fast_auth_enabled(void)
{
uint32_t chip_state;
uint64_t section3_row0_data;
uint64_t section3_row1_data;
section3_row0_data =
sotp_mem_read(SOTP_DEVICE_SECURE_CFG0_ROW, 0);
section3_row1_data =
sotp_mem_read(SOTP_DEVICE_SECURE_CFG1_ROW, 0);
chip_state = mmio_read_32(SOTP_REGS_SOTP_CHIP_STATES);
if (plat_is_trusted_boot() &&
(section3_row0_data & SOTP_DEVICE_SECURE_CFG0_DEV_MASK) &&
(section3_row0_data & SOTP_DEVICE_SECURE_CFG0_CID_MASK) &&
((section3_row1_data & SOTP_ENC_DEV_TYPE_MASK) ==
SOTP_ENC_DEV_TYPE_AB_DEV) &&
(chip_state & SOTP_CHIP_STATES_MANU_DEBUG_MASK))
return 1;
return 0;
}
#endif
/*
* Return the ROTPK hash in the following ASN.1 structure in DER format:
*
* AlgorithmIdentifier ::= SEQUENCE {
* algorithm OBJECT IDENTIFIER,
* parameters ANY DEFINED BY algorithm OPTIONAL
* }
*
* DigestInfo ::= SEQUENCE {
* digestAlgorithm AlgorithmIdentifier,
* digest OCTET STRING
* }
*/
int plat_get_rotpk_info(void *cookie, void **key_ptr, unsigned int *key_len,
unsigned int *flags)
{
uint8_t *dst;
assert(key_ptr != NULL);
assert(key_len != NULL);
assert(flags != NULL);
*flags = 0;
/* Copy the DER header */
memcpy(rotpk_hash_der, rotpk_hash_hdr, rotpk_hash_hdr_len);
dst = (uint8_t *)&rotpk_hash_der[rotpk_hash_hdr_len];
#if (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_DEVEL_RSA_ID)
memcpy(dst, arm_devel_rotpk_hash, SHA256_BYTES);
#elif (ARM_ROTPK_LOCATION_ID == ARM_ROTPK_REGS_ID)
uint32_t *src, tmp;
unsigned int words, i;
/*
* Append the hash from Trusted Root-Key Storage registers. The hash has
* not been written linearly into the registers, so we have to do a bit
* of byte swapping:
*
* 0x00 0x04 0x08 0x0C 0x10 0x14 0x18 0x1C
* +---------------------------------------------------------------+
* | Reg0 | Reg1 | Reg2 | Reg3 | Reg4 | Reg5 | Reg6 | Reg7 |
* +---------------------------------------------------------------+
* | ... ... | | ... ... |
* | +--------------------+ | +-------+
* | | | |
* +----------------------------+ +----------------------------+
* | | | |
* +-------+ | +--------------------+ |
* | | | |
* v v v v
* +---------------------------------------------------------------+
* | | |
* +---------------------------------------------------------------+
* 0 15 16 31
*
* Additionally, we have to access the registers in 32-bit words
*/
words = SHA256_BYTES >> 3;
/* Swap bytes 0-15 (first four registers) */
src = (uint32_t *)TZ_PUB_KEY_HASH_BASE;
for (i = 0 ; i < words ; i++) {
tmp = src[words - 1 - i];
/* Words are read in little endian */
*dst++ = (uint8_t)((tmp >> 24) & 0xFF);
*dst++ = (uint8_t)((tmp >> 16) & 0xFF);
*dst++ = (uint8_t)((tmp >> 8) & 0xFF);
*dst++ = (uint8_t)(tmp & 0xFF);
}
/* Swap bytes 16-31 (last four registers) */
src = (uint32_t *)(TZ_PUB_KEY_HASH_BASE + SHA256_BYTES / 2);
for (i = 0 ; i < words ; i++) {
tmp = src[words - 1 - i];
*dst++ = (uint8_t)((tmp >> 24) & 0xFF);
*dst++ = (uint8_t)((tmp >> 16) & 0xFF);
*dst++ = (uint8_t)((tmp >> 8) & 0xFF);
*dst++ = (uint8_t)(tmp & 0xFF);
}
#elif (ARM_ROTPK_LOCATION_ID == BRCM_ROTPK_SOTP_RSA_ID)
{
int i;
int ret = -1;
/*
* In non-AB mode, we do not read the key.
* In AB mode:
* - The Dauth is in BL11 if SBL is enabled
* - The Dauth is in SOTP if SBL is disabled.
*/
if (plat_is_trusted_boot() == 0) {
INFO("NON-AB: Do not read DAUTH!\n");
*flags = ROTPK_NOT_DEPLOYED;
ret = 0;
} else if ((sbl_status() == SBL_ENABLED) &&
(mmio_read_32(BL11_DAUTH_BASE) == BL11_DAUTH_ID)) {
/* Read hash from BL11 */
INFO("readKeys (DAUTH) from BL11\n");
memcpy(dst,
(void *)(BL11_DAUTH_BASE + sizeof(uint32_t)),
SHA256_BYTES);
for (i = 0; i < SHA256_BYTES; i++)
if (dst[i] != 0)
break;
if (i >= SHA256_BYTES)
ERROR("Hash not valid from BL11\n");
else
ret = 0;
} else if (sotp_key_erased()) {
memcpy(dst, plat_rotpk_hash, SHA256_BYTES);
INFO("SOTP erased, Use internal key hash.\n");
ret = 0;
} else if (plat_fast_auth_enabled()) {
INFO("AB DEV: FAST AUTH!\n");
*flags = ROTPK_NOT_DEPLOYED;
ret = 0;
} else if (!(mmio_read_32(SOTP_STATUS_1) & SOTP_DAUTH_ECC_ERROR_MASK)) {
/* Read hash from SOTP */
ret = sotp_read_key(dst,
SHA256_BYTES,
SOTP_DAUTH_ROW,
SOTP_K_HMAC_ROW-1);
INFO("sotp_read_key (DAUTH): %i\n", ret);
} else {
uint64_t row_data;
uint32_t k;
for (k = 0; k < (SOTP_K_HMAC_ROW - SOTP_DAUTH_ROW); k++) {
row_data = sotp_mem_read(SOTP_DAUTH_ROW + k,
SOTP_ROW_NO_ECC);
if (row_data != 0)
break;
}
if (k == (SOTP_K_HMAC_ROW - SOTP_DAUTH_ROW)) {
INFO("SOTP NOT PROGRAMMED: Do not use DAUTH!\n");
if (sotp_mem_read(SOTP_ATF2_CFG_ROW_ID,
SOTP_ROW_NO_ECC) & SOTP_ROMKEY_MASK) {
memcpy(dst, plat_rotpk_hash, SHA256_BYTES);
INFO("Use internal key hash.\n");
ret = 0;
} else {
*flags = ROTPK_NOT_DEPLOYED;
ret = 0;
}
} else {
INFO("No hash found in SOTP\n");
}
}
if (ret)
return ret;
}
#endif
*key_ptr = (void *)rotpk_hash_der;
*key_len = (unsigned int)sizeof(rotpk_hash_der);
*flags |= ROTPK_IS_HASH;
return 0;
}
#define SOTP_NUM_BITS_PER_ROW 41
#define SOTP_NVCTR_ROW_ALL_ONES 0x1ffffffffff
#define SOTP_NVCTR_TRUSTED_IN_USE \
((uint64_t)0x3 << (SOTP_NUM_BITS_PER_ROW-2))
#define SOTP_NVCTR_NON_TRUSTED_IN_USE ((uint64_t)0x3)
#define SOTP_NVCTR_TRUSTED_NEAR_END SOTP_NVCTR_NON_TRUSTED_IN_USE
#define SOTP_NVCTR_NON_TRUSTED_NEAR_END SOTP_NVCTR_TRUSTED_IN_USE
#define SOTP_NVCTR_ROW_START 64
#define SOTP_NVCTR_ROW_END 75
/*
* SOTP NVCTR are stored in section 10 of SOTP (rows 64-75).
* Each row of SOTP is 41 bits.
* NVCTR's are stored in a bitstream format.
* We are tolerant to consecutive bit errors.
* Trusted NVCTR starts at the top of row 64 in bitstream format.
* Non Trusted NVCTR starts at the bottom of row 75 in reverse bitstream.
* Each row can only be used by 1 of the 2 counters. This is determined
* by 2 zeros remaining at the beginning or end of the last available row.
* If one counter has already starting using a row, the other will be
* prevent from writing to that row.
*
* Example counter values for SOTP programmed below:
* Trusted Counter (rows64-69) = 5 * 41 + 40 = 245
* NonTrusted Counter (row75-71) = 3 * 41 + 4 = 127
* 40 39 38 37 36 ..... 5 4 3 2 1 0
* row 64 1 1 1 1 1 1 1 1 1 1 1
* row 65 1 1 1 1 1 1 1 1 1 1 1
* row 66 1 1 1 1 1 1 1 1 1 1 1
* row 67 1 1 1 1 1 1 1 1 1 1 1
* row 68 1 1 1 1 1 1 1 1 1 1 1
* row 69 1 1 1 1 1 1 1 1 1 1 0
* row 71 0 0 0 0 0 0 0 0 0 0 0
* row 71 0 0 0 0 0 0 0 0 0 0 0
* row 71 0 0 0 0 0 0 0 1 1 1 1
* row 73 1 1 1 1 1 1 1 1 1 1 1
* row 74 1 1 1 1 1 1 1 1 1 1 1
* row 75 1 1 1 1 1 1 1 1 1 1 1
*
*/
#if (DEBUG == 1)
/*
* Dump sotp rows
*/
void sotp_dump_rows(uint32_t start_row, uint32_t end_row)
{
int32_t rownum;
uint64_t rowdata;
for (rownum = start_row; rownum <= end_row; rownum++) {
rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
INFO("%d 0x%llx\n", rownum, rowdata);
}
}
#endif
/*
* Get SOTP Trusted nvctr
*/
unsigned int sotp_get_trusted_nvctr(void)
{
uint64_t rowdata;
uint64_t nextrowdata;
uint32_t rownum;
unsigned int nvctr;
rownum = SOTP_NVCTR_ROW_START;
nvctr = SOTP_NUM_BITS_PER_ROW;
/*
* Determine what row has last valid data for trusted ctr
*/
rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
while ((rowdata & SOTP_NVCTR_TRUSTED_IN_USE) &&
(rowdata & SOTP_NVCTR_TRUSTED_NEAR_END) &&
(rownum < SOTP_NVCTR_ROW_END)) {
/*
* Current row in use and has data in last 2 bits as well.
* Check if next row also has data for this counter
*/
nextrowdata = sotp_mem_read(rownum+1, SOTP_ROW_NO_ECC);
if (nextrowdata & SOTP_NVCTR_TRUSTED_IN_USE) {
/* Next row also has data so increment rownum */
rownum++;
nvctr += SOTP_NUM_BITS_PER_ROW;
rowdata = nextrowdata;
} else {
/* Next row does not have data */
break;
}
}
if (rowdata & SOTP_NVCTR_TRUSTED_IN_USE) {
while ((rowdata & 0x1) == 0) {
nvctr--;
rowdata >>= 1;
}
} else
nvctr -= SOTP_NUM_BITS_PER_ROW;
INFO("CTR %i\n", nvctr);
return nvctr;
}
/*
* Get SOTP NonTrusted nvctr
*/
unsigned int sotp_get_nontrusted_nvctr(void)
{
uint64_t rowdata;
uint64_t nextrowdata;
uint32_t rownum;
unsigned int nvctr;
nvctr = SOTP_NUM_BITS_PER_ROW;
rownum = SOTP_NVCTR_ROW_END;
/*
* Determine what row has last valid data for nontrusted ctr
*/
rowdata = sotp_mem_read(rownum, SOTP_ROW_NO_ECC);
while ((rowdata & SOTP_NVCTR_NON_TRUSTED_NEAR_END) &&
(rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) &&
(rownum > SOTP_NVCTR_ROW_START)) {
/*
* Current row in use and has data in last 2 bits as well.
* Check if next row also has data for this counter
*/
nextrowdata = sotp_mem_read(rownum-1, SOTP_ROW_NO_ECC);
if (nextrowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) {
/* Next row also has data so decrement rownum */
rownum--;
nvctr += SOTP_NUM_BITS_PER_ROW;
rowdata = nextrowdata;
} else {
/* Next row does not have data */
break;
}
}
if (rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE) {
while ((rowdata & ((uint64_t)0x1 << (SOTP_NUM_BITS_PER_ROW-1)))
==
0) {
nvctr--;
rowdata <<= 1;
}
} else
nvctr -= SOTP_NUM_BITS_PER_ROW;
INFO("NCTR %i\n", nvctr);
return nvctr;
}
/*
* Set SOTP Trusted nvctr
*/
int sotp_set_trusted_nvctr(unsigned int nvctr)
{
int numrows_available;
uint32_t nontrusted_rownum;
uint32_t trusted_rownum;
uint64_t rowdata;
unsigned int maxnvctr;
/*
* Read SOTP to find out how many rows are used by the
* NON Trusted nvctr
*/
nontrusted_rownum = SOTP_NVCTR_ROW_END;
do {
rowdata = sotp_mem_read(nontrusted_rownum, SOTP_ROW_NO_ECC);
if (rowdata & SOTP_NVCTR_NON_TRUSTED_IN_USE)
nontrusted_rownum--;
else
break;
} while (nontrusted_rownum >= SOTP_NVCTR_ROW_START);
/*
* Calculate maximum value we can have for nvctr based on
* number of available rows.
*/
numrows_available = nontrusted_rownum - SOTP_NVCTR_ROW_START + 1;
maxnvctr = numrows_available * SOTP_NUM_BITS_PER_ROW;
if (maxnvctr) {
/*
* Last 2 bits of counter can't be written or it will
* overflow with nontrusted counter
*/
maxnvctr -= 2;
}
if (nvctr > maxnvctr) {
/* Error - not enough room */
WARN("tctr not set\n");
return 1;
}
/*
* It is safe to write the nvctr, fill all 1's up to the
* last row and then fill the last row with partial bitstream
*/
trusted_rownum = SOTP_NVCTR_ROW_START;
rowdata = SOTP_NVCTR_ROW_ALL_ONES;
while (nvctr >= SOTP_NUM_BITS_PER_ROW) {
sotp_mem_write(trusted_rownum, SOTP_ROW_NO_ECC, rowdata);
nvctr -= SOTP_NUM_BITS_PER_ROW;
trusted_rownum++;
}
rowdata <<= (SOTP_NUM_BITS_PER_ROW - nvctr);
sotp_mem_write(trusted_rownum, SOTP_ROW_NO_ECC, rowdata);
return 0;
}
/*
* Set SOTP NonTrusted nvctr
*/
int sotp_set_nontrusted_nvctr(unsigned int nvctr)
{
int numrows_available;
uint32_t nontrusted_rownum;
uint32_t trusted_rownum;
uint64_t rowdata;
unsigned int maxnvctr;
/*
* Read SOTP to find out how many rows are used by the
* Trusted nvctr
*/
trusted_rownum = SOTP_NVCTR_ROW_START;
do {
rowdata = sotp_mem_read(trusted_rownum, SOTP_ROW_NO_ECC);
if (rowdata & SOTP_NVCTR_TRUSTED_IN_USE)
trusted_rownum++;
else
break;
} while (trusted_rownum <= SOTP_NVCTR_ROW_END);
/*
* Calculate maximum value we can have for nvctr based on
* number of available rows.
*/
numrows_available = SOTP_NVCTR_ROW_END - trusted_rownum + 1;
maxnvctr = numrows_available * SOTP_NUM_BITS_PER_ROW;
if (maxnvctr) {
/*
* Last 2 bits of counter can't be written or it will
* overflow with nontrusted counter
*/
maxnvctr -= 2;
}
if (nvctr > maxnvctr) {
/* Error - not enough room */
WARN("nctr not set\n");
return 1;
}
/*
* It is safe to write the nvctr, fill all 1's up to the
* last row and then fill the last row with partial bitstream
*/
nontrusted_rownum = SOTP_NVCTR_ROW_END;
rowdata = SOTP_NVCTR_ROW_ALL_ONES;
while (nvctr >= SOTP_NUM_BITS_PER_ROW) {
sotp_mem_write(nontrusted_rownum, SOTP_ROW_NO_ECC, rowdata);
nvctr -= SOTP_NUM_BITS_PER_ROW;
nontrusted_rownum--;
}
rowdata >>= (SOTP_NUM_BITS_PER_ROW - nvctr);
sotp_mem_write(nontrusted_rownum, SOTP_ROW_NO_ECC, rowdata);
return 0;
}
/*
* Return the non-volatile counter value stored in the platform. The cookie
* will contain the OID of the counter in the certificate.
*
* Return: 0 = success, Otherwise = error
*/
int plat_get_nv_ctr(void *cookie, unsigned int *nv_ctr)
{
const char *oid;
assert(cookie != NULL);
assert(nv_ctr != NULL);
*nv_ctr = 0;
if ((sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
SOTP_ATF_NVCOUNTER_ENABLE_MASK)) {
oid = (const char *)cookie;
if (strcmp(oid, TRUSTED_FW_NVCOUNTER_OID) == 0)
*nv_ctr = sotp_get_trusted_nvctr();
else if (strcmp(oid, NON_TRUSTED_FW_NVCOUNTER_OID) == 0)
*nv_ctr = sotp_get_nontrusted_nvctr();
else
return 1;
}
return 0;
}
/*
* Store a new non-volatile counter value.
*
* Return: 0 = success, Otherwise = error
*/
int plat_set_nv_ctr(void *cookie, unsigned int nv_ctr)
{
const char *oid;
if (sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
SOTP_ATF_NVCOUNTER_ENABLE_MASK) {
INFO("set CTR %i\n", nv_ctr);
oid = (const char *)cookie;
if (strcmp(oid, TRUSTED_FW_NVCOUNTER_OID) == 0)
return sotp_set_trusted_nvctr(nv_ctr);
else if (strcmp(oid, NON_TRUSTED_FW_NVCOUNTER_OID) == 0)
return sotp_set_nontrusted_nvctr(nv_ctr);
return 1;
}
return 0;
}
int plat_get_mbedtls_heap(void **heap_addr, size_t *heap_size)
{
return get_mbedtls_heap_helper(heap_addr, heap_size);
}

View File

@ -28,6 +28,17 @@ SYSCNT_FREQ := $(GENTIMER_ACTUAL_CLOCK)
$(eval $(call add_define,SYSCNT_FREQ))
endif
# By default, Trusted Watchdog is always enabled unless SPIN_ON_BL1_EXIT is set
ifeq (${BRCM_DISABLE_TRUSTED_WDOG},)
BRCM_DISABLE_TRUSTED_WDOG := 0
endif
ifeq (${SPIN_ON_BL1_EXIT}, 1)
BRCM_DISABLE_TRUSTED_WDOG := 1
endif
$(eval $(call assert_boolean,BRCM_DISABLE_TRUSTED_WDOG))
$(eval $(call add_define,BRCM_DISABLE_TRUSTED_WDOG))
# Process ARM_BL31_IN_DRAM flag
ifeq (${ARM_BL31_IN_DRAM},)
ARM_BL31_IN_DRAM := 0
@ -36,6 +47,7 @@ $(eval $(call assert_boolean,ARM_BL31_IN_DRAM))
$(eval $(call add_define,ARM_BL31_IN_DRAM))
ifeq (${STANDALONE_BL2},yes)
BL2_LOG_LEVEL := 40
$(eval $(call add_define,MMU_DISABLED))
endif
@ -45,6 +57,29 @@ ifeq (${RUN_BL2_FROM_QSPI},1)
$(eval $(call add_define,RUN_BL2_FROM_QSPI))
endif
# BL2 XIP from NAND
RUN_BL2_FROM_NAND := 0
ifeq (${RUN_BL2_FROM_NAND},1)
$(eval $(call add_define,RUN_BL2_FROM_NAND))
endif
ifneq (${ELOG_AP_UART_LOG_BASE},)
$(eval $(call add_define,ELOG_AP_UART_LOG_BASE))
endif
ifeq (${ELOG_SUPPORT},1)
ifeq (${ELOG_STORE_MEDIA},DDR)
$(eval $(call add_define,ELOG_STORE_MEDIA_DDR))
ifneq (${ELOG_STORE_OFFSET},)
$(eval $(call add_define,ELOG_STORE_OFFSET))
endif
endif
endif
ifneq (${BL2_LOG_LEVEL},)
$(eval $(call add_define,BL2_LOG_LEVEL))
endif
# Use CRMU SRAM from iHOST
ifneq (${USE_CRMU_SRAM},)
$(eval $(call add_define,USE_CRMU_SRAM))
@ -63,15 +98,18 @@ PLAT_INCLUDES += -Iplat/brcm/board/common \
PLAT_BL_COMMON_SOURCES += plat/brcm/common/brcm_common.c \
plat/brcm/board/common/cmn_sec.c \
plat/brcm/board/common/bcm_console.c \
plat/brcm/board/common/brcm_mbedtls.c \
plat/brcm/board/common/plat_setup.c \
plat/brcm/board/common/platform_common.c \
drivers/arm/sp804/sp804_delay_timer.c \
drivers/brcm/sotp.c \
drivers/delay_timer/delay_timer.c \
drivers/io/io_fip.c \
drivers/io/io_memmap.c \
drivers/io/io_storage.c \
plat/brcm/common/brcm_io_storage.c \
plat/brcm/board/common/err.c \
plat/brcm/board/common/sbl_util.c \
drivers/arm/sp805/sp805.c
BL2_SOURCES += plat/brcm/common/brcm_bl2_mem_params_desc.c \
@ -82,6 +120,88 @@ BL2_SOURCES += plat/brcm/common/brcm_bl2_setup.c
BL31_SOURCES += plat/brcm/common/brcm_bl31_setup.c
ifeq (${BCM_ELOG},yes)
ELOG_SOURCES += plat/brcm/board/common/bcm_elog.c
BL2_SOURCES += ${ELOG_SOURCES}
endif
ifeq (${DRIVER_OCOTP_ENABLE},1)
$(eval $(call add_define,DRIVER_OCOTP_ENABLE))
BL2_SOURCES += drivers/brcm/ocotp.c
endif
# Enable FRU table support
ifeq (${USE_FRU},yes)
$(eval $(call add_define,USE_FRU))
BL2_SOURCES += drivers/brcm/fru.c
endif
# Enable GPIO support
ifeq (${USE_GPIO},yes)
$(eval $(call add_define,USE_GPIO))
BL2_SOURCES += drivers/gpio/gpio.c
BL2_SOURCES += drivers/brcm/iproc_gpio.c
ifeq (${GPIO_SUPPORT_FLOAT_DETECTION},yes)
$(eval $(call add_define,GPIO_SUPPORT_FLOAT_DETECTION))
endif
endif
# Include mbedtls if it can be located
MBEDTLS_DIR := mbedtls
MBEDTLS_CHECK := $(shell find ${MBEDTLS_DIR}/include -name '${MBEDTLS_DIR}')
ifneq (${MBEDTLS_CHECK},)
$(info Found mbedTLS at ${MBEDTLS_DIR})
PLAT_INCLUDES += -I${MBEDTLS_DIR}/include/mbedtls
# Specify mbedTLS configuration file
MBEDTLS_CONFIG_FILE := "<brcm_mbedtls_config.h>"
# By default, use RSA keys
KEY_ALG := rsa_1_5
# Include common TBB sources
AUTH_SOURCES += drivers/auth/auth_mod.c \
drivers/auth/crypto_mod.c \
drivers/auth/img_parser_mod.c \
drivers/auth/tbbr/tbbr_cot.c
BL2_SOURCES += ${AUTH_SOURCES}
# Use ATF framework for MBEDTLS
TRUSTED_BOARD_BOOT := 1
CRYPTO_LIB_MK := drivers/auth/mbedtls/mbedtls_crypto.mk
IMG_PARSER_LIB_MK := drivers/auth/mbedtls/mbedtls_x509.mk
$(info Including ${CRYPTO_LIB_MK})
include ${CRYPTO_LIB_MK}
$(info Including ${IMG_PARSER_LIB_MK})
include ${IMG_PARSER_LIB_MK}
# Use ATF secure boot functions
# Use Hardcoded hash for devel
ARM_ROTPK_LOCATION=arm_rsa
ifeq (${ARM_ROTPK_LOCATION}, arm_rsa)
ARM_ROTPK_LOCATION_ID=ARM_ROTPK_DEVEL_RSA_ID
ROT_KEY=plat/arm/board/common/rotpk/arm_rotprivk_rsa.pem
else ifeq (${ARM_ROTPK_LOCATION}, brcm_rsa)
ARM_ROTPK_LOCATION_ID=BRCM_ROTPK_SOTP_RSA_ID
ifeq (${ROT_KEY},)
ROT_KEY=plat/brcm/board/common/rotpk/rsa_dauth2048_key.pem
endif
KEY_FIND := $(shell m="${ROT_KEY}"; [ -f "$$m" ] && echo "$$m")
ifeq (${KEY_FIND},)
$(error Error: No ${ROT_KEY} located)
else
$(info Using ROT_KEY: ${ROT_KEY})
endif
else
$(error "Unsupported ARM_ROTPK_LOCATION value")
endif
$(eval $(call add_define,ARM_ROTPK_LOCATION_ID))
PLAT_BL_COMMON_SOURCES+=plat/brcm/board/common/board_arm_trusted_boot.c
endif
#M0 runtime firmware
ifdef SCP_BL2
$(eval $(call add_define,NEED_SCP_BL2))

View File

@ -0,0 +1,12 @@
/*
* Copyright 2015 - 2020 Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <common/debug.h>
void tls_exit(int code)
{
INFO("%s: 0x%x\n", __func__, code);
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef CHIP_ID_H
#define CHIP_ID_H
#include <lib/mmio.h>
#include <platform_def.h>
#define CHIP_REV_MAJOR_MASK 0xF0
#define CHIP_REV_MAJOR_AX 0x00
#define CHIP_REV_MAJOR_BX 0x10
#define CHIP_REV_MAJOR_CX 0x20
#define CHIP_REV_MAJOR_DX 0x30
/* Get Chip ID (product number) of the chip */
static inline unsigned int chip_get_product_id(void)
{
return PLAT_CHIP_ID_GET;
}
/* Get Revision ID (major and minor) number of the chip */
static inline unsigned int chip_get_rev_id(void)
{
return PLAT_CHIP_REV_GET;
}
static inline unsigned int chip_get_rev_id_major(void)
{
return (chip_get_rev_id() & CHIP_REV_MAJOR_MASK);
}
#endif

View File

@ -7,6 +7,56 @@
#ifndef CMN_PLAT_DEF_H
#define CMN_PLAT_DEF_H
#include <bcm_elog.h>
#ifndef GET_LOG_LEVEL
#define GET_LOG_LEVEL() LOG_LEVEL
#endif
#ifndef SET_LOG_LEVEL
#define SET_LOG_LEVEL(x) ((void)(x))
#endif
#define PLAT_LOG_NOTICE(...) \
do { \
if (GET_LOG_LEVEL() >= LOG_LEVEL_NOTICE) { \
bcm_elog(LOG_MARKER_NOTICE __VA_ARGS__); \
tf_log(LOG_MARKER_NOTICE __VA_ARGS__); \
} \
} while (0)
#define PLAT_LOG_ERROR(...) \
do { \
if (GET_LOG_LEVEL() >= LOG_LEVEL_ERROR) { \
bcm_elog(LOG_MARKER_ERROR, __VA_ARGS__); \
tf_log(LOG_MARKER_ERROR __VA_ARGS__); \
} \
} while (0)
#define PLAT_LOG_WARN(...) \
do { \
if (GET_LOG_LEVEL() >= LOG_LEVEL_WARNING) { \
bcm_elog(LOG_MARKER_WARNING, __VA_ARGS__);\
tf_log(LOG_MARKER_WARNING __VA_ARGS__); \
} \
} while (0)
#define PLAT_LOG_INFO(...) \
do { \
if (GET_LOG_LEVEL() >= LOG_LEVEL_INFO) { \
bcm_elog(LOG_MARKER_INFO __VA_ARGS__); \
tf_log(LOG_MARKER_INFO __VA_ARGS__); \
} \
} while (0)
#define PLAT_LOG_VERBOSE(...) \
do { \
if (GET_LOG_LEVEL() >= LOG_LEVEL_VERBOSE) { \
bcm_elog(LOG_MARKER_VERBOSE __VA_ARGS__);\
tf_log(LOG_MARKER_VERBOSE __VA_ARGS__); \
} \
} while (0)
/* Print file and line number on assert */
#define PLAT_LOG_LEVEL_ASSERT LOG_LEVEL_INFO

View File

@ -6,14 +6,65 @@
#include <arch_helpers.h>
#include <common/debug.h>
#include <drivers/brcm/sotp.h>
#include <cmn_plat_util.h>
#include <platform_def.h>
uint32_t boot_source_get(void)
{
/* For now return BOOT_SOURCE_QSPI */
return BOOT_SOURCE_QSPI;
uint32_t data;
#ifdef FORCE_BOOTSOURCE
data = FORCE_BOOTSOURCE;
#else
/* Read primary boot strap from CRMU persistent registers */
data = mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG1);
if (data & BOOT_SOURCE_SOFT_ENABLE_MASK) {
data >>= BOOT_SOURCE_SOFT_DATA_OFFSET;
} else {
uint64_t sotp_atf_row;
sotp_atf_row =
sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC);
if (sotp_atf_row & SOTP_BOOT_SOURCE_ENABLE_MASK) {
/* Construct the boot source based on SOTP bits */
data = 0;
if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS0)
data |= 0x1;
if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS1)
data |= 0x2;
if (sotp_atf_row & SOTP_BOOT_SOURCE_BITS2)
data |= 0x4;
} else {
/*
* This path is for L0 reset with
* Primary Boot source disabled in SOTP.
* BOOT_SOURCE_FROM_PR_ON_L1 compile flag will allow
* to never come back here so that the
* external straps will not be read on L1 reset.
*/
/* Use the external straps */
data = mmio_read_32(ROM_S0_IDM_IO_STATUS);
#ifdef BOOT_SOURCE_FROM_PR_ON_L1
/* Enable boot source read from PR#1 */
mmio_setbits_32(CRMU_IHOST_SW_PERSISTENT_REG1,
BOOT_SOURCE_SOFT_ENABLE_MASK);
/* set boot source */
data &= BOOT_SOURCE_MASK;
mmio_clrsetbits_32(CRMU_IHOST_SW_PERSISTENT_REG1,
BOOT_SOURCE_MASK << BOOT_SOURCE_SOFT_DATA_OFFSET,
data << BOOT_SOURCE_SOFT_DATA_OFFSET);
#endif
}
}
#endif
return (data & BOOT_SOURCE_MASK);
}
void __dead2 plat_soft_reset(uint32_t reset)

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2015 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stdint.h>
#include <common/debug.h>
#include <platform_def.h>
#include <sbl_util.h>
#include <sotp.h>
#pragma weak plat_sbl_status
int plat_sbl_status(uint64_t sbl_status)
{
return sbl_status ? 1:0;
}
int sbl_status(void)
{
uint64_t sbl_sotp = 0;
int ret = SBL_DISABLED;
sbl_sotp = sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC);
if (sbl_sotp != SOTP_ECC_ERR_DETECT) {
sbl_sotp &= SOTP_SBL_MASK;
if (plat_sbl_status(sbl_sotp))
ret = SBL_ENABLED;
}
VERBOSE("SBL status: %d\n", ret);
return ret;
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (c) 2015 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SBL_UTIL_H
#define SBL_UTIL_H
#include <stdint.h>
#include <sotp.h>
#define SBL_DISABLED 0
#define SBL_ENABLED 1
int sbl_status(void);
#endif /* #ifdef SBL_UTIL_H */

View File

@ -14,3 +14,9 @@ include plat/brcm/board/stingray/bcm958742t.mk
ifneq (${BL33_OVERRIDE_LOAD_ADDR},)
$(eval $(call add_define_val,BL33_OVERRIDE_LOAD_ADDR,0xFF000000))
endif
# Nitro DDR secure memory
# Nitro FW and config 0x8AE00000 - 0x8B000000
# Nitro Crash dump 0x8B000000 - 0x8D000000
DDR_NITRO_SECURE_REGION_START := 0x8AE00000
DDR_NITRO_SECURE_REGION_END := 0x8D000000

View File

@ -0,0 +1,33 @@
/*
* Copyright (c) 2019-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BOARD_FAMILY_H
#define BOARD_FAMILY_H
#if defined(DRIVER_SPD_ENABLE) && !defined(DRIVER_SPD_SPOOF)
#include <spd.h>
#endif
#ifdef USE_GPIO
/* max number of supported GPIOs to construct the bitmap for board detection */
#define MAX_NR_GPIOS 4
/* max GPIO bitmap value */
#define MAX_GPIO_BITMAP_VAL (BIT(MAX_NR_GPIOS) - 1)
#endif
struct mcb_ref_group {
uint32_t mcb_ref;
unsigned int *mcb_cfg;
};
#define MCB_REF_GROUP(ref) \
{ \
.mcb_ref = 0x ## ref, \
.mcb_cfg = mcb_ ## ref, \
}
#endif

View File

@ -0,0 +1,302 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <platform_def.h>
static void brcm_stingray_pnor_pinmux_init(void)
{
unsigned int i;
INFO(" - pnor pinmux init start.\n");
/* Set PNOR_ADV_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2dc),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_BAA_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e0),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_BLS_0_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e4),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_BLS_1_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2e8),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_CRE_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2ec),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_CS_2_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f0),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_CS_1_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f4),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_CS_0_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2f8),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_WE_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x2fc),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_OE_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x300),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_INTR_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x304),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set PNOR_DAT_x_MODE_SEL_CONTROL.fsel = 0x2 */
for (i = 0; i < 0x40; i += 0x4) {
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x308 + i),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
}
/* Set NAND_CE1_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x348),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_CE0_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x34c),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_WE_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x350),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_WP_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x354),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_RE_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x358),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_RDY_BSY_N_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x35c),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_IOx_0_MODE_SEL_CONTROL.fsel = 0x2 */
for (i = 0; i < 0x40; i += 0x4) {
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x360 + i),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
}
/* Set NAND_ALE_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x3a0),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
/* Set NAND_CLE_MODE_SEL_CONTROL.fsel = 0x2 */
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x3a4),
MODE_SEL_CONTROL_FSEL_MASK,
MODE_SEL_CONTROL_FSEL_MODE2);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x40), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x44), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x48), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x4c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x50), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x54), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x58), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x5c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x60), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x64), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x68), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x6c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x70), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x74), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x78), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x7c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x80), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x84), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x88), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x8c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x90), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x94), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x98), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0x9c), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa0), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa4), (7 << 1), 0x8);
mmio_clrsetbits_32((uintptr_t)(HSLS_IOPAD_BASE + 0xa8), (7 << 1), 0x8);
INFO(" - pnor pinmux init done.\n");
}
#if BL2_TEST_EXT_SRAM
#define SRAM_CHECKS_GRANUL 0x100000
#define SRAM_CHECKS_CNT 8
static unsigned int sram_checks[SRAM_CHECKS_CNT] = {
/* offset, magic */
0xd00dfeed,
0xfadebabe,
0xc001d00d,
0xa5a5b5b5,
0x5a5a5b5b,
0xc5c5d5d5,
0x5c5c5d5d,
0xe5e5f5f5,
};
#endif
static void brcm_stingray_pnor_sram_init(void)
{
unsigned int val, tmp;
#if BL2_TEST_EXT_SRAM
unsigned int off, i;
#endif
INFO(" - pnor sram init start.\n");
/* Enable PNOR Clock */
INFO(" -- enable pnor clock\n");
mmio_write_32((uintptr_t)(PNOR_IDM_IO_CONTROL_DIRECT), 0x1);
udelay(500);
/* Reset PNOR */
INFO(" -- reset pnor\n");
mmio_setbits_32((uintptr_t)(PNOR_IDM_IO_RESET_CONTROL), 0x1);
udelay(500);
mmio_clrbits_32((uintptr_t)(PNOR_IDM_IO_RESET_CONTROL), 0x1);
udelay(500);
/* Configure slave address to chip-select mapping */
INFO(" -- configure pnor slave address to chip-select mapping\n");
/* 0x74000000-0x75ffffff => CS0 (32MB) */
val = (0xfe << PNOR_ICFG_CS_x_MASK0_SHIFT);
val |= (0x74);
mmio_write_32((uintptr_t)(PNOR_ICFG_CS_0), val);
/* 0x76000000-0x77ffffff => CS1 (32MB) */
val = (0xfe << PNOR_ICFG_CS_x_MASK0_SHIFT);
val |= (0x76);
mmio_write_32((uintptr_t)(PNOR_ICFG_CS_1), val);
/* 0xffffffff-0xffffffff => CS2 (0MB) */
val = (0x00 << PNOR_ICFG_CS_x_MASK0_SHIFT);
val |= (0xff);
mmio_write_32((uintptr_t)(PNOR_ICFG_CS_2), val);
/* Print PNOR ID */
tmp = 0x0;
val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID0));
tmp |= (val & PNOR_REG_PERIPH_IDx_MASK);
val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID1));
tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 8);
val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID2));
tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 16);
val = mmio_read_32((uintptr_t)(PNOR_REG_PERIPH_ID3));
tmp |= ((val & PNOR_REG_PERIPH_IDx_MASK) << 24);
INFO(" -- pnor primecell_id = 0x%x\n", tmp);
/* PNOR set_cycles */
#ifdef EMULATION_SETUP
val = 0x00129A44;
#else
val = 0x00125954; /* 0x00002DEF; */
#endif
mmio_write_32((uintptr_t)(PNOR_REG_SET_CYCLES), val);
INFO(" -- pnor set_cycles = 0x%x\n", val);
/* PNOR set_opmode */
val = 0x0;
#ifdef EMULATION_SETUP
/* TODO: Final values to be provided by DV folks */
val &= ~(0x7 << 7); /* set_wr_bl */
val &= ~(0x7 << 3); /* set_rd_bl */
val &= ~(0x3);
val |= (0x1); /* set_mw */
#else
/* TODO: Final values to be provided by DV folks */
val &= ~(0x7 << 7); /* set_wr_bl */
val &= ~(0x7 << 3); /* set_rd_bl */
val &= ~(0x3);
val |= (0x1); /* set_mw */
#endif
mmio_write_32((uintptr_t)(PNOR_REG_SET_OPMODE), val);
INFO(" -- pnor set_opmode = 0x%x\n", val);
#ifndef EMULATION_SETUP
/* Actual SRAM chip will require self-refresh */
val = 0x1;
mmio_write_32((uintptr_t)(PNOR_REG_REFRESH_0), val);
INFO(" -- pnor refresh_0 = 0x%x\n", val);
#endif
#if BL2_TEST_EXT_SRAM
/* Check PNOR SRAM access */
for (off = 0; off < NOR_SIZE; off += SRAM_CHECKS_GRANUL) {
i = (off / SRAM_CHECKS_GRANUL) % SRAM_CHECKS_CNT;
val = sram_checks[i];
INFO(" -- pnor sram write addr=0x%lx value=0x%lx\n",
(unsigned long)(NOR_BASE_ADDR + off),
(unsigned long)val);
mmio_write_32((uintptr_t)(NOR_BASE_ADDR + off), val);
}
tmp = 0;
for (off = 0; off < NOR_SIZE; off += SRAM_CHECKS_GRANUL) {
i = (off / SRAM_CHECKS_GRANUL) % SRAM_CHECKS_CNT;
val = mmio_read_32((uintptr_t)(NOR_BASE_ADDR + off));
INFO(" -- pnor sram read addr=0x%lx value=0x%lx\n",
(unsigned long)(NOR_BASE_ADDR + off),
(unsigned long)val);
if (val == sram_checks[i])
tmp++;
}
INFO(" -- pnor sram checks pass=%d total=%d\n",
tmp, (NOR_SIZE / SRAM_CHECKS_GRANUL));
if (tmp != (NOR_SIZE / SRAM_CHECKS_GRANUL)) {
INFO(" - pnor sram init failed.\n");
while (1)
;
} else {
INFO(" - pnor sram init done.\n");
}
#endif
}
void ext_sram_init(void)
{
INFO("%s start.\n", __func__);
brcm_stingray_pnor_pinmux_init();
brcm_stingray_pnor_sram_init();
INFO("%s done.\n", __func__);
}

View File

@ -0,0 +1,11 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef EXT_SRAM_INIT_H
#define EXT_SRAM_INIT_H
void ext_sram_init(void);
#endif

View File

@ -0,0 +1,375 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <stdint.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <sr_utils.h>
#include <swreg.h>
#define MIN_VOLT 760000
#define MAX_VOLT 1060000
#define BSTI_WRITE 0x1
#define BSTI_READ 0x2
#define BSTI_COMMAND_TA 0x2
#define BSTI_COMMAND_DATA 0xFF
#define BSTI_CONTROL_VAL 0x81
#define BSTI_CONTROL_BUSY 0x100
#define BSTI_TOGGLE_BIT 0x2
#define BSTI_CONFI_DONE_MASK 0xFFFFFFFD
#define BSTI_REG_DATA_MASK 0xFFFF
#define BSTI_CMD(sb, op, pa, ra, ta, data) \
((((sb) & 0x3) << 30) | (((op) & 0x3) << 28) | \
(((pa) & 0x1F) << 23) | (((ra) & 0x1F) << 18) | \
(((ta) & 0x3) << 16) | (data))
#define PHY_REG0 0x0
#define PHY_REG1 0x1
#define PHY_REG4 0x4
#define PHY_REG5 0x5
#define PHY_REG6 0x6
#define PHY_REG7 0x7
#define PHY_REGC 0xc
#define IHOST_VDDC_DATA 0x560
#define DDR_CORE_DATA 0x2560
#define UPDATE_POS_EDGE(data, set) ((data) | ((set) << 1))
/*
* Formula for SR A2 reworked board:
* step = ((vol/(1.4117 * 0.98)) - 500000)/3125
* where,
* vol - input voltage
* 500000 - Reference voltage
* 3125 - one step value
*/
#define A2_VOL_REF 500000
#define ONE_STEP_VALUE 3125
#define VOL_DIV(vol) (((vol*10000ull)/(14117*98ull)) * 100ull)
#define STEP_VALUE(vol) \
((((((VOL_DIV(vol)) - A2_VOL_REF) / ONE_STEP_VALUE) & 0xFF) << 8) | 4)
#define B0_VOL_REF ((500000/100)*98)
#define B0_ONE_STEP_VALUE 3125
/*
* Formula for SR B0 chip for IHOST12/03 and VDDC_CORE
* step = ((vol/1.56) - (500000 * 0.98))/3125
* where,
* vol - input voltage
* 500000 - Reference voltage
* 3125 - one step value
*/
#define B0_VOL_DIV(vol) (((vol)*100ull)/156)
#define B0_STEP_VALUE(vol) \
((((((B0_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
& 0xFF) << 8) | 4)
/*
* Formula for SR B0 chip for DDR-CORE
* step = ((vol/1) - (500000 * 0.98))/3125
* where,
* vol - input voltage
* 500000 - Reference voltage
* 3125 - one step value
*/
#define B0_DDR_VDDC_VOL_DIV(vol) ((vol)/1)
#define B0_DDR_VDDC_STEP_VALUE(vol) \
((((((B0_DDR_VDDC_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
& 0xFF) << 8) | 4)
#define MAX_SWREG_CNT 8
#define MAX_ADDR_PER_SWREG 16
#define MAX_REG_ADDR 0xF
#define MIN_REG_ADDR 0x0
static const char *sw_reg_name[MAX_SWREG_CNT] = {
"DDR_VDDC",
"IHOST03",
"IHOST12",
"IHOST_ARRAY",
"DDRIO_SLAVE",
"VDDC_CORE",
"VDDC1",
"DDRIO_MASTER"
};
/* firmware values for all SWREG for 3.3V input operation */
static const uint16_t swreg_fm_data_bx[MAX_SWREG_CNT][MAX_ADDR_PER_SWREG] = {
/* DDR logic: Power Domains independent of 12v or 3p3v */
{0x25E0, 0x2D54, 0x0EC6, 0x01EC, 0x28BB, 0x1144, 0x0200, 0x69C0,
0x0010, 0x0EDF, 0x90D7, 0x8000, 0x820C, 0x0003, 0x0001, 0x0000},
/* ihost03, 3p3V */
{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
/* ihost12 3p3v */
{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
/* ihost array */
{0x25E0, 0x2D94, 0x0EC6, 0x01EC, 0x2ABB, 0x1144, 0x0340, 0x69C0,
0x0010, 0x0EDF, 0x90D7, 0x8000, 0x860C, 0x0003, 0x0001, 0x0000},
/* ddr io slave : 3p3v */
{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
0x003F, 0x0FFF, 0x10D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
/* core master 3p3v */
{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
/* core slave 3p3v */
{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
0x003F, 0x0FFF, 0x10D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
/* ddr io master : 3p3v */
{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
0x003F, 0x0FFF, 0x90D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
};
#define FM_DATA swreg_fm_data_bx
static int swreg_poll(void)
{
uint32_t data;
int retry = 100;
do {
data = mmio_read_32(BSTI_CONTROL_OFFSET);
if ((data & BSTI_CONTROL_BUSY) != BSTI_CONTROL_BUSY)
return 0;
retry--;
udelay(1);
} while (retry > 0);
return -ETIMEDOUT;
}
static int write_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t data)
{
uint32_t cmd;
int ret;
cmd = BSTI_CMD(0x1, BSTI_WRITE, reg_id, addr, BSTI_COMMAND_TA, data);
mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
ret = swreg_poll();
if (ret) {
ERROR("Failed to write swreg %s addr 0x%x\n",
sw_reg_name[reg_id-1], addr);
return ret;
}
return ret;
}
static int read_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t *data)
{
uint32_t cmd;
int ret;
cmd = BSTI_CMD(0x1, BSTI_READ, reg_id, addr, BSTI_COMMAND_TA, PHY_REG0);
mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
ret = swreg_poll();
if (ret) {
ERROR("Failed to read swreg %s addr 0x%x\n",
sw_reg_name[reg_id-1], addr);
return ret;
}
*data = mmio_read_32(BSTI_COMMAND_OFFSET);
*data &= BSTI_REG_DATA_MASK;
return ret;
}
static int swreg_config_done(enum sw_reg reg_id)
{
uint32_t read_data;
int ret;
ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
if (ret)
return ret;
read_data &= BSTI_CONFI_DONE_MASK;
read_data |= BSTI_TOGGLE_BIT;
ret = write_swreg_config(reg_id, PHY_REG0, read_data);
if (ret)
return ret;
ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
if (ret)
return ret;
read_data &= BSTI_CONFI_DONE_MASK;
ret = write_swreg_config(reg_id, PHY_REG0, read_data);
if (ret)
return ret;
return ret;
}
#ifdef DUMP_SWREG
static void dump_swreg_firmware(void)
{
enum sw_reg reg_id;
uint32_t data;
int addr;
int ret;
for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
INFO("SWREG: %s\n", sw_reg_name[reg_id - 1]);
for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
ret = read_swreg_config(reg_id, addr, &data);
if (ret)
ERROR("Failed to read offset %d\n", addr);
INFO("\t0x%x: 0x%04x\n", addr, data);
}
}
}
#endif
int set_swreg(enum sw_reg reg_id, uint32_t micro_volts)
{
uint32_t step, programmed_step;
uint32_t data = IHOST_VDDC_DATA;
int ret;
if ((micro_volts > MAX_VOLT) || (micro_volts < MIN_VOLT)) {
ERROR("input voltage out-of-range\n");
ret = -EINVAL;
goto failed;
}
ret = read_swreg_config(reg_id, PHY_REGC, &programmed_step);
if (ret)
goto failed;
if (reg_id == DDR_VDDC)
step = B0_DDR_VDDC_STEP_VALUE(micro_volts);
else
step = B0_STEP_VALUE(micro_volts);
if ((step >> 8) != (programmed_step >> 8)) {
ret = write_swreg_config(reg_id, PHY_REGC, step);
if (ret)
goto failed;
if (reg_id == DDR_VDDC)
data = DDR_CORE_DATA;
ret = write_swreg_config(reg_id, PHY_REG0,
UPDATE_POS_EDGE(data, 1));
if (ret)
goto failed;
ret = write_swreg_config(reg_id, PHY_REG0,
UPDATE_POS_EDGE(data, 0));
if (ret)
goto failed;
}
INFO("%s voltage updated to %duV\n", sw_reg_name[reg_id-1],
micro_volts);
return ret;
failed:
/*
* Stop booting if voltages are not set
* correctly. Booting will fail at random point
* if we continue with wrong voltage settings.
*/
ERROR("Failed to set %s voltage to %duV\n", sw_reg_name[reg_id-1],
micro_volts);
assert(0);
return ret;
}
/* Update SWREG firmware for all power doman for A2 chip */
int swreg_firmware_update(void)
{
enum sw_reg reg_id;
uint32_t data;
int addr;
int ret;
/* write firmware values */
for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
/* write higher location first */
for (addr = MAX_REG_ADDR; addr >= MIN_REG_ADDR; addr--) {
ret = write_swreg_config(reg_id, addr,
FM_DATA[reg_id - 1][addr]);
if (ret)
goto exit;
}
}
/* trigger SWREG firmware update */
for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
/*
* Slave regulator doesn't have to be updated,
* Updating Master is enough
*/
if ((reg_id == DDRIO_SLAVE) || (reg_id == VDDC1))
continue;
ret = swreg_config_done(reg_id);
if (ret) {
ERROR("Failed to trigger SWREG firmware update for %s\n"
, sw_reg_name[reg_id-1]);
return ret;
}
}
for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
/*
* IHOST_ARRAY will be used on some boards like STRATUS and
* there will not be any issue even if it is updated on other
* boards where it is not used.
*/
if (reg_id == IHOST_ARRAY)
continue;
for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
ret = read_swreg_config(reg_id, addr, &data);
if (ret || (!ret &&
(data != FM_DATA[reg_id - 1][addr]))) {
ERROR("swreg fm update failed: %s at off %d\n",
sw_reg_name[reg_id - 1], addr);
ERROR("Read val: 0x%x, expected val: 0x%x\n",
data, FM_DATA[reg_id - 1][addr]);
return -1;
}
}
}
INFO("Updated SWREG firmware\n");
#ifdef DUMP_SWREG
dump_swreg_firmware();
#endif
return ret;
exit:
/*
* Stop booting if swreg firmware update fails.
* Booting will fail at random point if we
* continue with wrong voltage settings.
*/
ERROR("Failed to update firmware for %s SWREG\n",
sw_reg_name[reg_id-1]);
assert(0);
return ret;
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef BOARD_INFO_H
#define BOARD_INFO_H
#define IHOST_REG_INTEGRATED 0
#define IHOST_REG_EXT_PROGRAMMABLE 1
#define IHOST_REG_EXT_FIXED 2
#if defined(IHOST_REG_TYPE)
#if ((IHOST_REG_TYPE != IHOST_REG_INTEGRATED) && \
(IHOST_REG_TYPE != IHOST_REG_EXT_PROGRAMMABLE) && \
(IHOST_REG_TYPE != IHOST_REG_EXT_FIXED))
#error "IHOST_REG_TYPE not valid"
#endif
#else
#define IHOST_REG_TYPE IHOST_REG_INTEGRATED
#endif
#define VDDC_REG_INTEGRATED 0
#define VDDC_REG_EXT_PROGRAMMABLE 1
#define VDDC_REG_EXT_FIXED 2
#if defined(VDDC_REG_TYPE)
#if ((VDDC_REG_TYPE != VDDC_REG_INTEGRATED) && \
(VDDC_REG_TYPE != VDDC_REG_EXT_PROGRAMMABLE) && \
(VDDC_REG_TYPE != VDDC_REG_EXT_FIXED))
#error "VDDC_REG_TYPE not valid"
#endif
#else
#define VDDC_REG_TYPE VDDC_REG_INTEGRATED
#endif
#endif

View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2016 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef DDR_INIT_H
#define DDR_INIT_H
#include <fru.h>
#pragma weak ddr_initialize
#pragma weak ddr_secure_region_config
#pragma weak ddr_info_save
#pragma weak get_active_ddr_channel
#pragma weak is_warmboot
void ddr_initialize(struct ddr_info *ddr)
{
}
void ddr_secure_region_config(uint64_t start, uint64_t end)
{
}
void ddr_info_save(void)
{
}
unsigned char get_active_ddr_channel(void)
{
return 0;
}
static inline unsigned int is_warmboot(void)
{
return 0;
}
#endif

View File

@ -12,10 +12,8 @@
#include <plat/common/common_def.h>
#include <brcm_def.h>
#include <cmn_plat_def.h>
#include "sr_def.h"
#include <cmn_plat_def.h>
/*
* Most platform porting definitions provided by included headers

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PLATFORM_SOTP_H
#define PLATFORM_SOTP_H
#define SOTP_DEVICE_SECURE_CFG0_ROW 17
#define SOTP_DEVICE_SECURE_CFG1_ROW 18
#define SOTP_DEVICE_SECURE_CFG2_ROW 19
#define SOTP_DEVICE_SECURE_CFG3_ROW 20
#define SOTP_BRCM_SOFTWARE_CFG0_ROW 21
#define SOTP_BRCM_SOFTWARE_CFG1_ROW 22
#define SOTP_BRCM_SOFTWARE_CFG2_ROW 23
#define SOTP_BRCM_SOFTWARE_CFG3_ROW 24
#define SOTP_CUSTOMER_ID_CFG0_ROW 25
#define SOTP_CUSTOMER_ID_CFG1_ROW 26
#define SOTP_CUSTOMER_ID_CFG2_ROW 27
#define SOTP_CUSTOMER_ID_CFG3_ROW 28
#define SOTP_CUSTOMER_DEV_CFG0_ROW 29
#define SOTP_CUSTOMER_DEV_CFG1_ROW 30
#define SOTP_CUSTOMER_DEV_CFG2_ROW 31
#define SOTP_CUSTOMER_DEV_CFG3_ROW 32
#define SOTP_DAUTH_ROW 33
#define SOTP_K_HMAC_ROW 45
#define SOTP_K_AES_ROW 57
#define SOTP_NVCOUNTER_ROW 69
#define SOTP_BRCM_CFG_ECC_ERROR_MASK 0x100000
#define SOTP_DAUTH_ECC_ERROR_MASK 0x800000
#define SOTP_K_HMAC_ECC_ERROR_MASK 0x1000000
#define SOTP_K_AES_ECC_ERROR_MASK 0x2000000
#endif

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2017-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SCP_CMD_H
#define SCP_SMD_H
#include <stdint.h>
typedef struct {
int cmd;
int completed;
int ret;
} crmu_response_t;
#define SCP_CMD_MASK 0xffff
#define SCP_CMD_DEFAULT_TIMEOUT_US 1000
#define SCP_CMD_SCP_BOOT_TIMEOUT_US 5000
int scp_send_cmd(uint32_t cmd, uint32_t param, uint32_t timeout);
#endif

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SCP_UTILS_H
#define SCP_UTILS_H
#include <common/bl_common.h>
#include <lib/mmio.h>
#include <m0_cfg.h>
int plat_bcm_bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info);
bool is_crmu_alive(void);
bool bcm_scp_issue_sys_reset(void);
#define SCP_READ_CFG(cfg) mmio_read_32(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg))
#define SCP_WRITE_CFG(cfg, value) mmio_write_32(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg), value)
#define SCP_READ_CFG16(cfg) mmio_read_16(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg))
#define SCP_WRITE_CFG16(cfg, value) mmio_write_16(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg), value)
#define SCP_READ_CFG8(cfg) mmio_read_8(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg))
#define SCP_WRITE_CFG8(cfg, value) mmio_write_8(CRMU_CFG_BASE + \
offsetof(M0CFG, cfg), value)
#endif

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SR_UTILS_H
#define SR_UTILS_H
#include <lib/mmio.h>
#include <chip_id.h>
#include <cmn_plat_util.h>
#include <sr_def.h>
static inline void brcm_stingray_set_qspi_mux(int enable_ap)
{
mmio_write_32(QSPI_HOLD_N_MODE_SEL_CONTROL, enable_ap);
mmio_write_32(QSPI_WP_N_MODE_SEL_CONTROL, enable_ap);
mmio_write_32(QSPI_SCK_MODE_SEL_CONTROL, enable_ap);
mmio_write_32(QSPI_CS_N_MODE_SEL_CONTROL, enable_ap);
mmio_write_32(QSPI_MOSI_MODE_SEL_CONTROL, enable_ap);
mmio_write_32(QSPI_MISO_MODE_SEL_CONTROL, enable_ap);
}
static inline void brcm_stingray_set_straps(uint32_t boot_source)
{
/* Enable software strap override */
mmio_setbits_32(CDRU_CHIP_STRAP_CTRL,
BIT(CDRU_CHIP_STRAP_CTRL__SOFTWARE_OVERRIDE));
/* set straps to the next boot source */
mmio_clrsetbits_32(CDRU_CHIP_STRAP_DATA,
BOOT_SOURCE_MASK,
boot_source);
/* Disable software strap override */
mmio_clrbits_32(CDRU_CHIP_STRAP_CTRL,
BIT(CDRU_CHIP_STRAP_CTRL__SOFTWARE_OVERRIDE));
}
#endif

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SWREG_H
#define SWREG_H
/* default voltage if no valid OTP */
#define VDDC_CORE_DEF_VOLT 910000 /* 0.91v */
#define IHOST_DEF_VOLT 940000 /* 0.94v */
#define B0_VDDC_CORE_DEF_VOLT 950000 /* 0.95v */
#define B0_IHOST_DEF_VOLT 950000 /* 0.95v */
#define B0_DDR_VDDC_DEF_VOLT 1000000 /* 1v */
#define SWREG_IHOST1_DIS 4
#define SWREG_IHOST1_REG_RESETB 5
#define SWREG_IHOST1_PMU_STABLE 2
enum sw_reg {
DDR_VDDC = 1,
IHOST03,
IHOST12,
IHOST_ARRAY,
DDRIO_SLAVE,
VDDC_CORE,
VDDC1,
DDRIO_MASTER
};
int set_swreg(enum sw_reg reg_id, uint32_t micro_volts);
int swreg_firmware_update(void);
#endif

View File

@ -4,6 +4,19 @@
# SPDX-License-Identifier: BSD-3-Clause
#
# Set the toc_flags to 1 for 100% speed operation
# Set the toc_flags to 2 for 50% speed operation
# Set the toc_flags to 3 for 25% speed operation
# Set the toc_flags bit 3 to indicate ignore the fip in UEFI copy mode
PLAT_TOC_FLAGS := 0x0
# Set the IHOST_PLL_FREQ to,
# 1 for full speed
# 2 for 50% speed
# 3 for 25% speed
# 0 for bypass
$(eval $(call add_define_val,IHOST_PLL_FREQ,1))
# Enable workaround for ERRATA_A72_859971
ERRATA_A72_859971 := 1
@ -16,16 +29,42 @@ ARM_BL31_IN_DRAM := 1
USE_CRMU_SRAM := yes
# Enable error logging by default for Stingray
BCM_ELOG := yes
# Enable FRU support by default for Stingray
ifeq (${USE_FRU},)
USE_FRU := no
endif
# Use single cluster
ifeq (${USE_SINGLE_CLUSTER},yes)
$(info Using Single Cluster)
$(eval $(call add_define,USE_SINGLE_CLUSTER))
endif
# Use DDR
ifeq (${USE_DDR},yes)
$(info Using DDR)
$(eval $(call add_define,USE_DDR))
endif
ifeq (${BOARD_CFG},)
BOARD_CFG := bcm958742k
endif
# Use NAND
ifeq (${USE_NAND},$(filter yes, ${USE_NAND}))
$(info Using NAND)
$(eval $(call add_define,USE_NAND))
endif
# Enable Broadcom error logging support
ifeq (${BCM_ELOG},yes)
$(info Using BCM_ELOG)
$(eval $(call add_define,BCM_ELOG))
endif
# BL31 build for standalone mode
ifeq (${STANDALONE_BL31},yes)
RESET_TO_BL31 := 1
@ -43,6 +82,9 @@ endif
# Default soft reset is L3
$(eval $(call add_define,CONFIG_SOFT_RESET_L3))
# Enable Chip OTP driver
DRIVER_OCOTP_ENABLE := 1
include plat/brcm/board/common/board_common.mk
SOC_DIR := brcm/board/stingray
@ -58,6 +100,17 @@ PLAT_BL_COMMON_SOURCES += lib/cpus/aarch64/cortex_a72.S \
drivers/arm/tzc/tzc400.c \
plat/${SOC_DIR}/src/topology.c
BL2_SOURCES += plat/${SOC_DIR}/driver/ihost_pll_config.c \
plat/${SOC_DIR}/src/bl2_setup.c \
plat/${SOC_DIR}/driver/swreg.c
ifeq (${USE_DDR},yes)
PLAT_INCLUDES += -Iplat/${SOC_DIR}/driver/ddr/soc/include
else
PLAT_INCLUDES += -Iplat/${SOC_DIR}/driver/ext_sram_init
BL2_SOURCES += plat/${SOC_DIR}/driver/ext_sram_init/ext_sram_init.c
endif
# Include GICv3 driver files
include drivers/arm/gic/v3/gicv3.mk
@ -77,6 +130,12 @@ BL31_SOURCES += \
ifdef SCP_BL2
PLAT_INCLUDES += -Iplat/brcm/common/
BL2_SOURCES += plat/brcm/common/brcm_mhu.c \
plat/brcm/common/brcm_scpi.c \
plat/${SOC_DIR}/src/scp_utils.c \
plat/${SOC_DIR}/src/scp_cmd.c \
drivers/brcm/scp.c
BL31_SOURCES += plat/brcm/common/brcm_mhu.c \
plat/brcm/common/brcm_scpi.c \
plat/${SOC_DIR}/src/brcm_pm_ops.c
@ -85,5 +144,19 @@ BL31_SOURCES += plat/${SOC_DIR}/src/ihost_pm.c \
plat/${SOC_DIR}/src/pm.c
endif
ifeq (${ELOG_SUPPORT},1)
ifeq (${ELOG_STORE_MEDIA},DDR)
BL2_SOURCES += plat/brcm/board/common/bcm_elog_ddr.c
endif
endif
# Do not execute the startup code on warm reset.
PROGRAMMABLE_RESET_ADDRESS := 1
# Nitro FW, config and Crash log uses secure DDR memory
# Inaddition to above, Nitro master and slave is also secure
ifneq ($(NITRO_SECURE_ACCESS),)
$(eval $(call add_define,NITRO_SECURE_ACCESS))
$(eval $(call add_define,DDR_NITRO_SECURE_REGION_START))
$(eval $(call add_define,DDR_NITRO_SECURE_REGION_END))
endif

View File

@ -0,0 +1,742 @@
/*
* Copyright (c) 2016-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/arm/sp805.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>
#include <chimp.h>
#include <chip_id.h>
#include <cmn_plat_util.h>
#include <dmu.h>
#include <fru.h>
#ifdef USE_GPIO
#include <drivers/gpio.h>
#include <iproc_gpio.h>
#endif
#include <platform_def.h>
#include <sotp.h>
#include <swreg.h>
#include <sr_utils.h>
#ifdef USE_DDR
#include <ddr_init.h>
#else
#include <ext_sram_init.h>
#endif
#if DRIVER_OCOTP_ENABLE
#include <ocotp.h>
#endif
#include "board_info.h"
#define WORD_SIZE 8
#define SWREG_AVS_OTP_OFFSET (13 * WORD_SIZE) /* 13th row byte offset */
#define AON_GPIO_OTP_OFFSET (28 * WORD_SIZE) /* 28th row byte offset */
#define BYTES_TO_READ 8
/* OTP voltage step definitions */
#define MVOLT_STEP_MAX 0x18 /* 1v */
#define MVOLT_PER_STEP 10 /* 0.01mv per step */
#define MVOLT_BASE 760 /* 0.76v */
#define STEP_TO_UVOLTS(step) \
((MVOLT_BASE + (MVOLT_PER_STEP * (step))) * 1000)
#define GET_BITS(first, last, data) \
((data >> first) & ((1 << (last - first + 1)) - 1))
/*
* SW-REG OTP encoding:
*
* SWREG_bits[11:0] = OTP 13th row 12 bits[55:44]
* SWREG_bits[11:10] - Valid Bits (0x2 - valid, if not 0x2 - Invalid)
* SWREG_bits[9:5] - iHost03, iHost12
* SWREG_bits[4:0] - Core VDDC
*/
#define SWREG_OTP_BITS_START 12 /* 44th bit in MSB 32-bits */
#define SWREG_OTP_BITS_END 23 /* 55th bit in MSB 32-bits */
#define SWREG_VDDC_FIELD_START 0
#define SWREG_VDDC_FIELD_END 4
#define SWREG_IHOST_FIELD_START 5
#define SWREG_IHOST_FIELD_END 9
#define SWREG_VALID_BIT_START 10
#define SWREG_VALID_BIT_END 11
#define SWREG_VALID_BITS 0x2
/*
* Row 13 bit 56 is programmed as '1' today. It is not being used, so plan
* is to flip this bit to '0' for B1 rev. Hence SW can leverage this bit
* to identify Bx chip to program different sw-regulators.
*/
#define SPARE_BIT 24
#define IS_SR_B0(data) (((data) >> SPARE_BIT) & 0x1)
#if DRIVER_OCOTP_ENABLE
static struct otpc_map otp_stingray_map = {
.otpc_row_size = 2,
.data_r_offset = {0x10, 0x5c},
.data_w_offset = {0x2c, 0x64},
.word_size = 8,
.stride = 8,
};
#endif
void plat_bcm_bl2_early_platform_setup(void)
{
/* Select UART0 for AP via mux setting*/
if (PLAT_BRCM_BOOT_UART_BASE == UART0_BASE_ADDR) {
mmio_write_32(UART0_SIN_MODE_SEL_CONTROL, 1);
mmio_write_32(UART0_SOUT_MODE_SEL_CONTROL, 1);
}
}
#ifdef USE_NAND
static void brcm_stingray_nand_init(void)
{
unsigned int val;
unsigned int nand_idm_reset_control = 0x68e0a800;
VERBOSE(" stingray nand init start.\n");
/* Reset NAND */
VERBOSE(" - reset nand\n");
val = mmio_read_32((uintptr_t)(nand_idm_reset_control + 0x0));
mmio_write_32((uintptr_t)(nand_idm_reset_control + 0x0), val | 0x1);
udelay(500);
val = mmio_read_32((uintptr_t)(nand_idm_reset_control + 0x0));
mmio_write_32((uintptr_t)(nand_idm_reset_control + 0x0), val & ~0x1);
udelay(500);
VERBOSE(" stingray nand init done.\n");
}
#endif
#if defined(USE_PAXB) || defined(USE_PAXC) || defined(USE_SATA)
#define PCIE_RESCAL_CFG_0 0x40000130
#define PCIE_CFG_RESCAL_RSTB_R (1 << 16)
#define PCIE_CFG_RESCAL_PWRDNB_R (1 << 8)
#define PCIE_RESCAL_STATUS_0 0x4000014c
#define PCIE_STAT_PON_VALID_R (1 << 0)
#define PCIE_RESCAL_OUTPUT_STATUS 0x40000154
#define CDRU_PCIE_RESET_N_R (1 << CDRU_MISC_RESET_CONTROL__CDRU_PCIE_RESET_N_R)
#ifdef EMULATION_SETUP
static void brcm_stingray_pcie_reset(void)
{
}
#else
static void brcm_stingray_pcie_reset(void)
{
unsigned int data;
int try;
if (bcm_chimp_is_nic_mode()) {
INFO("NIC mode detected; PCIe reset/rescal not executed\n");
return;
}
mmio_clrbits_32(CDRU_MISC_RESET_CONTROL, CDRU_PCIE_RESET_N_R);
mmio_setbits_32(CDRU_MISC_RESET_CONTROL, CDRU_PCIE_RESET_N_R);
/* Release reset */
mmio_setbits_32(PCIE_RESCAL_CFG_0, PCIE_CFG_RESCAL_RSTB_R);
mdelay(1);
/* Power UP */
mmio_setbits_32(PCIE_RESCAL_CFG_0,
(PCIE_CFG_RESCAL_RSTB_R | PCIE_CFG_RESCAL_PWRDNB_R));
try = 1000;
do {
udelay(1);
data = mmio_read_32(PCIE_RESCAL_STATUS_0);
try--;
} while ((data & PCIE_STAT_PON_VALID_R) == 0x0 && (try > 0));
if (try <= 0)
ERROR("PCIE_RESCAL_STATUS_0: 0x%x\n", data);
VERBOSE("PCIE_SATA_RESCAL_STATUS_0 0x%x.\n",
mmio_read_32(PCIE_RESCAL_STATUS_0));
VERBOSE("PCIE_SATA_RESCAL_OUTPUT_STATUS 0x%x.\n",
mmio_read_32(PCIE_RESCAL_OUTPUT_STATUS));
INFO("PCIE SATA Rescal Init done\n");
}
#endif /* EMULATION_SETUP */
#endif /* USE_PAXB || USE_PAXC || USE_SATA */
#ifdef USE_PAXC
void brcm_stingray_chimp_check_and_fastboot(void)
{
int fastboot_init_result;
if (bcm_chimp_is_nic_mode())
/* Do not wait here */
return;
#if WARMBOOT_DDR_S3_SUPPORT
/*
* Currently DDR shmoo parameters and QSPI boot source are
* tied. DDR shmoo parameters are stored in QSPI, which is
* used for warmboot.
* Do not reset nitro for warmboot
*/
if (is_warmboot() && (boot_source_get() == BOOT_SOURCE_QSPI))
return;
#endif /* WARMBOOT_DDR_S3_SUPPORT */
/*
* Not in NIC mode,
* initiate fastboot (if enabled)
*/
if (FASTBOOT_TYPE == CHIMP_FASTBOOT_NITRO_RESET) {
VERBOSE("Bring up Nitro/ChiMP\n");
if (boot_source_get() == BOOT_SOURCE_QSPI)
WARN("Nitro boots from QSPI when AP has booted from QSPI.\n");
brcm_stingray_set_qspi_mux(0);
VERBOSE("Nitro controls the QSPI\n");
}
fastboot_init_result = bcm_chimp_initiate_fastboot(FASTBOOT_TYPE);
if (fastboot_init_result && boot_source_get() != BOOT_SOURCE_QSPI)
ERROR("Nitro init error %d. Status: 0x%x; bpe_mod reg: 0x%x\n"
"fastboot register: 0x%x; handshake register 0x%x\n",
fastboot_init_result,
bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_STAT_REG),
bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_MODE_REG),
bcm_chimp_read_ctrl(CHIMP_REG_CTRL_FSTBOOT_PTR_REG),
bcm_chimp_read(CHIMP_REG_ECO_RESERVED));
/*
* CRMU watchdog kicks is an example, which is L1 reset,
* does not clear Nitro scratch pad ram.
* For Nitro resets: Clear the Nitro health status memory.
*/
bcm_chimp_write((CHIMP_REG_CHIMP_SCPAD + CHIMP_HEALTH_STATUS_OFFSET),
0);
}
#endif
void set_ihost_vddc_swreg(uint32_t ihost_uvolts, uint32_t vddc_uvolts)
{
NOTICE("ihost_uvolts: %duv, vddc_uvolts: %duv\n",
ihost_uvolts, vddc_uvolts);
set_swreg(VDDC_CORE, vddc_uvolts);
set_swreg(IHOST03, ihost_uvolts);
set_swreg(IHOST12, ihost_uvolts);
}
/*
* Reads SWREG AVS OTP bits (13th row) with ECC enabled and get voltage
* defined in OTP if valid OTP is found
*/
void read_avs_otp_bits(uint32_t *ihost_uvolts, uint32_t *vddc_uvolts)
{
uint32_t offset = SWREG_AVS_OTP_OFFSET;
uint32_t ihost_step, vddc_step;
uint32_t avs_bits;
uint32_t buf[2];
if (bcm_otpc_read(offset, &buf[0], BYTES_TO_READ, 1) == -1)
return;
VERBOSE("AVS OTP %d ROW: 0x%x.0x%x\n",
offset/WORD_SIZE, buf[1], buf[0]);
/* get voltage readings from AVS OTP bits */
avs_bits = GET_BITS(SWREG_OTP_BITS_START,
SWREG_OTP_BITS_END,
buf[1]);
/* check for valid otp bits */
if (GET_BITS(SWREG_VALID_BIT_START, SWREG_VALID_BIT_END, avs_bits) !=
SWREG_VALID_BITS) {
WARN("Invalid AVS OTP bits at %d row\n", offset/WORD_SIZE);
return;
}
/* get ihost and vddc step value */
vddc_step = GET_BITS(SWREG_VDDC_FIELD_START,
SWREG_VDDC_FIELD_END,
avs_bits);
ihost_step = GET_BITS(SWREG_IHOST_FIELD_START,
SWREG_IHOST_FIELD_END,
avs_bits);
if ((ihost_step > MVOLT_STEP_MAX) || (vddc_step > MVOLT_STEP_MAX)) {
WARN("OTP entry invalid\n");
return;
}
/* get voltage in micro-volts */
*ihost_uvolts = STEP_TO_UVOLTS(ihost_step);
*vddc_uvolts = STEP_TO_UVOLTS(vddc_step);
}
/*
* This api reads otp bits and program internal swreg's - ihos12, ihost03,
* vddc_core and ddr_core based on different chip. External swreg's
* programming will be done from crmu.
*
* For A2 chip:
* Read OTP row 20, bit 50. This bit will be set for A2 chip. Once A2 chip is
* found, read AVS OTP row 13, 12bits[55:44], if valid otp bits are found
* then set ihost and vddc according to avs otp bits else set them to 0.94v
* and 0.91v respectively. Also update the firmware after setting voltage.
*
* For B0 chip:
* Read OTP row 13, bit 56. This bit will be set for B0 chip. Once B0 chip is
* found then set ihost and vddc to 0.95v and ddr_core to 1v. No AVS OTP bits
* are used get ihost/vddc voltages.
*
* For B1 chip:
* Read AVS OTP row 13, 12bits[55:44], if valid otp bits are found then set
* ihost and vddc according to avs otp bits else set them to 0.94v and 0.91v
* respectively.
*/
void set_swreg_based_on_otp(void)
{
/* default voltage if no valid OTP */
uint32_t vddc_uvolts = VDDC_CORE_DEF_VOLT;
uint32_t ihost_uvolts = IHOST_DEF_VOLT;
uint32_t ddrc_uvolts;
uint32_t offset;
uint32_t buf[2];
offset = SWREG_AVS_OTP_OFFSET;
if (bcm_otpc_read(offset, &buf[0], BYTES_TO_READ, 1) == -1)
return;
VERBOSE("OTP %d ROW: 0x%x.0x%x\n",
offset/WORD_SIZE, buf[1], buf[0]);
if (IS_SR_B0(buf[1])) {
/* don't read AVS OTP for B0 */
ihost_uvolts = B0_IHOST_DEF_VOLT;
vddc_uvolts = B0_VDDC_CORE_DEF_VOLT;
ddrc_uvolts = B0_DDR_VDDC_DEF_VOLT;
} else {
read_avs_otp_bits(&ihost_uvolts, &vddc_uvolts);
}
#if (IHOST_REG_TYPE == IHOST_REG_INTEGRATED) && \
(VDDC_REG_TYPE == VDDC_REG_INTEGRATED)
/* enable IHOST12 cluster before changing voltage */
NOTICE("Switching on the Regulator idx: %u\n",
SWREG_IHOST1_DIS);
mmio_clrsetbits_32(CRMU_SWREG_CTRL_ADDR,
BIT(SWREG_IHOST1_DIS),
BIT(SWREG_IHOST1_REG_RESETB));
/* wait for regulator supply gets stable */
while (!(mmio_read_32(CRMU_SWREG_STATUS_ADDR) &
(1 << SWREG_IHOST1_PMU_STABLE)))
;
INFO("Regulator supply got stable\n");
#ifndef DEFAULT_SWREG_CONFIG
swreg_firmware_update();
#endif
set_ihost_vddc_swreg(ihost_uvolts, vddc_uvolts);
#endif
if (IS_SR_B0(buf[1])) {
NOTICE("ddrc_uvolts: %duv\n", ddrc_uvolts);
set_swreg(DDR_VDDC, ddrc_uvolts);
}
}
#ifdef USE_DDR
static struct ddr_info ddr_info;
#endif
#ifdef USE_FRU
static struct fru_area_info fru_area[FRU_MAX_NR_AREAS];
static struct fru_board_info board_info;
static struct fru_time fru_tm;
static uint8_t fru_tbl[BCM_MAX_FRU_LEN];
static void board_detect_fru(void)
{
uint32_t i, result;
int ret = -1;
result = bcm_emmc_init(false);
if (!result) {
ERROR("eMMC init failed\n");
return;
}
/* go through eMMC boot partitions looking for FRU table */
for (i = EMMC_BOOT_PARTITION1; i <= EMMC_BOOT_PARTITION2; i++) {
result = emmc_partition_select(i);
if (!result) {
ERROR("Switching to eMMC part %u failed\n", i);
return;
}
result = emmc_read(BCM_FRU_TBL_OFFSET, (uintptr_t)fru_tbl,
BCM_MAX_FRU_LEN, BCM_MAX_FRU_LEN);
if (!result) {
ERROR("Failed to read from eMMC part %u\n", i);
return;
}
/*
* Run sanity check and checksum to make sure valid FRU table
* is detected
*/
ret = fru_validate(fru_tbl, fru_area);
if (ret < 0) {
WARN("FRU table not found in eMMC part %u\n", i);
continue;
}
/* parse DDR information from FRU table */
ret = fru_parse_ddr(fru_tbl, &fru_area[FRU_AREA_INTERNAL],
&ddr_info);
if (ret < 0) {
WARN("No FRU DDR info found in eMMC part %u\n", i);
continue;
}
/* parse board information from FRU table */
ret = fru_parse_board(fru_tbl, &fru_area[FRU_AREA_BOARD_INFO],
&board_info);
if (ret < 0) {
WARN("No FRU board info found in eMMC part %u\n", i);
continue;
}
/* if we reach here, valid FRU table is parsed */
break;
}
if (ret < 0) {
WARN("FRU table missing for this board\n");
return;
}
for (i = 0; i < BCM_MAX_NR_DDR; i++) {
INFO("DDR channel index: %d\n", ddr_info.mcb[i].idx);
INFO("DDR size %u GB\n", ddr_info.mcb[i].size_mb / 1024);
INFO("DDR ref ID by SW (Not MCB Ref ID) 0x%x\n",
ddr_info.mcb[i].ref_id);
}
fru_format_time(board_info.mfg_date, &fru_tm);
INFO("**** FRU board information ****\n");
INFO("Language 0x%x\n", board_info.lang);
INFO("Manufacturing Date %u.%02u.%02u, %02u:%02u\n",
fru_tm.year, fru_tm.month, fru_tm.day,
fru_tm.hour, fru_tm.min);
INFO("Manufacturing Date(Raw) 0x%x\n", board_info.mfg_date);
INFO("Manufacturer %s\n", board_info.manufacturer);
INFO("Product Name %s\n", board_info.product_name);
INFO("Serial number %s\n", board_info.serial_number);
INFO("Part number %s\n", board_info.part_number);
INFO("File ID %s\n", board_info.file_id);
}
#endif /* USE_FRU */
#ifdef USE_GPIO
#define INVALID_GPIO 0xffff
static const int gpio_cfg_bitmap[MAX_NR_GPIOS] = {
#ifdef BRD_DETECT_GPIO_BIT0
BRD_DETECT_GPIO_BIT0,
#else
INVALID_GPIO,
#endif
#ifdef BRD_DETECT_GPIO_BIT1
BRD_DETECT_GPIO_BIT1,
#else
INVALID_GPIO,
#endif
#ifdef BRD_DETECT_GPIO_BIT2
BRD_DETECT_GPIO_BIT2,
#else
INVALID_GPIO,
#endif
#ifdef BRD_DETECT_GPIO_BIT3
BRD_DETECT_GPIO_BIT3,
#else
INVALID_GPIO,
#endif
};
static uint8_t gpio_bitmap;
/*
* Use an odd number to avoid potential conflict with public GPIO level
* defines
*/
#define GPIO_STATE_FLOAT 15
/*
* If GPIO_SUPPORT_FLOAT_DETECTION is disabled, simply return GPIO level
*
* If GPIO_SUPPORT_FLOAT_DETECTION is enabled, add additional test for possible
* pin floating (unconnected) scenario. This support is assuming externally
* applied pull up / pull down will have a stronger pull than the internal pull
* up / pull down.
*/
static uint8_t gpio_get_state(int gpio)
{
uint8_t val;
/* set direction to GPIO input */
gpio_set_direction(gpio, GPIO_DIR_IN);
#ifndef GPIO_SUPPORT_FLOAT_DETECTION
if (gpio_get_value(gpio) == GPIO_LEVEL_HIGH)
val = GPIO_LEVEL_HIGH;
else
val = GPIO_LEVEL_LOW;
return val;
#else
/*
* Enable internal pull down. If GPIO level is still high, there must
* be an external pull up
*/
gpio_set_pull(gpio, GPIO_PULL_DOWN);
if (gpio_get_value(gpio) == GPIO_LEVEL_HIGH) {
val = GPIO_LEVEL_HIGH;
goto exit;
}
/*
* Enable internal pull up. If GPIO level is still low, there must
* be an external pull down
*/
gpio_set_pull(gpio, GPIO_PULL_UP);
if (gpio_get_value(gpio) == GPIO_LEVEL_LOW) {
val = GPIO_LEVEL_LOW;
goto exit;
}
/* if reached here, the pin must be not connected */
val = GPIO_STATE_FLOAT;
exit:
/* make sure internall pull is disabled */
if (gpio_get_pull(gpio) != GPIO_PULL_NONE)
gpio_set_pull(gpio, GPIO_PULL_NONE);
return val;
#endif
}
static void board_detect_gpio(void)
{
unsigned int i, val;
int gpio;
iproc_gpio_init(IPROC_GPIO_S_BASE, IPROC_GPIO_NR,
IPROC_IOPAD_MODE_BASE, HSLS_IOPAD_BASE);
gpio_bitmap = 0;
for (i = 0; i < MAX_NR_GPIOS; i++) {
if (gpio_cfg_bitmap[i] == INVALID_GPIO)
continue;
/*
* Construct the bitmap based on GPIO value. Floating pin
* detection is a special case. As soon as a floating pin is
* detected, a special value of MAX_GPIO_BITMAP_VAL is
* assigned and we break out of the loop immediately
*/
gpio = gpio_cfg_bitmap[i];
val = gpio_get_state(gpio);
if (val == GPIO_STATE_FLOAT) {
gpio_bitmap = MAX_GPIO_BITMAP_VAL;
break;
}
if (val == GPIO_LEVEL_HIGH)
gpio_bitmap |= BIT(i);
}
memcpy(&ddr_info, &gpio_ddr_info[gpio_bitmap], sizeof(ddr_info));
INFO("Board detection GPIO bitmap = 0x%x\n", gpio_bitmap);
}
#endif /* USE_GPIO */
static void bcm_board_detect(void)
{
#ifdef DDR_LEGACY_MCB_SUPPORTED
/* Loading default DDR info */
memcpy(&ddr_info, &default_ddr_info, sizeof(ddr_info));
#endif
#ifdef USE_FRU
board_detect_fru();
#endif
#ifdef USE_GPIO
board_detect_gpio();
#endif
}
static void dump_persistent_regs(void)
{
NOTICE("pr0: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG0));
NOTICE("pr1: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG1));
NOTICE("pr2: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG2));
NOTICE("pr3: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG3));
NOTICE("pr4: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG4));
NOTICE("pr5: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG5));
NOTICE("pr6: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG6));
NOTICE("pr7: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG7));
NOTICE("pr8: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG8));
NOTICE("pr9: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG9));
NOTICE("pr10: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG10));
NOTICE("pr11: %x\n", mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG11));
}
void plat_bcm_bl2_plat_arch_setup(void)
{
if (chip_get_rev_id_major() == CHIP_REV_MAJOR_AX) {
if (!(sotp_mem_read(SOTP_ATF_CFG_ROW_ID, SOTP_ROW_NO_ECC) &
SOTP_ATF_WATCHDOG_ENABLE_MASK)) {
/*
* Stop sp805 watchdog timer immediately.
* It might has been set up by MCU patch earlier for
* eMMC workaround.
*
* Note the watchdog timer started in CRMU has a very
* short timeout and needs to be stopped immediately.
* Down below we restart it with a much longer timeout
* for BL2 and BL31
*/
sp805_stop(ARM_SP805_TWDG_BASE);
}
}
#if !BRCM_DISABLE_TRUSTED_WDOG
/*
* start secure watchdog for BL2 and BL31.
* Note that UART download can take a longer time,
* so do not allow watchdog for UART download,
* as this boot source is not a standard modus operandi.
*/
if (boot_source_get() != BOOT_SOURCE_UART)
sp805_start(ARM_SP805_TWDG_BASE, ARM_TWDG_LOAD_VAL);
#endif
#ifdef BCM_ELOG
/* Ensure logging is started out fresh in BL2. */
mmio_write_32(BCM_ELOG_BL2_BASE, 0);
#endif
/*
* In BL2, since we have very limited space to store logs, we only
* save logs that are >= the WARNING level.
*/
bcm_elog_init((void *)BCM_ELOG_BL2_BASE, BCM_ELOG_BL2_SIZE,
LOG_LEVEL_WARNING);
dump_persistent_regs();
/* Read CRMU mailbox 0 */
NOTICE("RESET (reported by CRMU): 0x%x\n",
mmio_read_32(CRMU_READ_MAIL_BOX0));
/*
* All non-boot-source PADs are in forced input-mode at
* reset so clear the force on non-boot-source PADs using
* CDRU register.
*/
mmio_clrbits_32((uintptr_t)CDRU_CHIP_IO_PAD_CONTROL,
(1 << CDRU_CHIP_IO_PAD_CONTROL__CDRU_IOMUX_FORCE_PAD_IN_R));
#if DRIVER_OCOTP_ENABLE
bcm_otpc_init(&otp_stingray_map);
#endif
set_swreg_based_on_otp();
#if IHOST_PLL_FREQ != 0
bcm_set_ihost_pll_freq(0x0, IHOST_PLL_FREQ);
#endif
#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
/* The erasable unit of the eMMC is the "Erase Group";
* Erase group is measured in write blocks which are the
* basic writable units of the Device.
* The size of the Erase Group is a Device specific parameter
*/
emmc_erase(EMMC_ERASE_START_BLOCK, EMMC_ERASE_BLOCK_COUNT,
EMMC_ERASE_PARTITION);
#endif
bcm_board_detect();
#ifdef DRIVER_EMMC_ENABLE
/* Initialize the card, if it is not */
if (bcm_emmc_init(true) < 0)
WARN("eMMC Card Initialization Failed!!!\n");
#endif
#if BL2_TEST_I2C
i2c_test();
#endif
#ifdef USE_DDR
ddr_initialize(&ddr_info);
ddr_secure_region_config(SECURE_DDR_BASE_ADDRESS,
SECURE_DDR_END_ADDRESS);
#ifdef NITRO_SECURE_ACCESS
ddr_secure_region_config(DDR_NITRO_SECURE_REGION_START,
DDR_NITRO_SECURE_REGION_END);
#endif
#else
ext_sram_init();
#endif
#if BL2_TEST_MEM
ddr_test();
#endif
#ifdef USE_NAND
brcm_stingray_nand_init();
#endif
#if defined(USE_PAXB) || defined(USE_PAXC) || defined(USE_SATA)
brcm_stingray_pcie_reset();
#endif
#ifdef USE_PAXC
if (boot_source_get() != BOOT_SOURCE_QSPI)
brcm_stingray_chimp_check_and_fastboot();
#endif
#if ((!CLEAN_DDR || MMU_DISABLED))
/*
* Now DDR has been initialized. We want to copy all the logs in SRAM
* into DDR so we will have much more space to store the logs in the
* next boot stage
*/
bcm_elog_copy_log((void *)BCM_ELOG_BL31_BASE,
MIN(BCM_ELOG_BL2_SIZE, BCM_ELOG_BL31_SIZE)
);
/*
* We are not yet at the end of BL2, but we can stop log here so we do
* not need to add 'bcm_elog_exit' to the standard BL2 code. The
* benefit of capturing BL2 logs after this is very minimal in a
* production system
* NOTE: BL2 logging must be exited before going forward to setup
* page tables
*/
bcm_elog_exit();
#endif
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 2017-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <drivers/delay_timer.h>
#include <platform_def.h>
#include <scp.h>
#include <scp_cmd.h>
#include "m0_ipc.h"
/*
* Reads a response from CRMU MAILBOX
* Assumes that access has been granted and locked.
* Note that this is just a temporary implementation until
* channels are introduced
*/
static void scp_read_response(crmu_response_t *resp)
{
uint32_t code;
code = mmio_read_32(CRMU_MAIL_BOX0);
resp->completed = code & MCU_IPC_CMD_DONE_MASK;
resp->cmd = code & SCP_CMD_MASK;
resp->ret = (code & MCU_IPC_CMD_REPLY_MASK) >> MCU_IPC_CMD_REPLY_SHIFT;
}
/*
* Send a command to SCP and wait for timeout us.
* Return: 0 on success
* -1 if there was no proper reply from SCP
* >0 if there was a response from MCU, but
* command completed with an error.
*/
int scp_send_cmd(uint32_t cmd, uint32_t param, uint32_t timeout)
{
int ret = -1;
mmio_write_32(CRMU_MAIL_BOX0, cmd);
mmio_write_32(CRMU_MAIL_BOX1, param);
do {
crmu_response_t scp_resp;
udelay(1);
scp_read_response(&scp_resp);
if (scp_resp.completed &&
(scp_resp.cmd == cmd)) {
/* This command has completed */
ret = scp_resp.ret;
break;
}
} while (--timeout);
return ret;
}

View File

@ -0,0 +1,227 @@
/*
* Copyright (c) 2017-2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <arch_helpers.h>
#include <common/bl_common.h>
#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <bcm_elog_ddr.h>
#include <brcm_mhu.h>
#include <brcm_scpi.h>
#include <chimp.h>
#include <cmn_plat_util.h>
#include <ddr_init.h>
#include <scp.h>
#include <scp_cmd.h>
#include <scp_utils.h>
#include "m0_cfg.h"
#include "m0_ipc.h"
#ifdef BCM_ELOG
static void prepare_elog(void)
{
#if (CLEAN_DDR && !defined(MMU_DISABLED))
/*
* Now DDR has been initialized. We want to copy all the logs in SRAM
* into DDR so we will have much more space to store the logs in the
* next boot stage
*/
bcm_elog_copy_log((void *)BCM_ELOG_BL31_BASE,
MIN(BCM_ELOG_BL2_SIZE, BCM_ELOG_BL31_SIZE)
);
/*
* We are almost at the end of BL2, and we can stop log here so we do
* not need to add 'bcm_elog_exit' to the standard BL2 code. The
* benefit of capturing BL2 logs after this is very minimal in a
* production system.
*/
bcm_elog_exit();
#endif
/*
* Notify CRMU that now it should pull logs from DDR instead of from
* FS4 SRAM.
*/
SCP_WRITE_CFG(flash_log.can_use_ddr, 1);
}
#endif
bool is_crmu_alive(void)
{
return (scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0, SCP_CMD_DEFAULT_TIMEOUT_US)
== 0);
}
bool bcm_scp_issue_sys_reset(void)
{
return (scp_send_cmd(MCU_IPC_MCU_CMD_L1_RESET, 0,
SCP_CMD_DEFAULT_TIMEOUT_US));
}
/*
* Note that this is just a temporary implementation until
* channels are introduced
*/
int plat_bcm_bl2_plat_handle_scp_bl2(image_info_t *scp_bl2_image_info)
{
int scp_patch_activated, scp_patch_version;
#ifndef EMULATION_SETUP
uint8_t active_ch_bitmap, i;
#endif
uint32_t reset_state = 0;
uint32_t mcu_ap_init_param = 0;
/*
* First check if SCP patch has already been loaded
* Send NOP command and see if there is a valid response
*/
scp_patch_activated =
(scp_send_cmd(MCU_IPC_MCU_CMD_NOP, 0,
SCP_CMD_DEFAULT_TIMEOUT_US) == 0);
if (scp_patch_activated) {
INFO("SCP Patch is already active.\n");
reset_state = SCP_READ_CFG(board_cfg.reset_state);
mcu_ap_init_param = SCP_READ_CFG(board_cfg.mcu_init_param);
/* Clear reset state, it's been already read */
SCP_WRITE_CFG(board_cfg.reset_state, 0);
if (mcu_ap_init_param & MCU_PATCH_LOADED_BY_NITRO) {
/*
* Reset "MCU_PATCH_LOADED_BY_NITRO" flag, but
* Preserve any other flags we don't deal with here
*/
INFO("AP booted by Nitro\n");
SCP_WRITE_CFG(
board_cfg.mcu_init_param,
mcu_ap_init_param &
~MCU_PATCH_LOADED_BY_NITRO
);
}
} else {
/*
* MCU Patch not loaded, so load it.
* MCU patch stamps critical points in REG9 (debug test-point)
* Display its last content here. This helps to locate
* where crash occurred if a CRMU watchdog kicked in.
*/
int ret;
INFO("MCU Patch Point: 0x%x\n",
mmio_read_32(CRMU_IHOST_SW_PERSISTENT_REG9));
ret = download_scp_patch((void *)scp_bl2_image_info->image_base,
scp_bl2_image_info->image_size);
if (ret != 0)
return ret;
VERBOSE("SCP Patch loaded OK.\n");
ret = scp_send_cmd(MCU_IPC_MCU_CMD_INIT,
MCU_PATCH_LOADED_BY_AP,
SCP_CMD_SCP_BOOT_TIMEOUT_US);
if (ret) {
ERROR("SCP Patch could not initialize; error %d\n",
ret);
return ret;
}
INFO("SCP Patch successfully initialized.\n");
}
scp_patch_version = scp_send_cmd(MCU_IPC_MCU_CMD_GET_FW_VERSION, 0,
SCP_CMD_DEFAULT_TIMEOUT_US);
INFO("SCP Patch version :0x%x\n", scp_patch_version);
/* Next block just reports current AVS voltages (if applicable) */
{
uint16_t vcore_mv, ihost03_mv, ihost12_mv;
vcore_mv = SCP_READ_CFG16(vcore.millivolts) +
SCP_READ_CFG8(vcore.avs_cfg.additive_margin);
ihost03_mv = SCP_READ_CFG16(ihost03.millivolts) +
SCP_READ_CFG8(ihost03.avs_cfg.additive_margin);
ihost12_mv = SCP_READ_CFG16(ihost12.millivolts) +
SCP_READ_CFG8(ihost12.avs_cfg.additive_margin);
if (vcore_mv || ihost03_mv || ihost12_mv) {
INFO("AVS voltages from cfg (including margin)\n");
if (vcore_mv > 0)
INFO("%s\tVCORE: %dmv\n",
SCP_READ_CFG8(vcore.avs_cfg.avs_set) ?
"*" : "n/a", vcore_mv);
if (ihost03_mv > 0)
INFO("%s\tIHOST03: %dmv\n",
SCP_READ_CFG8(ihost03.avs_cfg.avs_set) ?
"*" : "n/a", ihost03_mv);
if (ihost12_mv > 0)
INFO("%s\tIHOST12: %dmv\n",
SCP_READ_CFG8(ihost12.avs_cfg.avs_set) ?
"*" : "n/a", ihost12_mv);
} else {
INFO("AVS settings not applicable\n");
}
}
#if (CLEAN_DDR && !defined(MMU_DISABLED) && !defined(EMULATION_SETUP))
/* This will clean the DDR and enable ECC if set */
check_ddr_clean();
#endif
#if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR)
elog_init_ddr_log();
#endif
#ifdef BCM_ELOG
/* Prepare ELOG to use DDR */
prepare_elog();
#endif
#ifndef EMULATION_SETUP
/* Ask ddr_init to save obtained DDR information into DDR */
ddr_info_save();
#endif
/*
* Configure TMON DDR address.
* This cfg is common for all cases
*/
SCP_WRITE_CFG(tmon_cfg.ddr_desc, TMON_SHARED_DDR_ADDRESS);
if (reset_state == SOFT_RESET_L3 && !mcu_ap_init_param) {
INFO("SCP configuration after L3 RESET done.\n");
return 0;
}
if (bcm_chimp_is_nic_mode())
/* Configure AP WDT to not reset the NIC interface */
SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3);
#if (WARMBOOT_DDR_S3_SUPPORT && ELOG_STORE_MEDIA_DDR)
/* When AP WDog triggers perform L3 reset if DDR err logging enabled */
SCP_WRITE_CFG(board_cfg.apwdt_reset_type, SOFT_RESET_L3);
#endif
#ifndef EMULATION_SETUP
#ifdef DDR_SCRUB_ENA
ddr_scrub_enable();
#endif
/* Fill the Active channel information */
active_ch_bitmap = get_active_ddr_channel();
for (i = 0; i < MAX_NR_DDR_CH; i++)
SCP_WRITE_CFG(ddr_cfg.ddr_cfg[i],
(active_ch_bitmap & BIT(i)) ? 1 : 0);
#endif
return 0;
}