| /* |
| * Copyright (c) 2012-2016, Freescale Semiconductor, Inc. |
| * All rights reserved. |
| * Copyright 2018 NXP |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * o Redistributions of source code must retain the above copyright notice, this list |
| * of conditions and the following disclaimer. |
| * |
| * o Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or |
| * other materials provided with the distribution. |
| * |
| * o Neither the name of Freescale Semiconductor, Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR |
| * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <memalign.h> |
| #include <asm/io.h> |
| #ifndef CONFIG_ARCH_MX7ULP |
| #include <asm/arch/crm_regs.h> |
| #else |
| #include <asm/arch/pcc.h> |
| #endif /* CONFIG_ARCH_MX7ULP */ |
| #include "fsl_caam_internal.h" |
| #include "fsl/desc_constr.h" |
| #include <fsl_caam.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| static void rng_init(void); |
| static void caam_clock_enable(void); |
| static int do_cfg_jrqueue(void); |
| static int do_job(u32 *desc); |
| static int jr_reset(void); |
| |
| /* |
| * Structures |
| */ |
| /* Definition of input ring object */ |
| struct inring_entry { |
| u32 desc; /* Pointer to input descriptor */ |
| }; |
| |
| /* Definition of output ring object */ |
| struct outring_entry { |
| u32 desc; /* Pointer to output descriptor */ |
| u32 status; /* Status of the Job Ring */ |
| }; |
| |
| /* Main job ring data structure */ |
| struct jr_data_st { |
| struct inring_entry *inrings; |
| struct outring_entry *outrings; |
| u32 status; /* Ring buffers init status */ |
| u32 *desc; /* Pointer to output descriptor */ |
| u32 raw_addr[DESC_MAX_SIZE * 2]; |
| }; |
| |
| /* |
| * Global variables |
| */ |
| static struct jr_data_st g_jrdata = {0, 0, 0xFFFFFFFF}; |
| |
| static u8 skeymod[] = { |
| 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, |
| 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 |
| }; |
| |
| /* |
| * Local functions |
| */ |
| static void dump_error(void) |
| { |
| int i; |
| |
| debug("Dump CAAM Error\n"); |
| debug("MCFGR 0x%08X\n", __raw_readl(CAAM_MCFGR)); |
| debug("FAR 0x%08X\n", __raw_readl(CAAM_FAR)); |
| debug("FAMR 0x%08X\n", __raw_readl(CAAM_FAMR)); |
| debug("FADR 0x%08X\n", __raw_readl(CAAM_FADR)); |
| debug("CSTA 0x%08X\n", __raw_readl(CAAM_STA)); |
| debug("RTMCTL 0x%X\n", __raw_readl(CAAM_RTMCTL)); |
| debug("RTSTATUS 0x%X\n", __raw_readl(CAAM_RTSTATUS)); |
| debug("RDSTA 0x%X\n", __raw_readl(CAAM_RDSTA)); |
| |
| for (i = 0; i < desc_len(g_jrdata.desc); i++) |
| debug("desc[%d]: 0x%08x\n", i, g_jrdata.desc[i]); |
| } |
| |
| /*! |
| * Secure memory run command. |
| * |
| * @param sec_mem_cmd Secure memory command register |
| * @return cmd_status Secure memory command status register |
| */ |
| u32 secmem_set_cmd_1(u32 sec_mem_cmd) |
| { |
| u32 temp_reg; |
| __raw_writel(sec_mem_cmd, CAAM_SMCJR0); |
| do { |
| temp_reg = __raw_readl(CAAM_SMCSJR0); |
| } while (temp_reg & CMD_COMPLETE); |
| |
| return temp_reg; |
| } |
| |
| |
| /*! |
| * Use CAAM to decapsulate a blob to secure memory. |
| * Such blob of secret key cannot be read once decrypted, |
| * but can still be used for enc/dec operation of user's data. |
| * |
| * @param blob_addr Location address of the blob. |
| * |
| * @return SUCCESS or ERROR_XXX |
| */ |
| u32 caam_decap_blob(u32 plain_text, u32 blob_addr, u32 size) |
| { |
| u32 ret = SUCCESS; |
| u32 key_sz = sizeof(skeymod); |
| u32 *decap_desc = g_jrdata.desc; |
| |
| /* prepare job descriptor */ |
| init_job_desc(decap_desc, 0); |
| append_load(decap_desc, PTR2CAAMDMA(skeymod), key_sz, |
| LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY); |
| append_seq_in_ptr_intlen(decap_desc, blob_addr, size + 48, 0); |
| append_seq_out_ptr_intlen(decap_desc, plain_text, size, 0); |
| append_operation(decap_desc, OP_TYPE_DECAP_PROTOCOL | OP_PCLID_BLOB); |
| |
| flush_dcache_range((uintptr_t)blob_addr & ALIGN_MASK, |
| ((uintptr_t)blob_addr & ALIGN_MASK) |
| + ROUND(2 * size, ARCH_DMA_MINALIGN)); |
| flush_dcache_range((uintptr_t)plain_text & ALIGN_MASK, |
| (plain_text & ALIGN_MASK) |
| + ROUND(2 * size, ARCH_DMA_MINALIGN)); |
| |
| /* Run descriptor with result written to blob buffer */ |
| ret = do_job(decap_desc); |
| |
| if (ret != SUCCESS) { |
| printf("Error: blob decap job failed 0x%x\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /*! |
| * Use CAAM to generate a blob. |
| * |
| * @param plain_data_addr Location address of the plain data. |
| * @param blob_addr Location address of the blob. |
| * |
| * @return SUCCESS or ERROR_XXX |
| */ |
| u32 caam_gen_blob(u32 plain_data_addr, u32 blob_addr, u32 size) |
| { |
| u32 ret = SUCCESS; |
| u32 key_sz = sizeof(skeymod); |
| u32 *encap_desc = g_jrdata.desc; |
| /* Buffer to hold the resulting blob */ |
| u8 *blob = (u8 *)CAAMDMA2PTR(blob_addr); |
| |
| /* initialize the blob array */ |
| memset(blob,0,size); |
| |
| /* prepare job descriptor */ |
| init_job_desc(encap_desc, 0); |
| append_load(encap_desc, PTR2CAAMDMA(skeymod), key_sz, |
| LDST_CLASS_2_CCB | LDST_SRCDST_BYTE_KEY); |
| append_seq_in_ptr_intlen(encap_desc, plain_data_addr, size, 0); |
| append_seq_out_ptr_intlen(encap_desc, PTR2CAAMDMA(blob), size + 48, 0); |
| append_operation(encap_desc, OP_TYPE_ENCAP_PROTOCOL | OP_PCLID_BLOB); |
| |
| flush_dcache_range((uintptr_t)plain_data_addr & ALIGN_MASK, |
| (plain_data_addr & ALIGN_MASK) |
| + ROUND(2 * size, ARCH_DMA_MINALIGN)); |
| flush_dcache_range((uintptr_t)blob & ALIGN_MASK, |
| ((uintptr_t)blob & ALIGN_MASK) |
| + ROUND(2 * size, ARCH_DMA_MINALIGN)); |
| |
| ret = do_job(encap_desc); |
| |
| if (ret != SUCCESS) { |
| printf("Error: blob encap job failed 0x%x\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| u32 caam_hwrng(u8 *output_ptr, u32 output_len) |
| { |
| u32 ret = SUCCESS; |
| u32 *hwrng_desc = g_jrdata.desc; |
| /* Buffer to hold the resulting output*/ |
| u8 *output = (u8 *)output_ptr; |
| |
| /* initialize the output array */ |
| memset(output,0,output_len); |
| |
| /* prepare job descriptor */ |
| init_job_desc(hwrng_desc, 0); |
| append_operation(hwrng_desc, OP_ALG_ALGSEL_RNG | OP_TYPE_CLASS1_ALG); |
| append_fifo_store(hwrng_desc, PTR2CAAMDMA(output), |
| output_len, FIFOST_TYPE_RNGSTORE); |
| |
| /* flush cache */ |
| flush_dcache_range((uintptr_t)hwrng_desc & ALIGN_MASK, |
| ((uintptr_t)hwrng_desc & ALIGN_MASK) |
| + ROUND(DESC_MAX_SIZE, ARCH_DMA_MINALIGN)); |
| |
| ret = do_job(hwrng_desc); |
| |
| flush_dcache_range((uintptr_t)output & ALIGN_MASK, |
| ((uintptr_t)output & ALIGN_MASK) |
| + ROUND(2 * output_len, ARCH_DMA_MINALIGN)); |
| |
| if (ret != SUCCESS) { |
| printf("Error: RNG generate failed 0x%x\n", ret); |
| } |
| |
| return ret; |
| } |
| |
| /*! |
| * Initialize the CAAM. |
| * |
| */ |
| void caam_open(void) |
| { |
| u32 temp_reg; |
| int ret; |
| |
| /* switch on the clock */ |
| #ifndef CONFIG_ARCH_IMX8 |
| caam_clock_enable(); |
| #endif |
| |
| /* reset the CAAM */ |
| temp_reg = __raw_readl(CAAM_MCFGR) | |
| CAAM_MCFGR_DMARST | CAAM_MCFGR_SWRST; |
| __raw_writel(temp_reg, CAAM_MCFGR); |
| while (__raw_readl(CAAM_MCFGR) & CAAM_MCFGR_DMARST) |
| ; |
| |
| jr_reset(); |
| ret = do_cfg_jrqueue(); |
| |
| if (ret != SUCCESS) { |
| printf("Error CAAM JR initialization\n"); |
| return; |
| } |
| |
| /* Check if the RNG is already instantiated */ |
| temp_reg = __raw_readl(CAAM_RDSTA); |
| if (temp_reg == (RDSTA_IF0 | RDSTA_IF1 | RDSTA_SKVN)) { |
| printf("RNG already instantiated 0x%X\n", temp_reg); |
| return; |
| } |
| |
| rng_init(); |
| } |
| |
| static void caam_clock_enable(void) |
| { |
| #if defined(CONFIG_ARCH_MX6) |
| struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; |
| u32 reg; |
| |
| reg = __raw_readl(&mxc_ccm->CCGR0); |
| |
| reg |= (MXC_CCM_CCGR0_CAAM_SECURE_MEM_MASK | |
| MXC_CCM_CCGR0_CAAM_WRAPPER_ACLK_MASK | |
| MXC_CCM_CCGR0_CAAM_WRAPPER_IPG_MASK); |
| |
| __raw_writel(reg, &mxc_ccm->CCGR0); |
| |
| #ifndef CONFIG_MX6UL |
| /* EMI slow clk */ |
| reg = __raw_readl(&mxc_ccm->CCGR6); |
| reg |= MXC_CCM_CCGR6_EMI_SLOW_MASK; |
| |
| __raw_writel(reg, &mxc_ccm->CCGR6); |
| #endif |
| |
| #elif defined(CONFIG_ARCH_MX7) |
| HW_CCM_CCGR_SET(36, MXC_CCM_CCGR36_CAAM_DOMAIN0_MASK); |
| #elif defined(CONFIG_ARCH_MX7ULP) |
| pcc_clock_enable(PER_CLK_CAAM, true); |
| #endif |
| } |
| |
| static void kick_trng(u32 ent_delay) |
| { |
| u32 samples = 512; /* number of bits to generate and test */ |
| u32 mono_min = 195; |
| u32 mono_max = 317; |
| u32 mono_range = mono_max - mono_min; |
| u32 poker_min = 1031; |
| u32 poker_max = 1600; |
| u32 poker_range = poker_max - poker_min + 1; |
| u32 retries = 2; |
| u32 lrun_max = 32; |
| s32 run_1_min = 27; |
| s32 run_1_max = 107; |
| s32 run_1_range = run_1_max - run_1_min; |
| s32 run_2_min = 7; |
| s32 run_2_max = 62; |
| s32 run_2_range = run_2_max - run_2_min; |
| s32 run_3_min = 0; |
| s32 run_3_max = 39; |
| s32 run_3_range = run_3_max - run_3_min; |
| s32 run_4_min = -1; |
| s32 run_4_max = 26; |
| s32 run_4_range = run_4_max - run_4_min; |
| s32 run_5_min = -1; |
| s32 run_5_max = 18; |
| s32 run_5_range = run_5_max - run_5_min; |
| s32 run_6_min = -1; |
| s32 run_6_max = 17; |
| s32 run_6_range = run_6_max - run_6_min; |
| u32 val; |
| |
| /* Put RNG in program mode */ |
| /* Setting both RTMCTL:PRGM and RTMCTL:TRNG_ACC causes TRNG to |
| * properly invalidate the entropy in the entropy register and |
| * force re-generation. |
| */ |
| setbits_le32(CAAM_RTMCTL, RTMCTL_PGM | RTMCTL_ACC); |
| |
| /* Configure the RNG Entropy Delay |
| * Performance-wise, it does not make sense to |
| * set the delay to a value that is lower |
| * than the last one that worked (i.e. the state handles |
| * were instantiated properly. Thus, instead of wasting |
| * time trying to set the values controlling the sample |
| * frequency, the function simply returns. |
| */ |
| val = __raw_readl(CAAM_RTSDCTL); |
| val &= BM_TRNG_ENT_DLY; |
| val >>= BS_TRNG_ENT_DLY; |
| if (ent_delay < val) { |
| /* Put RNG4 into run mode */ |
| clrbits_le32(CAAM_RTMCTL, RTMCTL_PGM | RTMCTL_ACC); |
| return; |
| } |
| |
| val = (ent_delay << BS_TRNG_ENT_DLY) | samples; |
| __raw_writel(val, CAAM_RTSDCTL); |
| |
| /* min. freq. count, equal to 1/2 of the entropy sample length */ |
| __raw_writel(ent_delay >> 1, CAAM_RTFRQMIN); |
| |
| /* max. freq. count, equal to 32 times the entropy sample length */ |
| __raw_writel(ent_delay << 5, CAAM_RTFRQMAX); |
| |
| __raw_writel((retries << 16) | lrun_max, CAAM_RTSCMISC); |
| __raw_writel(poker_max, CAAM_RTPKRMAX); |
| __raw_writel(poker_range, CAAM_RTPKRRNG); |
| __raw_writel((mono_range << 16) | mono_max, CAAM_RTSCML); |
| __raw_writel((run_1_range << 16) | run_1_max, CAAM_RTSCR1L); |
| __raw_writel((run_2_range << 16) | run_2_max, CAAM_RTSCR2L); |
| __raw_writel((run_3_range << 16) | run_3_max, CAAM_RTSCR3L); |
| __raw_writel((run_4_range << 16) | run_4_max, CAAM_RTSCR4L); |
| __raw_writel((run_5_range << 16) | run_5_max, CAAM_RTSCR5L); |
| __raw_writel((run_6_range << 16) | run_6_max, CAAM_RTSCR6PL); |
| |
| val = __raw_readl(CAAM_RTMCTL); |
| /* |
| * Select raw sampling in both entropy shifter |
| * and statistical checker |
| */ |
| val &= ~BM_TRNG_SAMP_MODE; |
| val |= TRNG_SAMP_MODE_RAW_ES_SC; |
| /* Put RNG4 into run mode */ |
| val &= ~(RTMCTL_PGM | RTMCTL_ACC); |
| /*test with sample mode only */ |
| __raw_writel(val, CAAM_RTMCTL); |
| |
| /* Clear the ERR bit in RTMCTL if set. The TRNG error can occur when the |
| * RNG clock is not within 1/2x to 8x the system clock. |
| * This error is possible if ROM code does not initialize the system PLLs |
| * immediately after PoR. |
| */ |
| /* setbits_le32(CAAM_RTMCTL, RTMCTL_ERR); */ |
| } |
| |
| /* |
| * Descriptors to instantiate SH0, SH1, load the keys |
| */ |
| static const u32 rng_inst_sh0_desc[] = { |
| /* Header, don't setup the size */ |
| CAAM_HDR_CTYPE | CAAM_HDR_ONE | CAAM_HDR_START_INDEX(0), |
| /* Operation instantiation (sh0) */ |
| CAAM_PROTOP_CTYPE | CAAM_C1_RNG | ALGO_RNG_SH(0) | ALGO_RNG_INSTANTIATE, |
| }; |
| |
| static const u32 rng_inst_sh1_desc[] = { |
| /* wait for done - Jump to next entry */ |
| CAAM_C1_JUMP | CAAM_JUMP_LOCAL | CAAM_JUMP_TST_ALL_COND_TRUE |
| | CAAM_JUMP_OFFSET(1), |
| /* Clear written register (write 1) */ |
| CAAM_C0_LOAD_IMM | CAAM_DST_CLEAR_WRITTEN | sizeof(u32), |
| 0x00000001, |
| /* Operation instantiation (sh1) */ |
| CAAM_PROTOP_CTYPE | CAAM_C1_RNG | ALGO_RNG_SH(1) |
| | ALGO_RNG_INSTANTIATE, |
| }; |
| |
| static const u32 rng_inst_load_keys[] = { |
| /* wait for done - Jump to next entry */ |
| CAAM_C1_JUMP | CAAM_JUMP_LOCAL | CAAM_JUMP_TST_ALL_COND_TRUE |
| | CAAM_JUMP_OFFSET(1), |
| /* Clear written register (write 1) */ |
| CAAM_C0_LOAD_IMM | CAAM_DST_CLEAR_WRITTEN | sizeof(u32), |
| 0x00000001, |
| /* Generate the Key */ |
| CAAM_PROTOP_CTYPE | CAAM_C1_RNG | BM_ALGO_RNG_SK | ALGO_RNG_GENERATE, |
| }; |
| |
| static void do_inst_desc(u32 *desc, u32 status) |
| { |
| u32 *pdesc = desc; |
| u8 desc_len; |
| bool add_sh0 = false; |
| bool add_sh1 = false; |
| bool load_keys = false; |
| |
| /* |
| * Modify the the descriptor to remove if necessary: |
| * - The key loading |
| * - One of the SH already instantiated |
| */ |
| desc_len = RNG_DESC_SH0_SIZE; |
| if ((status & RDSTA_IF0) != RDSTA_IF0) |
| add_sh0 = true; |
| |
| if ((status & RDSTA_IF1) != RDSTA_IF1) { |
| add_sh1 = true; |
| if (add_sh0) |
| desc_len += RNG_DESC_SH1_SIZE; |
| } |
| |
| if ((status & RDSTA_SKVN) != RDSTA_SKVN) { |
| load_keys = true; |
| desc_len += RNG_DESC_KEYS_SIZE; |
| } |
| |
| /* Copy the SH0 descriptor anyway */ |
| memcpy(pdesc, rng_inst_sh0_desc, sizeof(rng_inst_sh0_desc)); |
| pdesc += RNG_DESC_SH0_SIZE; |
| |
| if (load_keys) { |
| debug("RNG - Load keys\n"); |
| memcpy(pdesc, rng_inst_load_keys, sizeof(rng_inst_load_keys)); |
| pdesc += RNG_DESC_KEYS_SIZE; |
| } |
| |
| if (add_sh1) { |
| if (add_sh0) { |
| debug("RNG - Instantiation of SH0 and SH1\n"); |
| /* Add the sh1 descriptor */ |
| memcpy(pdesc, rng_inst_sh1_desc, |
| sizeof(rng_inst_sh1_desc)); |
| } else { |
| debug("RNG - Instantiation of SH1 only\n"); |
| /* Modify the SH0 descriptor to instantiate only SH1 */ |
| desc[1] &= ~BM_ALGO_RNG_SH; |
| desc[1] |= ALGO_RNG_SH(1); |
| } |
| } |
| |
| /* Setup the descriptor size */ |
| desc[0] &= ~(0x3F); |
| desc[0] |= CAAM_HDR_DESCLEN(desc_len); |
| } |
| |
| static int jr_reset(void) |
| { |
| /* |
| * Function reset the Job Ring HW |
| * Reset is done in 2 steps: |
| * - Flush all pending jobs (Set RESET bit) |
| * - Reset the Job Ring (Set RESET bit second time) |
| */ |
| u16 timeout = 10000; |
| u32 reg_val; |
| |
| /* Mask interrupts to poll for reset completion status */ |
| setbits_le32(CAAM_JRCFGR0_LS, BM_JRCFGR_LS_IMSK); |
| |
| /* Initiate flush (required prior to reset) */ |
| __raw_writel(JRCR_RESET, CAAM_JRCR0); |
| do { |
| reg_val = __raw_readl(CAAM_JRINTR0); |
| reg_val &= BM_JRINTR_HALT; |
| } while ((reg_val == JRINTR_HALT_ONGOING) && --timeout); |
| |
| if (!timeout || reg_val != JRINTR_HALT_DONE) { |
| printf("Failed to flush job ring\n"); |
| return ERROR_ANY; |
| } |
| |
| /* Initiate reset */ |
| timeout = 100; |
| __raw_writel(JRCR_RESET, CAAM_JRCR0); |
| do { |
| reg_val = __raw_readl(CAAM_JRCR0); |
| } while ((reg_val & JRCR_RESET) && --timeout); |
| |
| if (!timeout) { |
| printf("Failed to reset job ring\n"); |
| return ERROR_ANY; |
| } |
| |
| return 0; |
| } |
| |
| static int do_job(u32 *desc) |
| { |
| int ret; |
| phys_addr_t p_desc = virt_to_phys(desc); |
| |
| if (__raw_readl(CAAM_IRSAR0) == 0) |
| return ERROR_ANY; |
| g_jrdata.inrings[0].desc = p_desc; |
| |
| flush_dcache_range((uintptr_t)g_jrdata.inrings & ALIGN_MASK, |
| ((uintptr_t)g_jrdata.inrings & ALIGN_MASK) |
| + ROUND(DESC_MAX_SIZE, ARCH_DMA_MINALIGN)); |
| flush_dcache_range((uintptr_t)desc & ALIGN_MASK, |
| ((uintptr_t)desc & ALIGN_MASK) |
| + ROUND(DESC_MAX_SIZE, ARCH_DMA_MINALIGN)); |
| |
| /* Inform HW that a new JR is available */ |
| __raw_writel(1, CAAM_IRJAR0); |
| while (__raw_readl(CAAM_ORSFR0) == 0) |
| ; |
| |
| flush_dcache_range((uintptr_t)g_jrdata.outrings & ALIGN_MASK, |
| ((uintptr_t)g_jrdata.outrings & ALIGN_MASK) |
| + ROUND(DESC_MAX_SIZE, ARCH_DMA_MINALIGN)); |
| |
| if (PTR2CAAMDMA(desc) == g_jrdata.outrings[0].desc) { |
| ret = g_jrdata.outrings[0].status; |
| } else { |
| dump_error(); |
| ret = ERROR_ANY; |
| } |
| |
| /* Acknowledge interrupt */ |
| setbits_le32(CAAM_JRINTR0, JRINTR_JRI); |
| |
| /* Remove the JR from the output list even if no JR caller found */ |
| __raw_writel(1, CAAM_ORJRR0); |
| |
| return ret; |
| } |
| |
| static int do_cfg_jrqueue(void) |
| { |
| u32 value = 0; |
| phys_addr_t ip_base; |
| phys_addr_t op_base; |
| |
| /* check if already configured after relocation */ |
| if (g_jrdata.status == RING_RELOC_INIT) |
| return 0; |
| |
| /* |
| * jr configuration needs to be updated once, after relocation to ensure |
| * using the right buffers. |
| * When buffers are updated after relocation the flag RING_RELOC_INIT |
| * is used to prevent extra updates |
| */ |
| if (gd->flags & GD_FLG_RELOC) { |
| g_jrdata.inrings = (struct inring_entry *) |
| memalign(ARCH_DMA_MINALIGN, |
| ARCH_DMA_MINALIGN); |
| g_jrdata.outrings = (struct outring_entry *) |
| memalign(ARCH_DMA_MINALIGN, |
| ARCH_DMA_MINALIGN); |
| g_jrdata.desc = (u32 *) |
| memalign(ARCH_DMA_MINALIGN, ARCH_DMA_MINALIGN); |
| g_jrdata.status = RING_RELOC_INIT; |
| } else { |
| u32 align_idx = 0; |
| u32 *addr; |
| |
| #if defined(CONFIG_SPL_BUILD) && CONFIG_VAL(SYS_MALLOC_F_LEN) |
| ulong maddr = (ulong)malloc(DESC_MAX_SIZE * 2 + 8); |
| addr = (u32*)ALIGN(maddr, 8); |
| #else |
| addr = g_jrdata.raw_addr; |
| #endif |
| /* Ensure 64bits buffers addresses alignment */ |
| if ((uintptr_t)addr & 0x7) |
| align_idx = 1; |
| g_jrdata.inrings = (struct inring_entry *) |
| (&addr[align_idx]); |
| g_jrdata.outrings = (struct outring_entry *) |
| (&addr[align_idx + 2]); |
| g_jrdata.desc = (u32 *)(&addr[align_idx + 4]); |
| g_jrdata.status = RING_EARLY_INIT; |
| } |
| |
| if (!g_jrdata.inrings || !g_jrdata.outrings) |
| return ERROR_ANY; |
| |
| /* Configure the HW Job Rings */ |
| ip_base = virt_to_phys((void *)g_jrdata.inrings); |
| op_base = virt_to_phys((void *)g_jrdata.outrings); |
| __raw_writel(ip_base, CAAM_IRBAR0); |
| __raw_writel(1, CAAM_IRSR0); |
| |
| __raw_writel(op_base, CAAM_ORBAR0); |
| __raw_writel(1, CAAM_ORSR0); |
| |
| setbits_le32(CAAM_JRINTR0, JRINTR_JRI); |
| |
| /* |
| * Configure interrupts but disable it: |
| * Optimization to generate an interrupt either when there are |
| * half of the job done or when there is a job done and |
| * 10 clock cycles elapse without new job complete |
| */ |
| value = 10 << BS_JRCFGR_LS_ICTT; |
| value |= (1 << BS_JRCFGR_LS_ICDCT) & BM_JRCFGR_LS_ICDCT; |
| value |= BM_JRCFGR_LS_ICEN; |
| value |= BM_JRCFGR_LS_IMSK; |
| __raw_writel(value, CAAM_JRCFGR0_LS); |
| |
| /* Enable deco watchdog */ |
| setbits_le32(CAAM_MCFGR, BM_MCFGR_WDE); |
| |
| return 0; |
| } |
| |
| static void do_clear_rng_error(void) |
| { |
| u32 val; |
| |
| val = __raw_readl(CAAM_RTMCTL); |
| |
| if (val & (RTMCTL_ERR | RTMCTL_FCT_FAIL)) { |
| setbits_le32(CAAM_RTMCTL, RTMCTL_ERR); |
| val = __raw_readl(CAAM_RTMCTL); |
| } |
| } |
| |
| static int do_instantiation(void) |
| { |
| int ret = ERROR_ANY; |
| u32 cha_vid_ls; |
| u32 ent_delay; |
| u32 status; |
| |
| if (!g_jrdata.desc) { |
| printf("%d: CAAM Descriptor allocation error\n", __LINE__); |
| return ERROR_ANY; |
| } |
| |
| cha_vid_ls = __raw_readl(CAAM_CHAVID_LS); |
| |
| /* |
| * If SEC has RNG version >= 4 and RNG state handle has not been |
| * already instantiated, do RNG instantiation |
| */ |
| if (((cha_vid_ls & BM_CHAVID_LS_RNGVID) >> BS_CHAVID_LS_RNGVID) < 4) { |
| printf("%d: RNG already instantiated\n", __LINE__); |
| return 0; |
| } |
| |
| ent_delay = TRNG_SDCTL_ENT_DLY_MIN; |
| |
| do { |
| /* Read the CAAM RNG status */ |
| status = __raw_readl(CAAM_RDSTA); |
| |
| if ((status & RDSTA_IF0) != RDSTA_IF0) { |
| /* Configure the RNG entropy delay */ |
| kick_trng(ent_delay); |
| ent_delay += 400; |
| } |
| |
| do_clear_rng_error(); |
| |
| if ((status & (RDSTA_IF0 | RDSTA_IF1)) != |
| (RDSTA_IF0 | RDSTA_IF1)) { |
| /* Prepare the instantiation descriptor */ |
| do_inst_desc(g_jrdata.desc, status); |
| |
| /* Run Job */ |
| ret = do_job(g_jrdata.desc); |
| |
| if (ret == ERROR_ANY) { |
| /* CAAM JR failure ends here */ |
| printf("RNG Instantiation error\n"); |
| goto end_instantation; |
| } |
| } else { |
| ret = SUCCESS; |
| printf("RNG instantiation done (%d)\n", ent_delay); |
| goto end_instantation; |
| } |
| } while (ent_delay < TRNG_SDCTL_ENT_DLY_MAX); |
| |
| printf("RNG Instantation Failure - Entropy delay (%d)\n", ent_delay); |
| ret = ERROR_ANY; |
| |
| end_instantation: |
| return ret; |
| } |
| |
| static void rng_init(void) |
| { |
| int ret; |
| |
| ret = jr_reset(); |
| if (ret != SUCCESS) { |
| printf("Error CAAM JR reset\n"); |
| return; |
| } |
| |
| ret = do_instantiation(); |
| |
| if (ret != SUCCESS) |
| printf("Error do_instantiation\n"); |
| |
| jr_reset(); |
| |
| return; |
| } |
| |