| /* |
| * 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; |
| } |