| // SPDX-License-Identifier: BSD-2-Clause |
| /* |
| * Copyright 2019 Broadcom. |
| */ |
| |
| #include <assert.h> |
| #include <drivers/bcm_sotp.h> |
| #include <initcall.h> |
| #include <io.h> |
| #include <kernel/delay.h> |
| #include <mm/core_memprot.h> |
| #include <platform_config.h> |
| #include <util.h> |
| |
| #define SOTP_PROG_CONTROL 0x0 |
| #define SOTP_PROG_CONTROL__OTP_CPU_MODE_EN BIT(15) |
| #define SOTP_PROG_CONTROL__OTP_DISABLE_ECC BIT(9) |
| #define SOTP_ADDR__OTP_ROW_ADDR_R 6 |
| |
| #define SOTP_ADDR 0xc |
| |
| #define SOTP_CTRL_0 0x10 |
| #define SOTP_CTRL_0__START 1 |
| #define SOTP_READ 0 |
| |
| #define SOTP_STAT_0 0x18 |
| #define SOTP_STATUS_0__FDONE BIT(3) |
| |
| #define SOTP_STATUS_1 0x1c |
| #define SOTP_STATUS_1__CMD_DONE BIT(1) |
| #define SOTP_STATUS_1__ECC_DET BIT(17) |
| |
| #define SOTP_RDDATA_0 0x20 |
| #define SOTP_RDDATA_1 0x24 |
| #define SOTP_ADDR_MASK 0x3ff |
| |
| #define SOTP_ECC_ERR_DETECT BIT64(63) |
| |
| #define SOTP_TIMEOUT_US 300 |
| |
| static vaddr_t bcm_sotp_base; |
| |
| static TEE_Result otp_status_done_wait(vaddr_t addr, uint32_t bit) |
| { |
| uint64_t timeout = timeout_init_us(SOTP_TIMEOUT_US); |
| |
| while (!(io_read32(addr) & bit)) |
| if (timeout_elapsed(timeout)) |
| return TEE_ERROR_BUSY; |
| return TEE_SUCCESS; |
| } |
| |
| TEE_Result bcm_iproc_sotp_mem_read(uint32_t row_addr, uint32_t sotp_add_ecc, |
| uint64_t *rdata) |
| { |
| uint64_t read_data = 0; |
| uint32_t reg_val = 0; |
| TEE_Result ret = TEE_SUCCESS; |
| |
| assert(bcm_sotp_base); |
| /* Check for FDONE status */ |
| ret = otp_status_done_wait((bcm_sotp_base + SOTP_STAT_0), |
| SOTP_STATUS_0__FDONE); |
| if (ret) { |
| EMSG("FDONE status done wait failed"); |
| return ret; |
| } |
| |
| /* Enable OTP access by CPU */ |
| io_setbits32((bcm_sotp_base + SOTP_PROG_CONTROL), |
| SOTP_PROG_CONTROL__OTP_CPU_MODE_EN); |
| |
| /* ROWS does not support ECC */ |
| if (row_addr <= SOTP_NO_ECC_ROWS) |
| sotp_add_ecc = 0; |
| |
| if (sotp_add_ecc == 1) { |
| io_clrbits32((bcm_sotp_base + SOTP_PROG_CONTROL), |
| SOTP_PROG_CONTROL__OTP_DISABLE_ECC); |
| } else { |
| io_setbits32((bcm_sotp_base + SOTP_PROG_CONTROL), |
| SOTP_PROG_CONTROL__OTP_DISABLE_ECC); |
| } |
| |
| /* 10 bit row address */ |
| reg_val = (row_addr & SOTP_ADDR_MASK) << SOTP_ADDR__OTP_ROW_ADDR_R; |
| io_write32((bcm_sotp_base + SOTP_ADDR), reg_val); |
| reg_val = SOTP_READ; |
| io_write32((bcm_sotp_base + SOTP_CTRL_0), reg_val); |
| |
| /* Start bit to tell SOTP to send command to the OTP controller */ |
| io_setbits32((bcm_sotp_base + SOTP_CTRL_0), SOTP_CTRL_0__START); |
| |
| /* Wait for SOTP command done to be set */ |
| ret = otp_status_done_wait((bcm_sotp_base + SOTP_STAT_0), |
| SOTP_STATUS_1__CMD_DONE); |
| if (ret) { |
| EMSG("FDONE cmd done wait failed\n"); |
| return ret; |
| } |
| |
| DMSG("CMD Done\n"); |
| |
| /* Clr Start bit after command done */ |
| io_clrbits32((bcm_sotp_base + SOTP_CTRL_0), SOTP_CTRL_0__START); |
| read_data = io_read32(bcm_sotp_base + SOTP_RDDATA_1); |
| read_data = ((read_data & 0x1ff) << 32); |
| read_data |= io_read32(bcm_sotp_base + SOTP_RDDATA_0); |
| |
| reg_val = io_read32(bcm_sotp_base + SOTP_STATUS_1); |
| /* No ECC check till SOTP_NO_ECC_ROWS */ |
| if (row_addr > SOTP_NO_ECC_ROWS && |
| reg_val & SOTP_STATUS_1__ECC_DET) { |
| EMSG("SOTP ECC ERROR Detected ROW %d\n", row_addr); |
| read_data = SOTP_ECC_ERR_DETECT; |
| } |
| |
| /* Command done is cleared */ |
| io_setbits32((bcm_sotp_base + SOTP_STATUS_1), SOTP_STATUS_1__CMD_DONE); |
| io_clrbits32((bcm_sotp_base + SOTP_PROG_CONTROL), |
| SOTP_PROG_CONTROL__OTP_CPU_MODE_EN); |
| DMSG("read done\n"); |
| |
| *rdata = read_data; |
| return ret; |
| } |
| |
| static TEE_Result bcm_sotp_init(void) |
| { |
| bcm_sotp_base = (vaddr_t)phys_to_virt(SOTP_BASE, MEM_AREA_IO_SEC); |
| |
| DMSG("bcm_sotp init done\n"); |
| return TEE_SUCCESS; |
| } |
| |
| driver_init(bcm_sotp_init); |