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


Reply via email to