From b4b9a180c06b3239d05003f86725a1c867d2f246 Mon Sep 17 00:00:00 2001 From: TimJTi <56726697+TimJTi@users.noreply.github.com> Date: Thu, 27 Apr 2023 17:59:04 +0100 Subject: [PATCH] Add ATSAMA5D2/D4 Secure Fuse Controller (SFC) driver --- arch/arm/src/sama5/Kconfig | 1 + arch/arm/src/sama5/Make.defs | 4 + arch/arm/src/sama5/hardware/sam_sfc.h | 67 ++ arch/arm/src/sama5/sam_sfc.c | 935 ++++++++++++++++++++++++++ arch/arm/src/sama5/sam_sfc.h | 75 +++ drivers/efuse/efuse.c | 8 +- include/nuttx/efuse/efuse.h | 13 +- include/nuttx/efuse/sama5_sfc_fuses.h | 247 +++++++ 8 files changed, 1344 insertions(+), 6 deletions(-) create mode 100644 arch/arm/src/sama5/hardware/sam_sfc.h create mode 100644 arch/arm/src/sama5/sam_sfc.c create mode 100644 arch/arm/src/sama5/sam_sfc.h create mode 100644 include/nuttx/efuse/sama5_sfc_fuses.h diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index 23882fa104..ae10098560 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -814,6 +814,7 @@ config SAMA5_ARM config SAMA5_FUSE bool "Fuse Controller (FUSE)" default n + depends on SAMA5_HAVE_FUSE config SAMA5_MPDDRC bool "MPDDR controller (MPDDRC)" diff --git a/arch/arm/src/sama5/Make.defs b/arch/arm/src/sama5/Make.defs index 8600c9b1da..3b67d9f5cf 100644 --- a/arch/arm/src/sama5/Make.defs +++ b/arch/arm/src/sama5/Make.defs @@ -221,6 +221,10 @@ CHIP_CSRCS += sam_tickless.c endif endif +ifeq ($(CONFIG_SAMA5_SFC), y) +CHIP_CSRCS += sam_sfc.c +endif + ifeq ($(CONFIG_SAMA5_EBICS0_NAND),y) CHIP_CSRCS += sam_nand.c sam_pmecc.c sam_gf512.c sam_gf1024.c else diff --git a/arch/arm/src/sama5/hardware/sam_sfc.h b/arch/arm/src/sama5/hardware/sam_sfc.h new file mode 100644 index 0000000000..48e266dbe5 --- /dev/null +++ b/arch/arm/src/sama5/hardware/sam_sfc.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * arch/arm/src/sama5/hardware/sam_sfc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_SFC_H +#define __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_SFC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include "hardware/sam_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(ATSAMA5D2) || defined(ATSAMA5D4) + +/* SFC Register Offsets *****************************************************/ + +#define SAM_SFC_KR_OFFSET (0x0000) /* Key register */ +#define SAM_SFC_MR_OFFSET (0x0004) /* Mode register */ + /* 0x008-0x00c reserved */ +#define SAM_SFC_IER_OFFSET (0x0010) /* Interrupt enable register */ +#define SAM_SFC_IDR_OFFSET (0x0014) /* Interrupr disable register */ +#define SAM_SFC_IMR_OFFSET (0x0018) /* Interrupt mask register */ +#define SAM_SFC_SR_OFFSET (0x001c) /* Status register */ +#define SAM_SFC_DR_OFFSET(n) ((0x0020)+((n) << (2))) +#define SAM_SFC_SR_PGMC_SHIFT (0) +#define SAM_SFC_SR_PGMC ((1) << SAM_SFC_SR_PGMC_SHIFT) + /* Programming completed */ +#define SAM_SFC_SR_PGMF_SHIFT (1) +#define SAM_SFC_SR_PGMF ((1) << SAM_SFC_SR_PGMF_SHIFT) + /* Programming failed */ + +#define SAM_SFC_KR (SAM_SFC_VBASE + SAM_SFC_KR_OFFSET) +#define SAM_SFC_MR (SAM_SFC_VBASE + SAM_SFC_MR_OFFSET) +#define SAM_SFC_MR_MASK (1) +#define SAM_SFC_IER (SAM_SFC_VBASE + SAM_SFC_IER_OFFSET) +#define SAM_SFC_IDR (SAM_SFC_VBASE + SAM_SFC_IDR_OFFSET) +#define SAM_SFC_IMR (SAM_SFC_VBASE + SAM_SFC_IMR_OFFSET) +#define SAM_SFC_SR (SAM_SFC_VBASE + SAM_SFC_SR_OFFSET) + +#define SAM_SFC_DR(n) (SAM_SFC_VBASE + SAM_SFC_DR_OFFSET(n)) + +#define SAM_SFC_KEYCODE (0x00fb) /* Keycode to allow write */ + +#endif /* if defined(ATSAMA5D2) || defined(ATSAMA5D4) */ +#endif /* __ARCH_ARM_SRC_SAMA5_HARDWARE_SAM_SFC_H */ diff --git a/arch/arm/src/sama5/sam_sfc.c b/arch/arm/src/sama5/sam_sfc.c new file mode 100644 index 0000000000..73b9869277 --- /dev/null +++ b/arch/arm/src/sama5/sam_sfc.c @@ -0,0 +1,935 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_sfc.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "arm_internal.h" +#include "sam_sfc.h" +#include "hardware/sam_sfc.h" + +#ifdef CONFIG_SAMA5_SFC + +#ifdef ATSAMA5D4 +#warning SAMA5 SFC functions have NOT been checked on a board using SAMA5D4 +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SAM_SFC_DR_LEN 32 /* Each data register is 32 bits */ + +#define SFC_WDOG_DELAY MSEC2TICK(100) /* exact value not important. + * This is to prevent getting stuck + * while burning fuses. */ + +/**************************************************************************** + * Private Type Definitions + ****************************************************************************/ + +/* This structure describes the state of the upper half driver */ + +struct sama5_sfc_upperhalf_s +{ + mutex_t lock; /* Supports mutual exclusion */ + char *path; /* Registration path */ + struct efuse_lowerhalf_s *lower; /* Pointer to efuse_lowerhalf_s */ +}; + +/**************************************************************************** + * Name: sam_sfc_func_proc_t + * + * Description: + * This is type of function that will handle the sfc efuse field register. + * + * Input Parameters: + * num_reg - The register number. + * bit_start - Start bit in the register. + * bit_count - The number of bits used in the register. + * arr - A pointer to an array or variable. + * bits_counter - Counter bits. + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +typedef int (*sam_sfc_func_proc_t)(uint32_t num_reg, + int bit_start, + int bit_count, + void *arr, int *bits_counter); + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int sama5_sfc_lower_ioctl(struct efuse_lowerhalf_s *lower, + int cmd, unsigned long arg); +static int sama5_sfc_lower_read(struct efuse_lowerhalf_s *lower, + const efuse_desc_t *field[], + uint8_t *data, + size_t bits_len); +static int sama5_sfc_lower_write(struct efuse_lowerhalf_s *lower, + const efuse_desc_t *field[], + const uint8_t *data, + size_t bits_len); +static int sam_sfc_get_field_size(const efuse_desc_t *field[]); +static bool sam_sfc_check_range_of_bits(int offset_in_bits, int size_bits); +static int sam_sfc_get_number_of_items(int bits, int size_of_base); +static int sam_sfc_fill_buff(uint32_t num_reg, int bit_offset, + int bit_count, void *arr_out, + int *bits_counter); +static int sam_sfc_process(const efuse_desc_t *field[], void *ptr, + size_t ptr_size_bits, + sam_sfc_func_proc_t func_proc); +static int sam_sfc_get_reg_num(int bit_offset, int bit_count, int i_reg); +static int sam_sfc_get_count_bits_in_reg(int bit_offset, int bit_count, + int i_reg); +static uint32_t sam_sfc_read_reg(uint32_t num_reg); +static void sam_sfc_mask_read(void); +static uint32_t sam_sfc_get_mask(uint32_t bit_count, uint32_t shift); +static int sama5_sfc_burn_efuses(const efuse_desc_t *field[], + const void *src, size_t src_size_bits); +static int sama5_sfc_write_reg(uint32_t num_reg, uint32_t value); +static int sama5_sfc_write_blob(uint32_t num_reg, int bit_offset, + int bit_count, void *arr_in, + int *bits_counter); +static uint32_t sama5_sfc_fill_reg(int bit_start_in_reg, + int bit_count_in_reg, uint8_t *blob, + int *filled_bits_blob); +static void sfc_timeout(wdparm_t arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* EFUSE lower-half driver methods */ + +static const struct efuse_ops_s sama5_sfc_lower_ops = +{ + .read_field = sama5_sfc_lower_read, + .write_field = sama5_sfc_lower_write, + .ioctl = sama5_sfc_lower_ioctl, +}; + +static struct efuse_lowerhalf_s g_sama5_sfc_lowerhalf_s = +{ + .ops = &sama5_sfc_lower_ops, +}; + +static uint32_t g_start_sam_sfc_reg[SFC_DR_END] = +{ + SAM_SFC_DR(0), + SAM_SFC_DR(1), + SAM_SFC_DR(2), + SAM_SFC_DR(3), + SAM_SFC_DR(4), + SAM_SFC_DR(5), + SAM_SFC_DR(6), + SAM_SFC_DR(7), + SAM_SFC_DR(8), + SAM_SFC_DR(9), + SAM_SFC_DR(10), + SAM_SFC_DR(11), + SAM_SFC_DR(12), + SAM_SFC_DR(13), + SAM_SFC_DR(14), +#ifdef ATSAMA5D2 + SAM_SFC_DR(15), +#endif +}; + +struct wdog_s wdog; /* watchdog timer for efuse burning */ + +bool waiting; /* Waiting for efuse burning to be done */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: sama5_sfc_lower_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + * Description: + * Initialize the efuse driver. The efuse is initialized + * and registered as 'devpath'. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of + * the "lower-half" driver state structure + * cmd - The ioctl command value + * arg - The optional argument that accompanies the 'cmd' + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -ENOTTY. + * + ****************************************************************************/ + +static int sama5_sfc_lower_ioctl(struct efuse_lowerhalf_s *lower, + int cmd, + unsigned long arg) +{ + switch (cmd) + { + /* We don't have proprietary EFUSE ioctls */ + + case EFUSEIOC_SAMA5_MASK: + { + minfo("Masking fuses register \n"); + sam_sfc_mask_read(); + return OK; + } + break; + default: + { + minfo("Unrecognized cmd: %d\n", cmd); + return -ENOTTY; + } + break; + } + + return OK; +} + +/**************************************************************************** + * Name: sam_sfc_get_field_size + * + * Description: + * Get the length of the field in bits. + * + * Input Parameters: + * field - Pointer to the structure describing the efuse field + * + * Returned Value: + * The length of the field in bits. + * + ****************************************************************************/ + +static int sam_sfc_get_field_size(const efuse_desc_t *field[]) +{ + int bits_counter = 0; + int i; + + if (field != NULL) + { + i = 0; + + while (field[i] != NULL) + { + bits_counter += field[i]->bit_count; + ++i; + } + } + + return bits_counter; +} + +/**************************************************************************** + * Name: sam_sfc_check_range_of_bits + * + * Description: + * Check range of bits for any coding scheme. + * + * Input Parameters: + * offset_in_bits - The bit offset related to beginning of efuse + * size_bits - The length of bit field + * + * Returned Value: + * True is returned if the bits offset matched. Otherwise false. + * + ****************************************************************************/ + +static bool sam_sfc_check_range_of_bits(int offset_in_bits, int size_bits) +{ + int bit_offset = offset_in_bits % SAM_SFC_EFUSE_MAX_LEN; + int max_num_bit = bit_offset + size_bits; + + if (max_num_bit > SAM_SFC_EFUSE_MAX_LEN) + { + return false; + } + + return true; +} + +/**************************************************************************** + * Name: sam_sfc_get_number_of_items + * + * Description: + * Returns the number of array elements for placing these bits in an array + * with the length of each element equal to size_of_base. + * + * Input Parameters: + * bits - The number of bits required + * size_of_base - The base of bits required + * + * Returned Value: + * The number of array elements. + * + ****************************************************************************/ + +static int sam_sfc_get_number_of_items(int bits, int size_of_base) +{ + return bits / size_of_base + (bits % size_of_base > 0 ? 1 : 0); +} + +/**************************************************************************** + * Name: sam_sfc_get_reg_num + * + * Description: + * Returns the number of bits in the register. + * + * Input Parameters: + * bit_offset - Start bit in register + * bit_count - The number of bits required + * i_reg - The register number + * + * Returned Value: + * The register number in the array. + * + ****************************************************************************/ + +static int sam_sfc_get_reg_num(int bit_offset, int bit_count, int i_reg) +{ + uint32_t bit_start = (bit_offset % SAM_SFC_EFUSE_MAX_LEN); + int num_reg = i_reg + bit_start / SAM_SFC_DR_LEN; + + if (num_reg > (bit_start + bit_count - 1) / SAM_SFC_DR_LEN) + { + return -1; + } + + return num_reg; +} + +/**************************************************************************** + * Name: sam_sfc_get_count_bits_in_reg + * + * Description: + * Returns the number of bits in the register. + * + * Input Parameters: + * bit_offset - Start bit in register + * bit_count - The number of bits required + * i_reg - The register number + * + * Returned Value: + * The number of bits in the register. + * + ****************************************************************************/ + +static int sam_sfc_get_count_bits_in_reg(int bit_offset, int bit_count, + int i_reg) +{ + int ret_count = 0; + int num_reg = 0; + int bit_start = (bit_offset % SAM_SFC_DR_LEN); + int last_used_bit = (bit_start + bit_count - 1); + int num_bit; + + for (num_bit = bit_start; num_bit <= last_used_bit; ++num_bit) + { + ++ret_count; + if ((((num_bit + 1) % SAM_SFC_DR_LEN) == 0) || + (num_bit == last_used_bit)) + { + if (i_reg == num_reg) + { + return ret_count; + } + + ++num_reg; + ret_count = 0; + } + } + + return 0; +} + +/**************************************************************************** + * Name: sam_sfc_process + * + * Description: + * Processes the field by calling the passed function. + * + * Input Parameters: + * field - A pointer to describing the fields of efuse + * ptr - A pointer to array that will contain the result + * ptr_size_bits - The number of bits required to read + * func_proc - A callback for handle the efuse field register + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +static int sam_sfc_process(const efuse_desc_t *field[], void *ptr, + size_t ptr_size_bits, + sam_sfc_func_proc_t func_proc) +{ + int err = OK; + int bits_counter = 0; + int field_len; + int req_size; + int i = 0; + int i_reg; + int num_reg; + int num_bits; + int bit_offset; + + /* get and check size */ + + field_len = sam_sfc_get_field_size(field); + req_size = (ptr_size_bits == 0) ? field_len : \ + MIN(ptr_size_bits, field_len); + + while (err == OK && req_size > bits_counter && field[i] != NULL) + { + i_reg = 0; + + if (sam_sfc_check_range_of_bits(field[i]->bit_offset, + field[i]->bit_count) == false) + { + minfo("Range of data does not match the coding scheme"); + err = -EINVAL; + } + + while (err == OK && req_size > bits_counter && + (num_reg = sam_sfc_get_reg_num(field[i]->bit_offset, + field[i]->bit_count, i_reg)) != -1) + { + num_bits = sam_sfc_get_count_bits_in_reg(field[i]->bit_offset, + field[i]->bit_count, + i_reg); + bit_offset = field[i]->bit_offset; + + if ((bits_counter + num_bits) > req_size) + { + /* Limits the length of the field */ + + num_bits = req_size - bits_counter; + } + + err = func_proc(num_reg, bit_offset, num_bits, ptr, &bits_counter); + ++i_reg; + } + + i++; + } + + DEBUGASSERT(bits_counter <= req_size); + return err; +} + +/**************************************************************************** + * Name: sama5_sfc_fill_reg + * + * Description: + * Fill efuse register from array. + * + * Input Parameters: + * bit_start_in_reg - Start bit in register + * bit_count_in_reg - The number of bits required to write + * blob - A pointer that will contain the value + * filled_bits_blob - A pointer that will contain the bits counter + * + * Returned Value: + * The value to write efuse register. + * + ****************************************************************************/ + +static uint32_t sama5_sfc_fill_reg(int bit_start_in_reg, + int bit_count_in_reg, uint8_t *blob, + int *filled_bits_blob) +{ + uint32_t reg_to_write = 0; + uint32_t temp_blob_32; + int shift_reg; + int shift_bit = (*filled_bits_blob) % 8; + + if (shift_bit != 0) + { + temp_blob_32 = blob[(*filled_bits_blob) / 8] >> shift_bit; + shift_bit = MIN((8 - shift_bit), bit_count_in_reg); + + reg_to_write = temp_blob_32 & sam_sfc_get_mask(shift_bit, 0); + (*filled_bits_blob) += shift_bit; + bit_count_in_reg -= shift_bit; + } + + shift_reg = shift_bit; + + while (bit_count_in_reg > 0) + { + temp_blob_32 = blob[(*filled_bits_blob) / 8]; + shift_bit = MIN(bit_count_in_reg, 8); + reg_to_write |= (temp_blob_32 & \ + sam_sfc_get_mask(shift_bit, 0)) << shift_reg; + (*filled_bits_blob) += shift_bit; + bit_count_in_reg -= shift_bit; + shift_reg += 8; + }; + + return reg_to_write << bit_start_in_reg; +} + +/**************************************************************************** + * Name: sfc_timeout + * + * Description: + * Called if the efuse burn watchdog times out + * + * Input Parameters: none + * + * Returned Value: none + * + ****************************************************************************/ + +static void sfc_timeout(wdparm_t arg) +{ + waiting = false; +} + +/**************************************************************************** + * Name: sama5_sfc_write_reg + * + * Description: + * Write value to be written to efuse register, preceded by magic number + * to allow the write to occur. + * + * Input Parameters: + * num_reg - The register number in the block + * value - Value to write + * + * Returned Value: + * Success or error code. + * + ****************************************************************************/ + +static int sama5_sfc_write_reg(uint32_t num_reg, uint32_t value) +{ + uint32_t regval; + int ret; + + DEBUGASSERT(num_reg < SFC_DR_END); + + /* The register can be written in parts so we combine the new value + * with the one already available, if the new value can actually + * be programmed + */ + + regval = getreg32(g_start_sam_sfc_reg[num_reg]); + + if (regval == value) + { + /* it's the same! */ + + return OK; + } + + /* Write Key Code value then the value to be burned */ + + putreg32(SAM_SFC_KEYCODE, SAM_SFC_KR); + putreg32(regval | value, g_start_sam_sfc_reg[num_reg]); + + waiting = true; + ret = wd_start(&wdog, SFC_WDOG_DELAY, sfc_timeout, (wdparm_t)0); + if (ret < 0) + { + merr("ERROR: wd_start failed: %d\n", ret); + } + + while (waiting) + { + if (getreg32(SAM_SFC_SR) & SAM_SFC_SR_PGMC) + { + break; + } + } + + wd_cancel(&wdog); + + if (!waiting) + { + /* we got here because of a watchdog timeout */ + + merr("ERROR: efuse burning timed out\n"); + return -ETIMEDOUT; + } + + regval = getreg32(SAM_SFC_SR); + if (regval & SAM_SFC_SR_PGMF) + { + /* There was an internal error burning the fuses */ + + merr("ERROR: Error burning efuses\n"); + + return -EIO; + } + + return OK; +} + +/**************************************************************************** + * Name: sama5_sfc_write_blob + * + * Description: + * Fill register from array and write. + * + * Input Parameters: + * num_reg - The register number + * bit_offset - Start bit in register + * bit_count - The number of bits required to read + * arr_in - A pointer to array that will contain the value of writing + * bits_counter - A pointer that will contain the bits counter of writing + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -ERROR. + * + ****************************************************************************/ + +static int sama5_sfc_write_blob(uint32_t num_reg, int bit_offset, + int bit_count, void *arr_in, + int *bits_counter) +{ + uint32_t curval; + uint32_t mask; + + uint32_t bit_start = (bit_offset % SAM_SFC_DR_LEN); + uint32_t reg_to_write = sama5_sfc_fill_reg(bit_start, bit_count, + (uint8_t *)arr_in, + bits_counter); + + curval = getreg32(g_start_sam_sfc_reg[num_reg]); + + /* We cannot burn a 1 back to a 0 */ + + mask = sam_sfc_get_mask(bit_count, bit_start); + + if (!(curval & ~reg_to_write & mask)) + { + return sama5_sfc_write_reg(num_reg, reg_to_write); + } + else + { + merr("ERROR: requested value cannot be burned\n"); + return -EINVAL; + } +} + +/**************************************************************************** + * Name: sama5_sfc_burn_efuses + * + * Description: + * Write data fields to SFC EFUSE. + * + * Input Parameters: + * field - A pointer to describing the fields of efuse + * src - A pointer to array that contains the data for writing + * src_size_bits - The number of bits required to write + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +static int sama5_sfc_burn_efuses(const efuse_desc_t *field[], + const void *src, size_t src_size_bits) +{ + int err = OK; + + if (field == NULL || src == NULL || src_size_bits == 0) + { + err = -EINVAL; + } + else + { + err = sam_sfc_process(field, (void *)src, src_size_bits, + sama5_sfc_write_blob); + } + + return err; +} + +/**************************************************************************** + * Name: sama5_sfc_lower_read + * + * Description: + * Read value from EFUSE, writing it into an array. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of + * the "lower-half" driver state structure + * field - A pointer to describing the fields of efuse + * data - A pointer to array that contains the data for reading + * bits_len - The number of bits required to read + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +static int sama5_sfc_lower_read(struct efuse_lowerhalf_s *lower, + const efuse_desc_t *field[], + uint8_t *data, + size_t bits_len) +{ + int err = OK; + int num_registers; + + if (field == NULL || data == NULL || bits_len == 0) + { + err = -EINVAL; + } + else + { + num_registers = sam_sfc_get_number_of_items(bits_len, 8); + memset((uint8_t *)data, 0, num_registers); + + err = sam_sfc_process(field, data, bits_len, sam_sfc_fill_buff); + } + + return err; +} + +/**************************************************************************** + * Name: sama5_sfc_lower_write + * + * Description: + * Write array to EFUSE. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of + * the "lower-half" driver state structure + * field - A pointer to describing the fields of efuse + * data - A pointer to array that contains the data for writing + * bits_len - The number of bits required to write + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +static ssize_t sama5_sfc_lower_write(struct efuse_lowerhalf_s *lower, + const efuse_desc_t *field[], + const uint8_t *data, + size_t bits_len) +{ + /* Write the blob data to the field */ + + return sama5_sfc_burn_efuses(field, data, bits_len); +} + +/**************************************************************************** + * Name: sam_sfc_read_reg + * + * Description: + * Read efuse register. + * + * Input Parameters: + * num_reg - The register number + * + * Returned Value: + * Return the value in the efuse register. + * + ****************************************************************************/ + +static uint32_t sam_sfc_read_reg(uint32_t num_reg) +{ + uint32_t value; + + DEBUGASSERT(num_reg < SFC_DR_END); + + value = getreg32(g_start_sam_sfc_reg[num_reg]); + + return value; +} + +/**************************************************************************** + * Name: sam_sfc_mask_read + * + * Description: + * Read efuse register. + * + ****************************************************************************/ + +static void sam_sfc_mask_read(void) +{ + uint32_t regval; + + regval = getreg32(SAM_SFC_MR); + regval |= SAM_SFC_MR_MASK; + + putreg32(regval, SAM_SFC_MR); +} + +/**************************************************************************** + * Name: sam_sfc_get_mask + * + * Description: + * Return mask with required the number of ones with shift. + * + * Input Parameters: + * bit_count - The number of bits required + * shift - The shift of programmed as, '1' or '0' + * + * Returned Value: + * The mask with required the number of ones with shift. + * + ****************************************************************************/ + +static uint32_t sam_sfc_get_mask(uint32_t bit_count, uint32_t shift) +{ + uint32_t mask; + + if (bit_count != SAM_SFC_DR_LEN) + { + mask = (1 << bit_count) - 1; + } + else + { + mask = 0xffffffff; + } + + return mask << shift; +} + +/**************************************************************************** + * Name: sam_sfc_fill_buff + * + * Description: + * Read efuse register and write this value to array. + * + * Input Parameters: + * num_reg - The register number + * bit_offset - Start bit in register + * bit_count - The number of bits required to read + * arr_out - A pointer to array that will contain the result + * bits_counter - A pointer that will contain the bits counter of reading + * + * Returned Value: + * Zero (OK) is returned on success. + * + ****************************************************************************/ + +static int sam_sfc_fill_buff(uint32_t num_reg, int bit_offset, + int bit_count, void *arr_out, int *bits_counter) +{ + uint8_t *blob = (uint8_t *)arr_out; + uint32_t bit_start = (bit_offset % SAM_SFC_DR_LEN); + uint32_t reg_val = sam_sfc_read_reg(num_reg); + uint64_t reg_of_aligned_bits = (reg_val >> bit_start) & \ + sam_sfc_get_mask(bit_count, 0); + int sum_shift = 0; + int shift_bit = (*bits_counter) % 8; + + if (shift_bit != 0) + { + blob[(*bits_counter) / 8] |= (uint8_t)(reg_of_aligned_bits << \ + shift_bit); + shift_bit = ((8 - shift_bit) < bit_count) ? (8 - shift_bit) : \ + bit_count; + (*bits_counter) += shift_bit; + bit_count -= shift_bit; + } + + while (bit_count > 0) + { + sum_shift += shift_bit; + blob[(*bits_counter) / 8] |= (uint8_t)(reg_of_aligned_bits >> \ + sum_shift); + shift_bit = (bit_count > 8) ? 8 : bit_count; + (*bits_counter) += shift_bit; + bit_count -= shift_bit; + }; + + return OK; +} + +/**************************************************************************** + * Name: sama5_sfc_initialize + * + * Description: + * Initialize the sfc efuse driver. The efuse is initialized + * and registered as 'devpath'. + * + * Input Parameters: + * devpath - The full path to the efuse device. + * This should be of the form /dev/efuse + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -EEXIST (error). + * + ****************************************************************************/ + +int sama5_sfc_initialize(const char *devpath) +{ + struct sama5_sfc_upperhalf_s *upper = NULL; + struct efuse_lowerhalf_s *lower; + int ret = OK; + + DEBUGASSERT(devpath != NULL); + + lower = &g_sama5_sfc_lowerhalf_s; + + /* Register the efuse upper driver */ + + upper = efuse_register(devpath, lower); + + if (upper == NULL) + { + /* The actual cause of the failure may have been a failure to allocate + * perhaps a failure to register the efuser driver (such as if the + * 'devpath' were not unique). We know here but we return EEXIST to + * indicate the failure (implying the non-unique devpath). + */ + + ret = -EEXIST; + } + + return ret; +} + +#endif /* CONFIG_SAMA5_SFC */ diff --git a/arch/arm/src/sama5/sam_sfc.h b/arch/arm/src/sama5/sam_sfc.h new file mode 100644 index 0000000000..b2e277a9fd --- /dev/null +++ b/arch/arm/src/sama5/sam_sfc.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * arch/arm/src/sama5/sam_sfc.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_SAMA5_SAM_SFC_H +#define __ARCH_ARM_SRC_SAMA5_SAM_SFC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include + +#include "chip.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/**************************************************************************** + * Inline Functions + ****************************************************************************/ + +#ifndef __ASSEMBLY__ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int sama5_sfc_initialize(const char *devpath); +void sama5_sfc_unregister(void *handle); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_ARM_SRC_SAMA5_SAM_SFC_H */ diff --git a/drivers/efuse/efuse.c b/drivers/efuse/efuse.c index f790ca2d5f..20102015ed 100644 --- a/drivers/efuse/efuse.c +++ b/drivers/efuse/efuse.c @@ -170,8 +170,8 @@ static int efuse_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case EFUSEIOC_READ_FIELD: { - FAR struct efuse_param *param = - (FAR struct efuse_param *)((uintptr_t)arg); + FAR struct efuse_param_s *param = + (FAR struct efuse_param_s *)((uintptr_t)arg); /* Read the efuse */ @@ -200,8 +200,8 @@ static int efuse_ioctl(FAR struct file *filep, int cmd, unsigned long arg) case EFUSEIOC_WRITE_FIELD: { - FAR struct efuse_param *param = - (FAR struct efuse_param *)((uintptr_t)arg); + FAR struct efuse_param_s *param = + (FAR struct efuse_param_s *)((uintptr_t)arg); /* Write the efuse */ diff --git a/include/nuttx/efuse/efuse.h b/include/nuttx/efuse/efuse.h index d4239ae46c..d40559727d 100644 --- a/include/nuttx/efuse/efuse.h +++ b/include/nuttx/efuse/efuse.h @@ -57,6 +57,15 @@ #define EFUSEIOC_WRITE_FIELD _EFUSEIOC(0x0002) +/* Command: EFUSEIOC_MASK + * Description: Masks fuse registers to prevent them from being read. + * Used by ATSAMA5D2 and ATSAMA5D4. + * Arguments: None + * Return: Zero (OK) for success. + */ + +#define EFUSEIOC_MASK _EFUSEIOC(0x0003) + /**************************************************************************** * Public Types ****************************************************************************/ @@ -95,10 +104,10 @@ typedef struct efuse_desc_s efuse_desc_t; * on. */ -struct efuse_param +struct efuse_param_s { FAR const efuse_desc_t **field; - size_t size; + size_t size; FAR uint8_t *data; }; diff --git a/include/nuttx/efuse/sama5_sfc_fuses.h b/include/nuttx/efuse/sama5_sfc_fuses.h new file mode 100644 index 0000000000..60fe1c34e2 --- /dev/null +++ b/include/nuttx/efuse/sama5_sfc_fuses.h @@ -0,0 +1,247 @@ +/**************************************************************************** + * include/nuttx/efuse/sama5_sfc_fuses.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_EFUSE_SAMA5_FUSES_H +#define __INCLUDE_NUTTX_EFUSE_SAMA5_FUSES_H + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#if defined(ATSAMA5D2) +#define SAM_SFC_EFUSE_MAX_LEN 544 /* Max length of sfc area. */ +#elif defined ATSAMA5D4 +#define SAM_SFC_EFUSE_MAX_LEN 512 /* Max length of sfc area. */ +#endif + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +/**************************************************************************** + * Type Definitions + ****************************************************************************/ + +#if defined(CONFIG_EFUSE) && defined(CONFIG_SAMA5_SFC) + +/* enum of SFC blocks for SAMA5 */ + +enum +{ + SFC_DR0 = 0, + SFC_DR1, + SFC_DR2, + SFC_DR3, + SFC_DR4, + SFC_DR5, + SFC_DR6, + SFC_DR7, + SFC_DR8, + SFC_DR9, + SFC_DR10, + SFC_DR11, + SFC_DR12, + SFC_DR13, + SFC_DR14, +#ifdef ATSAMA5D2 + SFC_DR15, + SFC_BOOT, + SFC_JTAG, + SFC_SEC_BOOT, +#else + SFC_S, + SFC_MD, + SFC_SECURE_DEBUG, + SFC_JTAG_DIS, +#endif + SFC_DR_END, +}; + +/* Generic descriptions for the SAM SFC fuses, 32 bits wide. + * These can be replaced by user definitions in board code if required. + */ + +static const efuse_desc_t SAMA5_SFC_DATA0[] = +{ + { + 0, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA1[] = +{ + { + 32, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA2[] = +{ + { + 64, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA3[] = +{ + { + 96, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA4[] = +{ + { + 128, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA5[] = +{ + { + 160, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA6[] = +{ + { + 192, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA7[] = +{ + { + 224, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA8[] = +{ + { + 256, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA9[] = +{ + { + 288, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA10[] = +{ + { + 320, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA11[] = +{ + { + 352, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA12[] = +{ + { + 384, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA13[] = +{ + { + 416, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_DATA14[] = +{ + { + 448, 32 + }, +}; + #ifdef ATSAMA5D2 +static const efuse_desc_t SAMA5_SFC_DATA15[] = +{ + { + 480, 32 + }, +}; + +static const efuse_desc_t SAMA5_SFC_BOOT_CFG[] = +{ + { + 512, 30 + }, +}; + +static const efuse_desc_t SAMA5_SFC_JTAG_DIS[] = +{ + { + 542, 1 + }, +}; + +static const efuse_desc_t SAMA5_SFC_SECURE_DEBUG[] = +{ + { + 543, 1 + }, +}; + +#else + +static const efuse_desc_t SAMA5_SFC_S[] = +{ + { + 480, 1 + }, +}; + +static const efuse_desc_t SAMA5_SFC_MD[] = +{ + { + 482, 1 + }, +}; + +static const efuse_desc_t SAMA5_SFC_SECURE_DEBUG[] = +{ + { + 510, 1 + }, +}; + +static const efuse_desc_t SAMA5_SFC_JTAG_DIS[] = +{ + { + 511, 1 + }, +}; + +#endif + +#endif /* defined(CONFIG_EFUSE) && defined(CONFIG_SAMA5_SFC) */ +#endif /* __INCLUDE_NUTTX_EFUSE_SAMA5_FUSES_H */