This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new b4b9a180c0 Add ATSAMA5D2/D4 Secure Fuse Controller (SFC) driver
b4b9a180c0 is described below
commit b4b9a180c06b3239d05003f86725a1c867d2f246
Author: TimJTi <[email protected]>
AuthorDate: Thu Apr 27 17:59:04 2023 +0100
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(-)
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 <nuttx/config.h>
+#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 <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <errno.h>
+#include <debug.h>
+#include <sys/param.h>
+
+#include <nuttx/fs/fs.h>
+#include <nuttx/irq.h>
+#include <nuttx/kmalloc.h>
+#include <nuttx/mutex.h>
+#include <nuttx/efuse/efuse.h>
+#include <nuttx/efuse/sama5_sfc_fuses.h>
+#include <nuttx/wdog.h>
+
+#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 <nuttx/config.h>
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#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 */