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