Commit 926cd70a authored by Manish Pandey's avatar Manish Pandey Committed by TrustedFirmware Code Review
Browse files

Merge changes from topic "brcm_initial_support" into integration

* changes:
  doc: brcm: Add documentation file for brcm stingray platform
  drivers: Add SPI Nor flash support
  drivers: Add iproc spi driver
  drivers: Add emmc driver for Broadcom platforms
  Add BL31 support for Broadcom stingray platform
  Add BL2 support for Broadcom stingray platform
  Add bl31 support common across Broadcom platforms
  Add bl2 setup code common across Broadcom platforms
  drivers: Add support to retrieve plat_toc_flags
parents 33f1dd9c fd1017b1
Description
===========
Broadcom's Stingray(BCM958742t) is a multi-core processor with 8 Cortex-A72 cores.
Trusted Firmware-A (TF-A) is used to implement secure world firmware, supporting
BL2 and BL31 for Broadcom Stingray SoCs
On Poweron, Boot ROM will load bl2 image and Bl2 will initialize the hardware,
then loads bl31 and bl33 into DDR and boots to bl33.
Boot Sequence
=============
Bootrom --> TF-A BL2 --> TF-A BL31 --> BL33(u-boot)
Code Locations
--------------
- Trusted Firmware-A:
`link <https://github.com/ARM-software/arm-trusted-firmware>`__
How to build
============
Build Procedure
---------------
- Prepare AARCH64 toolchain.
- Build u-boot first, and get the binary image: u-boot.bin,
- Build TF-A
Build fip:
.. code::shell
make CROSS_COMPILE=aarch64-linux-gnu- PLAT=stingray BOARD_CFG=bcm958742t all fip BL33=u-boot.bin
Deploy TF-A Images
-----------------
The u-boot will be upstreamed soon, this doc will be updated once they are ready, and the link will be posted.
/*
* Copyright (c) 2016 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <drivers/delay_timer.h>
#include <chimp.h>
#include <chimp_nv_defs.h>
#define CHIMP_DEFAULT_STARTUP_ADDR 0xb4300000
/* ChiMP's view of APE scratchpad memory for fastboot */
#define CHIMP_FASTBOOT_ADDR 0x61000000
#define CHIMP_PREPARE_ACCESS_WINDOW(addr) \
(\
mmio_write_32(\
NIC400_NITRO_CHIMP_S_IDM_IO_CONTROL_DIRECT, \
addr & 0xffc00000)\
)
#define CHIMP_INDIRECT_TGT_ADDR(addr) \
(CHIMP_INDIRECT_BASE + (addr & CHIMP_INDIRECT_ADDR_MASK))
#define CHIMP_CTRL_ADDR(x) (CHIMP_REG_CTRL_BASE + x)
/* For non-PAXC builds */
#ifndef CHIMP_FB1_ENTRY
#define CHIMP_FB1_ENTRY 0
#endif
#define CHIMP_DBG VERBOSE
void bcm_chimp_write(uintptr_t addr, uint32_t value)
{
CHIMP_PREPARE_ACCESS_WINDOW(addr);
mmio_write_32(CHIMP_INDIRECT_TGT_ADDR(addr), value);
}
uint32_t bcm_chimp_read(uintptr_t addr)
{
CHIMP_PREPARE_ACCESS_WINDOW(addr);
return mmio_read_32(CHIMP_INDIRECT_TGT_ADDR(addr));
}
void bcm_chimp_clrbits(uintptr_t addr, uint32_t bits)
{
CHIMP_PREPARE_ACCESS_WINDOW(addr);
mmio_clrbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
}
void bcm_chimp_setbits(uintptr_t addr, uint32_t bits)
{
CHIMP_PREPARE_ACCESS_WINDOW(addr);
mmio_setbits_32(CHIMP_INDIRECT_TGT_ADDR(addr), bits);
}
int bcm_chimp_is_nic_mode(void)
{
uint32_t val;
/* Check if ChiMP straps are set */
val = mmio_read_32(CDRU_CHIP_STRAP_DATA_LSW);
val &= CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;
return val == CDRU_CHIP_STRAP_DATA_LSW__NIC_MODE_MASK;
}
void bcm_chimp_fru_prog_done(bool is_done)
{
uint32_t val;
val = is_done ? (1 << CHIMP_FRU_PROG_DONE_BIT) : 0;
bcm_chimp_setbits(CHIMP_REG_ECO_RESERVED, val);
}
int bcm_chimp_handshake_done(void)
{
uint32_t value;
value = bcm_chimp_read(CHIMP_REG_ECO_RESERVED);
value &= (1 << CHIMP_FLASH_ACCESS_DONE_BIT);
return value != 0;
}
int bcm_chimp_wait_handshake(void)
{
uint32_t timeout = CHIMP_HANDSHAKE_TIMEOUT_MS;
uint32_t status;
INFO("Waiting for ChiMP handshake...\n");
do {
if (bcm_chimp_handshake_done())
break;
/* No need to wait if ChiMP reported an error */
status = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_STAT_REG);
if (status & CHIMP_ERROR_MASK) {
ERROR("ChiMP error 0x%x. Wait aborted\n", status);
break;
}
mdelay(1);
} while (--timeout);
if (!bcm_chimp_handshake_done()) {
if (timeout == 0) {
WARN("Timeout waiting for ChiMP handshake\n");
}
} else {
INFO("Got handshake from ChiMP!\n");
}
return bcm_chimp_handshake_done();
}
uint32_t bcm_chimp_read_ctrl(uint32_t offset)
{
return bcm_chimp_read(CHIMP_CTRL_ADDR(offset));
}
static int bcm_chimp_nitro_reset(void)
{
uint32_t timeout;
/* Perform tasks done by M0 in NIC mode */
CHIMP_DBG("Taking Nitro out of reset\n");
mmio_setbits_32(CDRU_MISC_RESET_CONTROL,
/* MHB_RESET_N */
(1 << CDRU_MISC_RESET_CONTROL__CDRU_MHB_RESET_N_R) |
/* PCI_RESET_N */
(1 << CDRU_MISC_RESET_CONTROL__CDRU_PCIE_RESET_N_R) |
/* PM_RESET_N */
(1 << CDRU_MISC_RESET_CONTROL__CDRU_PM_RESET_N_R) |
/* NIC_RESET_N */
(1 << CDRU_MISC_RESET_CONTROL__CDRU_NITRO_RESET_N_R)
);
/* Wait until Nitro is out of reset */
timeout = NIC_RESET_RELEASE_TIMEOUT_US;
do {
uint32_t value;
value = bcm_chimp_read_ctrl(CHIMP_REG_CTRL_BPE_MODE_REG);
if ((value & CHIMP_BPE_MODE_ID_MASK) ==
CHIMP_BPE_MODE_ID_PATTERN)
break;
udelay(1);
} while (--timeout);
if (timeout == 0) {
ERROR("NIC reset release timed out\n");
return -1;
}
return 0;
}
static void bcm_nitro_secure_mode_enable(void)
{
mmio_setbits_32(CDRU_NITRO_CONTROL,
(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_MODE_R) |
(1 << CDRU_NITRO_CONTROL__CDRU_NITRO_SEC_OVERRIDE_R));
mmio_write_32(NITRO_TZPC_TZPCDECPROT0clr,
/* NITRO_TZPC */
1 << NITRO_TZPC_TZPCDECPROT0clr__DECPROT0_chimp_m_clr_R);
}
static int bcm_chimp_reset_and_initial_setup(void)
{
int err;
uint32_t handshake_reg;
err = bcm_chimp_nitro_reset();
if (err)
return err;
/* Enable Nitro secure mode */
bcm_nitro_secure_mode_enable();
/* Force ChiMP back into reset */
bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);
handshake_reg = (1 << SR_IN_SMARTNIC_MODE_BIT);
/* Get OTP secure Chimp boot status */
if (mmio_read_32(CRMU_OTP_STATUS) & (1 << CRMU_OTP_STATUS_BIT))
handshake_reg |= (1 << SR_CHIMP_SECURE_BOOT_BIT);
bcm_chimp_write(CHIMP_REG_ECO_RESERVED, handshake_reg);
CHIMP_DBG("ChiMP reset and initial handshake parameters set\n");
return 0;
}
static void bcm_nitro_chimp_release_reset(void)
{
bcm_chimp_clrbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
1 << CHIMP_REG_CHIMP_REG_CTRL_BPE_MODE_REG__cm3_rst_R);
CHIMP_DBG("Nitro Reset Released\n");
}
static void bcm_chimp_set_fastboot(int mode)
{
uint32_t fb_entry;
/* 1. Enable fastboot */
bcm_chimp_setbits(CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_BPE_MODE_REG),
(1 << CHIMP_FAST_BOOT_MODE_BIT));
fb_entry = CHIMP_FASTBOOT_ADDR | mode;
if (mode == CHIMP_FASTBOOT_JUMP_IN_PLACE)
fb_entry = CHIMP_FB1_ENTRY;
/* 2. Write startup address and mode */
INFO("Setting fastboot type %d entry to 0x%x\n", mode, fb_entry);
bcm_chimp_write(
CHIMP_CTRL_ADDR(CHIMP_REG_CTRL_FSTBOOT_PTR_REG),
fb_entry);
}
#ifndef CHIMPFW_USE_SIDELOAD
static void bcm_chimp_load_fw_from_spi(uintptr_t spi_addr, size_t size)
{
uintptr_t ape_scpad;
uintptr_t dest;
size_t bytes_left;
ape_scpad = CHIMP_REG_CHIMP_APE_SCPAD;
dest = CHIMP_INDIRECT_TGT_ADDR(CHIMP_REG_CHIMP_APE_SCPAD);
bytes_left = size;
while (bytes_left) {
uint32_t delta;
delta = bytes_left > CHIMP_WINDOW_SIZE ?
bytes_left - CHIMP_WINDOW_SIZE : bytes_left;
CHIMP_PREPARE_ACCESS_WINDOW(ape_scpad);
INFO("Transferring %d byte(s) from 0x%lx to 0x%lx\n",
delta, spi_addr, dest);
/*
* This single memcpy call takes significant amount of time
* on Palladium. Be patient
*/
memcpy((void *)dest, (void *)spi_addr, delta);
bytes_left -= delta;
INFO("Transferred %d byte(s) from 0x%lx to 0x%lx (%lu%%)\n",
delta, spi_addr, dest,
((size - bytes_left) * 100)/size);
spi_addr += delta;
dest += delta;
ape_scpad += delta;
}
}
static int bcm_chimp_find_fw_in_spi(uintptr_t *addr, size_t *size)
{
int i;
bnxnvm_master_block_header_t *master_block_hdr;
bnxnvm_directory_block_header_t *dir_block_hdr;
bnxnvm_directory_entry_t *dir_entry;
int found;
found = 0;
/* Read the master block */
master_block_hdr =
(bnxnvm_master_block_header_t *)(uintptr_t)QSPI_BASE_ADDR;
if (master_block_hdr->sig != BNXNVM_MASTER_BLOCK_SIG) {
WARN("Invalid masterblock 0x%x (expected 0x%x)\n",
master_block_hdr->sig,
BNXNVM_MASTER_BLOCK_SIG);
return -NV_NOT_NVRAM;
}
if ((master_block_hdr->block_size > NV_MAX_BLOCK_SIZE) ||
(master_block_hdr->directory_offset >=
master_block_hdr->nvram_size)) {
WARN("Invalid masterblock block size 0x%x or directory offset 0x%x\n",
master_block_hdr->block_size,
master_block_hdr->directory_offset);
return -NV_BAD_MB;
}
/* Skip to the Directory block start */
dir_block_hdr =
(bnxnvm_directory_block_header_t *)
((uintptr_t)QSPI_BASE_ADDR +
master_block_hdr->directory_offset);
if (dir_block_hdr->sig != BNXNVM_DIRECTORY_BLOCK_SIG) {
WARN("Invalid directory header 0x%x (expected 0x%x)\n",
dir_block_hdr->sig,
BNXNVM_DIRECTORY_BLOCK_SIG);
return -NV_BAD_DIR_HEADER;
}
/* Locate the firmware */
for (i = 0; i < dir_block_hdr->entries; i++) {
*addr = ((uintptr_t)dir_block_hdr + dir_block_hdr->length +
i * dir_block_hdr->entry_length);
dir_entry = (bnxnvm_directory_entry_t *)(*addr);
if ((dir_entry->type == BNX_DIR_TYPE_BOOTCODE) ||
(dir_entry->type == BNX_DIR_TYPE_BOOTCODE_2)) {
found = 1;
break;
}
}
if (!found)
return -NV_FW_NOT_FOUND;
*addr = QSPI_BASE_ADDR + dir_entry->item_location;
*size = dir_entry->data_length;
INFO("Found chimp firmware at 0x%lx, size %lu byte(s)\n",
*addr, *size);
return NV_OK;
}
#endif
int bcm_chimp_initiate_fastboot(int fastboot_type)
{
int err;
if ((fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) &&
(fastboot_type <= CHIMP_FASTBOOT_JUMP_DECOMPRESS)) {
CHIMP_DBG("Initiating ChiMP fastboot type %d\n", fastboot_type);
}
/*
* If we are here, M0 did not setup Nitro because NIC mode
* strap was not present
*/
err = bcm_chimp_reset_and_initial_setup();
if (err)
return err;
if (fastboot_type > CHIMP_FASTBOOT_JUMP_DECOMPRESS) {
WARN("ChiMP setup deferred\n");
return -1;
}
if (fastboot_type != CHIMP_FASTBOOT_NITRO_RESET) {
if ((fastboot_type == CHIMP_FASTBOOT_JUMP_IN_PLACE) &&
(CHIMP_FB1_ENTRY == 0)) {
ERROR("Missing ESAL entry point for fastboot type 1.\n"
"Fastboot failed\n");
return -1;
}
/*
* TODO: We need to think of the way to load the ChiMP fw.
* This could be SPI, NAND, etc.
* For now we temporarily stick to the SPI load unless
* CHIMPFW_USE_SIDELOAD is defined. Note that for the SPI NVRAM
* image we need to parse directory and get the image.
* When we load image from other media there is no need to
* parse because fw image can be directly placed into the APE's
* scratchpad.
* For sideload method we simply reset the ChiMP, set bpe_reg
* to do fastboot with the type we define, and release from
* reset so that ROM loader would initiate fastboot immediately
*/
#ifndef CHIMPFW_USE_SIDELOAD
{
uintptr_t spi_addr;
size_t size;
err = bcm_chimp_find_fw_in_spi(&spi_addr, &size);
if (!err) {
INFO("Loading ChiMP firmware, addr 0x%lx, size %lu byte(s)\n",
spi_addr, size);
bcm_chimp_load_fw_from_spi(spi_addr, size);
} else {
ERROR("Error %d ChiMP firmware not in NVRAM directory!\n",
err);
}
}
#else
INFO("Skip ChiMP QSPI fastboot type %d due to sideload requested\n",
fastboot_type);
#endif
if (!err) {
INFO("Instruct ChiMP to fastboot\n");
bcm_chimp_set_fastboot(fastboot_type);
INFO("Fastboot mode set\n");
}
}
bcm_nitro_chimp_release_reset();
return err;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* Copyright (c) 2016 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <emmc_api.h>
#include <cmn_plat_util.h>
#define MAX_CMD_RETRY 10
#if EMMC_USE_DMA
#define USE_DMA 1
#else
#define USE_DMA 0
#endif
struct emmc_global_buffer emmc_global_buf;
struct emmc_global_buffer *emmc_global_buf_ptr = &emmc_global_buf;
struct emmc_global_vars emmc_global_vars;
struct emmc_global_vars *emmc_global_vars_ptr = &emmc_global_vars;
static struct sd_handle *sdio_gethandle(void);
static uint32_t sdio_idle(struct sd_handle *p_sdhandle);
static uint32_t sdio_read(struct sd_handle *p_sdhandle,
uintptr_t mem_addr,
uintptr_t storage_addr,
size_t storage_size,
size_t bytes_to_read);
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
static uint32_t sdio_write(struct sd_handle *p_sdhandle,
uintptr_t mem_addr,
uintptr_t data_addr,
size_t bytes_to_write);
#endif
static struct sd_handle *sdio_init(void);
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle);
static void init_globals(void)
{
memset((void *)emmc_global_buf_ptr, 0, sizeof(*emmc_global_buf_ptr));
memset((void *)emmc_global_vars_ptr, 0, sizeof(*emmc_global_vars_ptr));
}
/*
* This function is used to change partition
*/
uint32_t emmc_partition_select(uint32_t partition)
{
int rc;
struct sd_handle *sd_handle = sdio_gethandle();
if (sd_handle->device == 0) {
EMMC_TRACE("eMMC init is not done");
return 0;
}
switch (partition) {
case EMMC_BOOT_PARTITION1:
rc = set_boot_config(sd_handle,
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT1);
EMMC_TRACE(
"Change to Boot Partition 1 result:%d (0 means SD_OK)\n",
rc);
break;
case EMMC_BOOT_PARTITION2:
rc = set_boot_config(sd_handle,
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_BOOT2);
EMMC_TRACE(
"Change to Boot Partition 2 result:%d (0 means SD_OK)\n",
rc);
break;
case EMMC_USE_CURRENT_PARTITION:
rc = SD_OK;
EMMC_TRACE("Stay on current partition");
break;
case EMMC_USER_AREA:
default:
rc = set_boot_config(sd_handle,
SDIO_HW_EMMC_EXT_CSD_BOOT_ACC_USER);
EMMC_TRACE("Change to User area result:%d (0 means SD_OK)\n",
rc);
break;
}
return (rc == SD_OK);
}
/*
* Initialize emmc controller for eMMC
* Returns 0 on fail condition
*/
uint32_t bcm_emmc_init(bool card_rdy_only)
{
struct sd_handle *p_sdhandle;
uint32_t result = 0;
EMMC_TRACE("Enter emmc_controller_init()\n");
/* If eMMC is already initialized, skip init */
if (emmc_global_vars_ptr->init_done)
return 1;
init_globals();
p_sdhandle = sdio_init();
if (p_sdhandle == NULL) {
ERROR("eMMC init failed");
return result;
}
if (card_rdy_only) {
/* Put the card in Ready state, Not complete init */
result = bcm_emmc_card_ready_state(p_sdhandle);
return !result;
}
if (sdio_idle(p_sdhandle) == EMMC_BOOT_OK) {
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, USE_DMA,
SD_DMA_BOUNDARY_256K, EMMC_BLOCK_SIZE,
EMMC_WFE_RETRY);
if (!select_blk_sz(p_sdhandle,
p_sdhandle->device->cfg.blockSize)) {
emmc_global_vars_ptr->init_done = 1;
result = 1;
} else {
ERROR("Select Block Size failed\n");
}
} else {
ERROR("eMMC init failed");
}
/* Initialization is failed, so deinit HW setting */
if (result == 0)
emmc_deinit();
return result;
}
/*
* Function to de-init SDIO controller for eMMC
*/
void emmc_deinit(void)
{
emmc_global_vars_ptr->init_done = 0;
emmc_global_vars_ptr->sdHandle.card = 0;
emmc_global_vars_ptr->sdHandle.device = 0;
}
/*
* Read eMMC memory
* Returns read_size
*/
uint32_t emmc_read(uintptr_t mem_addr, uintptr_t storage_addr,
size_t storage_size, size_t bytes_to_read)
{
struct sd_handle *sd_handle = sdio_gethandle();
if (sd_handle->device == 0) {
EMMC_TRACE("eMMC init is not done");
return 0;
}
return sdio_read(sdio_gethandle(), mem_addr, storage_addr,
storage_size, bytes_to_read);
}
#ifdef INCLUDE_EMMC_DRIVER_ERASE_CODE
#define EXT_CSD_ERASE_GRP_SIZE 224
static int emmc_block_erase(uintptr_t mem_addr, size_t blocks)
{
struct sd_handle *sd_handle = sdio_gethandle();
if (sd_handle->device == 0) {
ERROR("eMMC init is not done");
return -1;
}
return erase_card(sdio_gethandle(), mem_addr, blocks);
}
int emmc_erase(uintptr_t mem_addr, size_t num_of_blocks, uint32_t partition)
{
int err = 0;
size_t block_count = 0, blocks = 0;
size_t erase_group = 0;
erase_group =
emmc_global_buf_ptr->u.Ext_CSD_storage[EXT_CSD_ERASE_GRP_SIZE]*1024;
INFO("eMMC Erase Group Size=0x%lx\n", erase_group);
emmc_partition_select(partition);
while (block_count < num_of_blocks) {
blocks = ((num_of_blocks - block_count) > erase_group) ?
erase_group : (num_of_blocks - block_count);
err = emmc_block_erase(mem_addr + block_count, blocks);
if (err)
break;
block_count += blocks;
}
if (err == 0)
INFO("eMMC Erase of partition %d successful\n", partition);
else
ERROR("eMMC Erase of partition %d Failed(%i)\n", partition, err);
return err;
}
#endif
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
/*
* Write to eMMC memory
* Returns written_size
*/
uint32_t emmc_write(uintptr_t mem_addr, uintptr_t data_addr,
size_t bytes_to_write)
{
struct sd_handle *sd_handle = sdio_gethandle();
if (sd_handle->device == 0) {
EMMC_TRACE("eMMC init is not done");
return 0;
}
return sdio_write(sd_handle, mem_addr, data_addr, bytes_to_write);
}
#endif
/*
* Send SDIO Cmd
* Return 0 for pass condition
*/
uint32_t send_sdio_cmd(uint32_t cmdIndex, uint32_t argument,
uint32_t options, struct sd_resp *resp)
{
struct sd_handle *sd_handle = sdio_gethandle();
if (sd_handle->device == 0) {
EMMC_TRACE("eMMC init is not done");
return 1;
}
return send_cmd(sd_handle, cmdIndex, argument, options, resp);
}
/*
* This function return SDIO handle
*/
struct sd_handle *sdio_gethandle(void)
{
return &emmc_global_vars_ptr->sdHandle;
}
/*
* Initialize SDIO controller
*/
struct sd_handle *sdio_init(void)
{
uint32_t SDIO_base;
struct sd_handle *p_sdhandle = &emmc_global_vars_ptr->sdHandle;
SDIO_base = EMMC_CTRL_REGS_BASE_ADDR;
if (SDIO_base == SDIO0_EMMCSDXC_SYSADDR)
EMMC_TRACE(" ---> for SDIO 0 Controller\n\n");
memset(p_sdhandle, 0, sizeof(struct sd_handle));
p_sdhandle->device = &emmc_global_vars_ptr->sdDevice;
p_sdhandle->card = &emmc_global_vars_ptr->sdCard;
memset(p_sdhandle->device, 0, sizeof(struct sd_dev));
memset(p_sdhandle->card, 0, sizeof(struct sd_card_info));
if (chal_sd_start((CHAL_HANDLE *) p_sdhandle->device,
SD_PIO_MODE, SDIO_base, SDIO_base) != SD_OK)
return NULL;
set_config(p_sdhandle, SD_NORMAL_SPEED, MAX_CMD_RETRY, SD_DMA_OFF,
SD_DMA_BOUNDARY_4K, EMMC_BLOCK_SIZE, EMMC_WFE_RETRY);
return &emmc_global_vars_ptr->sdHandle;
}
uint32_t sdio_idle(struct sd_handle *p_sdhandle)
{
reset_card(p_sdhandle);
SD_US_DELAY(1000);
if (init_card(p_sdhandle, SD_CARD_DETECT_MMC) != SD_OK) {
reset_card(p_sdhandle);
reset_host_ctrl(p_sdhandle);
return EMMC_BOOT_NO_CARD;
}
return EMMC_BOOT_OK;
}
/*
* This function read eMMC
*/
uint32_t sdio_read(struct sd_handle *p_sdhandle,
uintptr_t mem_addr,
uintptr_t storage_addr,
size_t storage_size, size_t bytes_to_read)
{
uint32_t offset = 0, blockAddr, readLen = 0, rdCount;
uint32_t remSize, manual_copy_size;
uint8_t *outputBuf = (uint8_t *) storage_addr;
const size_t blockSize = p_sdhandle->device->cfg.blockSize;
VERBOSE("EMMC READ: dst=0x%lx, src=0x%lx, size=0x%lx\n",
storage_addr, mem_addr, bytes_to_read);
if (storage_size < bytes_to_read)
/* Don't have sufficient storage to complete the operation */
return 0;
/* Range check non high capacity memory */
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
if (mem_addr > 0x80000000)
return 0;
}
/* High capacity card use block address mode */
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
blockAddr = (uint32_t) (mem_addr / blockSize);
offset = (uint32_t) (mem_addr - (blockAddr * blockSize));
} else {
blockAddr = (uint32_t) (mem_addr / blockSize) * blockSize;
offset = (uint32_t) (mem_addr - blockAddr);
}
remSize = bytes_to_read;
rdCount = 0;
/* Process first unaligned block of MAX_READ_LENGTH */
if (offset > 0) {
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
blockAddr, SD_MAX_READ_LENGTH)) {
if (remSize < (blockSize - offset)) {
rdCount += remSize;
manual_copy_size = remSize;
remSize = 0; /* read is done */
} else {
remSize -= (blockSize - offset);
rdCount += (blockSize - offset);
manual_copy_size = blockSize - offset;
}
/* Check for overflow */
if (manual_copy_size > storage_size ||
(((uintptr_t)outputBuf + manual_copy_size) >
(storage_addr + storage_size))) {
ERROR("EMMC READ: Overflow 1\n");
return 0;
}
memcpy(outputBuf,
(void *)((uintptr_t)
(emmc_global_buf_ptr->u.tempbuf + offset)),
manual_copy_size);
/* Update Physical address */
outputBuf += manual_copy_size;
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
blockAddr++;
else
blockAddr += blockSize;
} else {
return 0;
}
}
while (remSize >= blockSize) {
if (remSize >= SD_MAX_BLK_TRANSFER_LENGTH)
readLen = SD_MAX_BLK_TRANSFER_LENGTH;
else
readLen = (remSize / blockSize) * blockSize;
/* Check for overflow */
if ((rdCount + readLen) > storage_size ||
(((uintptr_t) outputBuf + readLen) >
(storage_addr + storage_size))) {
ERROR("EMMC READ: Overflow\n");
return 0;
}
if (!read_block(p_sdhandle, outputBuf, blockAddr, readLen)) {
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
blockAddr += (readLen / blockSize);
else
blockAddr += readLen;
remSize -= readLen;
rdCount += readLen;
/* Update Physical address */
outputBuf += readLen;
} else {
return 0;
}
}
/* process the last unaligned block reading */
if (remSize > 0) {
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
blockAddr, SD_MAX_READ_LENGTH)) {
rdCount += remSize;
/* Check for overflow */
if (rdCount > storage_size ||
(((uintptr_t) outputBuf + remSize) >
(storage_addr + storage_size))) {
ERROR("EMMC READ: Overflow\n");
return 0;
}
memcpy(outputBuf,
emmc_global_buf_ptr->u.tempbuf, remSize);
/* Update Physical address */
outputBuf += remSize;
} else {
rdCount = 0;
}
}
return rdCount;
}
#ifdef INCLUDE_EMMC_DRIVER_WRITE_CODE
static uint32_t sdio_write(struct sd_handle *p_sdhandle, uintptr_t mem_addr,
uintptr_t data_addr, size_t bytes_to_write)
{
uint32_t offset, blockAddr, writeLen, wtCount = 0;
uint32_t remSize, manual_copy_size = 0;
uint8_t *inputBuf = (uint8_t *)data_addr;
/* range check non high capacity memory */
if ((p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) == 0) {
if (mem_addr > 0x80000000)
return 0;
}
/* the high capacity card use block address mode */
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY) {
blockAddr =
(uint32_t)(mem_addr / p_sdhandle->device->cfg.blockSize);
offset =
(uint32_t)(mem_addr -
blockAddr * p_sdhandle->device->cfg.blockSize);
} else {
blockAddr =
((uint32_t)mem_addr / p_sdhandle->device->cfg.blockSize) *
p_sdhandle->device->cfg.blockSize;
offset = (uint32_t) mem_addr - blockAddr;
}
remSize = bytes_to_write;
wtCount = 0;
/* process first unaligned block */
if (offset > 0) {
if (!read_block(p_sdhandle, emmc_global_buf_ptr->u.tempbuf,
blockAddr, p_sdhandle->device->cfg.blockSize)) {
if (remSize <
(p_sdhandle->device->cfg.blockSize - offset))
manual_copy_size = remSize;
else
manual_copy_size =
p_sdhandle->device->cfg.blockSize - offset;
memcpy((void *)((uintptr_t)
(emmc_global_buf_ptr->u.tempbuf + offset)),
inputBuf,
manual_copy_size);
/* Update Physical address */
if (!write_block(p_sdhandle,
emmc_global_buf_ptr->u.tempbuf,
blockAddr,
p_sdhandle->device->cfg.blockSize)) {
if (remSize <
(p_sdhandle->device->cfg.blockSize -
offset)) {
wtCount += remSize;
manual_copy_size = remSize;
remSize = 0; /* read is done */
} else {
remSize -=
(p_sdhandle->device->cfg.blockSize -
offset);
wtCount +=
(p_sdhandle->device->cfg.blockSize -
offset);
manual_copy_size =
p_sdhandle->device->cfg.blockSize -
offset;
}
inputBuf += manual_copy_size;
if (p_sdhandle->device->ctrl.ocr &
SD_CARD_HIGH_CAPACITY)
blockAddr++;
else
blockAddr +=
p_sdhandle->device->cfg.blockSize;
} else
return 0;
} else {
return 0;
}
}
/* process block writing */
while (remSize >= p_sdhandle->device->cfg.blockSize) {
if (remSize >= SD_MAX_READ_LENGTH) {
writeLen = SD_MAX_READ_LENGTH;
} else {
writeLen =
(remSize / p_sdhandle->device->cfg.blockSize) *
p_sdhandle->device->cfg.blockSize;
}
if (!write_block(p_sdhandle, inputBuf, blockAddr, writeLen)) {
if (p_sdhandle->device->ctrl.ocr & SD_CARD_HIGH_CAPACITY)
blockAddr +=
(writeLen /
p_sdhandle->device->cfg.blockSize);
else
blockAddr += writeLen;
remSize -= writeLen;
wtCount += writeLen;
inputBuf += writeLen;
} else {
return 0;
}
}
/* process the last unaligned block reading */
if (remSize > 0) {
if (!read_block(p_sdhandle,
emmc_global_buf_ptr->u.tempbuf,
blockAddr, p_sdhandle->device->cfg.blockSize)) {
memcpy(emmc_global_buf_ptr->u.tempbuf,
inputBuf, remSize);
/* Update Physical address */
if (!write_block(p_sdhandle,
emmc_global_buf_ptr->u.tempbuf,
blockAddr,
p_sdhandle->device->cfg.blockSize)) {
wtCount += remSize;
inputBuf += remSize;
} else {
return 0;
}
} else {
wtCount = 0;
}
}
return wtCount;
}
#endif
/*
* Function to put the card in Ready state by sending CMD0 and CMD1
*/
static int32_t bcm_emmc_card_ready_state(struct sd_handle *p_sdhandle)
{
int32_t result = 0;
uint32_t argument = MMC_CMD_IDLE_RESET_ARG; /* Exit from Boot mode */
if (p_sdhandle) {
send_sdio_cmd(SD_CMD_GO_IDLE_STATE, argument, 0, NULL);
result = reset_card(p_sdhandle);
if (result != SD_OK) {
EMMC_TRACE("eMMC Reset error\n");
return SD_RESET_ERROR;
}
SD_US_DELAY(2000);
result = mmc_cmd1(p_sdhandle);
}
return result;
}
/*
* 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);
}
/*
* 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;
}
/*
* 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;
}
/*
* 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;
}
This diff is collapsed.
/*
* Copyright (c) 2017 - 2020, Broadcom
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef IPROC_QSPI_H
#define IPROC_QSPI_H
#include <platform_def.h>
/*SPI configuration enable*/
#define IPROC_QSPI_CLK_SPEED 62500000
#define SPI_CPHA (1 << 0)
#define SPI_CPOL (1 << 1)
#define IPROC_QSPI_MODE0 0
#define IPROC_QSPI_MODE3 (SPI_CPOL|SPI_CPHA)
#define IPROC_QSPI_BUS 0
#define IPROC_QSPI_CS 0
#define IPROC_QSPI_BASE_REG QSPI_CTRL_BASE_ADDR
#define IPROC_QSPI_CRU_CONTROL_REG QSPI_CLK_CTRL
#define QSPI_AXI_CLK 200000000
#define QSPI_RETRY_COUNT_US_MAX 200000
/* Chip attributes */
#define QSPI_REG_BASE IPROC_QSPI_BASE_REG
#define CRU_CONTROL_REG IPROC_QSPI_CRU_CONTROL_REG
#define SPBR_DIV_MIN 8U
#define SPBR_DIV_MAX 255U
#define NUM_CDRAM_BYTES 16U
/* Register fields */
#define MSPI_SPCR0_MSB_BITS_8 0x00000020
/* Flash opcode and parameters */
#define CDRAM_PCS0 2
#define CDRAM_CONT (1 << 7)
#define CDRAM_BITS_EN (1 << 6)
#define CDRAM_QUAD_MODE (1 << 8)
#define CDRAM_RBIT_INPUT (1 << 10)
/* MSPI registers */
#define QSPI_MSPI_MODE_REG_BASE (QSPI_REG_BASE + 0x200)
#define MSPI_SPCR0_LSB_REG 0x000
#define MSPI_SPCR0_MSB_REG 0x004
#define MSPI_SPCR1_LSB_REG 0x008
#define MSPI_SPCR1_MSB_REG 0x00c
#define MSPI_NEWQP_REG 0x010
#define MSPI_ENDQP_REG 0x014
#define MSPI_SPCR2_REG 0x018
#define MSPI_STATUS_REG 0x020
#define MSPI_CPTQP_REG 0x024
#define MSPI_TXRAM_REG 0x040
#define MSPI_RXRAM_REG 0x0c0
#define MSPI_CDRAM_REG 0x140
#define MSPI_WRITE_LOCK_REG 0x180
#define MSPI_DISABLE_FLUSH_GEN_REG 0x184
#define MSPI_SPCR0_MSB_REG_MSTR_SHIFT 7
#define MSPI_SPCR0_MSB_REG_16_BITS_PER_WD_SHIFT (0 << 2)
#define MSPI_SPCR0_MSB_REG_MODE_MASK 0x3
/* BSPI registers */
#define QSPI_BSPI_MODE_REG_BASE QSPI_REG_BASE
#define BSPI_MAST_N_BOOT_CTRL_REG 0x008
#define BSPI_BUSY_STATUS_REG 0x00c
#define MSPI_CMD_COMPLETE_MASK 1
#define BSPI_BUSY_MASK 1
#define MSPI_CTRL_MASK 1
#define MSPI_SPE (1 << 6)
#define MSPI_CONT_AFTER_CMD (1 << 7)
/* State */
enum bcm_qspi_state {
QSPI_STATE_DISABLED,
QSPI_STATE_MSPI,
QSPI_STATE_BSPI
};
/* QSPI private data */
struct bcmspi_priv {
/* Specified SPI parameters */
uint32_t max_hz;
uint32_t spi_mode;
/* State */
enum bcm_qspi_state state;
int mspi_16bit;
/* Registers */
uintptr_t mspi_hw;
uintptr_t bspi_hw;
};
int iproc_qspi_setup(uint32_t bus, uint32_t cs,
uint32_t max_hz, uint32_t mode);
int iproc_qspi_claim_bus(void);
void iproc_qspi_release_bus(void);
int iproc_qspi_xfer(uint32_t bitlen, const void *dout,
void *din, unsigned long flags);
#endif /* _IPROC_QSPI_H_ */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment