59bbcfdb创建于 4月21日历史提交
/****************************************************************************
 * arch/arm/src/sama5/sam_mcan.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.
 *
 ****************************************************************************/

/* References:
 *   SAMA5D2 Series Data Sheet
 *   Atmel sample code
 *   Based on pre-existing SAMV71 NuttX mcan driver
 */

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>

#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <stdint.h>
#include <inttypes.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>

#include <arch/board/board.h>
#include <nuttx/arch.h>
#include <nuttx/mutex.h>
#include <nuttx/spinlock.h>
#include <nuttx/semaphore.h>
#include <nuttx/can/can.h>

#include "arm_internal.h"

#include "hardware/sam_sfr.h"
#include "hardware/sam_pinmap.h"
#include "sam_periphclks.h"
#include "sam_pio.h"
#include "sam_mcan.h"

#if defined(CONFIG_CAN) && (defined(CONFIG_SAMA5_MCAN0) || \
            defined(CONFIG_SAMA5_MCAN1))

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

/* dedicated RS and TX buffers are not supported */

#define DEDICATED_BUFFERS_NOT_USED

/* Clock source */

/* PCK5 is the programmable clock source, common to all MCAN controllers */

#if defined(CONFIG_SAMA5_MCAN_CLKSRC_SLOW)
#  define SAMA5_MCAN_CLKSRC           PMC_PCR_GCKCSS_SLOW
#  define SAMA5_MCAN_CLKSRC_FREQUENCY BOARD_SLOWCLK_FREQUENCY
#elif defined(CONFIG_SAMA5_MCAN_CLKSRC_PLLA)
#  define SAMA5_MCAN_CLKSRC           PMC_PCR_GCKCSS_PLLA
#  define SAMA5_MCAN_CLKSRC_FREQUENCY BOARD_PLLA_FREQUENCY
#elif defined(CONFIG_SAMA5_MCAN_CLKSRC_UPLL)
#  define SAMA5_MCAN_CLKSRC           PMC_PCR_GCKCSS_UPLL
#  define SAMA5_MCAN_CLKSRC_FREQUENCY BOARD_UPLL_FREQUENCY
#elif defined(CONFIG_SAMA5_MCAN_CLKSRC_MCK)
#  define SAMA5_MCAN_CLKSRC           PMC_PCR_GCKCSS_MCK
#  define SAMA5_MCAN_CLKSRC_FREQUENCY BOARD_MCK_FREQUENCY
#elif defined(CONFIG_SAMA5_MCAN_CLKSRC_MAIN)
#  define SAMA5_MCAN_CLKSRC           PMC_PCR_GCKCSS_MAIN
#  define SAMA5_MCAN_CLKSRC_FREQUENCY BOARD_MAINOSC_FREQUENCY
#else
#  error no mcan clock defined!
#endif

#ifndef CONFIG_SAMA5_MCAN_CLKSRC_PRESCALER
#  define CONFIG_SAMA5_MCAN_CLKSRC_PRESCALER 1
#endif

#define SAMA5_MCANCLK_FREQUENCY \
  (SAMA5_MCAN_CLKSRC_FREQUENCY / CONFIG_SAMA5_MCAN_CLKSRC_PRESCALER)

/* Buffer Alignment.
 *
 * The MCAN peripheral does not require any data be aligned. However, if
 * the data cache is enabled then alignment is required. That is because
 * the data will need to be invalidated and that cache invalidation will
 * occur in multiples of full change lines.
 */

#ifdef CONFIG_ARCH_DCACHE
#  define MCAN_ALIGN        ARMV7A_DCACHE_LINESIZE
#  define MCAN_ALIGN_MASK   (MCAN_ALIGN - 1)
#  define MCAN_ALIGN_UP(n)  (((n) + MCAN_ALIGN_MASK) & ~MCAN_ALIGN_MASK)
#  define SAM_MCAN_SFR_SHIFT 16
#  define SAM_MCAN0_SFR_MASK 0xffff0000
#  define SAM_MCAN1_SFR_MASK 0x0000ffff
#  define SAM_MCAN_SFR_MSB_MASK 0xffff0000
#  define SAM_MCAN_SFR_GET_MSB(n) (( (uint32_t)(n) & (SAM_MCAN_SFR_MSB_MASK)) >> SAM_MCAN_SFR_SHIFT)
#else
#  define MCAN_ALIGN        (0)
#  define MCAN_ALIGN_MASK   (0)
#  define MCAN_ALIGN_UP(n)  (n)
#endif

/* General Configuration */

#ifndef CONFIG_CAN_TXREADY
#  warning WARNING!!! CONFIG_CAN_TXREADY is required by this driver
#endif

/* MCAN0 Configuration */

#ifdef CONFIG_SAMA5_MCAN0

  /* Bit timing */

#  define MCAN0_NTSEG1  (CONFIG_SAMA5_MCAN0_PROPSEG + CONFIG_SAMA5_MCAN0_PHASESEG1 - 1)
#  define MCAN0_NTSEG2  (CONFIG_SAMA5_MCAN0_PHASESEG2 - 1)
#  define MCAN0_NBRP    ((uint32_t)(((float)SAMA5_MCANCLK_FREQUENCY / \
                         ((float)(MCAN0_NTSEG1 + MCAN0_NTSEG2 + 3) * \
                          (float)CONFIG_SAMA5_MCAN0_BITRATE)) - 1))

#  define MCAN0_NSJW    (CONFIG_SAMA5_MCAN0_SJW - 1)

#  if ((MCAN0_NTSEG1 > 255) || (MCAN0_NTSEG1 < 1))
#    error Invalid MCAN0 TSEG1
#  endif
#  if MCAN0_NTSEG2 > 127
#    error Invalid MCAN0 TSEG2
#  endif
#  if MCAN0_NSJW > 127
#    error Invalid MCAN0 SJW
#  endif

#  define MCAN0_FTSEG1 (CONFIG_SAMA5_MCAN0_FPROPSEG + CONFIG_SAMA5_MCAN0_FPHASESEG1 - 1)
#  define MCAN0_FTSEG2 (CONFIG_SAMA5_MCAN0_FPHASESEG2 - 1)
#  define MCAN0_FBRP   ((uint32_t)(((float)SAMA5_MCANCLK_FREQUENCY / \
                        ((float)(MCAN0_FTSEG1 + MCAN0_FTSEG2 + 3) * \
                         (float)CONFIG_SAMA5_MCAN0_FBITRATE)) - 1))
#  define MCAN0_FSJW   (CONFIG_SAMA5_MCAN0_FSJW - 1)

#  if ((MCAN0_FTSEG1 > 31) || (MCAN0_FTSEG1 < 1))
#    error Invalid MCAN0 FTSEG1
#  endif
#  if MCAN0_FTSEG2 > 15
#    error Invalid MCAN0 FTSEG2
#  endif
#  if MCAN0_FSJW > 7
#    error Invalid MCAN0 FSJW
#  endif

/* MCAN0 RX FIFO0 element size */

#  if defined(CONFIG_SAMA5_MCAN0_RXFIFO0_8BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  8
#    define MCAN0_RXFIFO0_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_12BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  12
#    define MCAN0_RXFIFO0_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_16BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  16
#    define MCAN0_RXFIFO0_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_20BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  20
#    define MCAN0_RXFIFO0_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_24BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  24
#    define MCAN0_RXFIFO0_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_32BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  32
#    define MCAN0_RXFIFO0_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_48BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  48
#    define MCAN0_RXFIFO0_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO0_64BYTES)
#    define MCAN0_RXFIFO0_ELEMENT_SIZE  64
#    define MCAN0_RXFIFO0_ENCODED_SIZE  7
#  else
#    error Undefined MCAN0 RX FIFO0 element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_RXFIFO0_SIZE
#    define CONFIG_SAMA5_MCAN0_RXFIFO0_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN0_RXFIFO0_SIZE > 64
#    error Invalid MCAN0 number of RX FIFO0 elements
#  endif

#  define MCAN0_RXFIFO0_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_RXFIFO0_SIZE * \
                   (MCAN0_RXFIFO0_ELEMENT_SIZE + 8))
#  define MCAN0_RXFIFO0_WORDS (MCAN0_RXFIFO0_BYTES >> 2)

/* MCAN0 RX FIFO1 element size */

#  if defined(CONFIG_SAMA5_MCAN0_RXFIFO1_8BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  8
#    define MCAN0_RXFIFO1_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_12BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  12
#    define MCAN0_RXFIFO1_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_16BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  16
#    define MCAN0_RXFIFO1_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_20BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  20
#    define MCAN0_RXFIFO1_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_24BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  24
#    define MCAN0_RXFIFO1_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_32BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  32
#    define MCAN0_RXFIFO1_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_48BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  48
#    define MCAN0_RXFIFO1_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN0_RXFIFO1_64BYTES)
#    define MCAN0_RXFIFO1_ELEMENT_SIZE  64
#    define MCAN0_RXFIFO1_ENCODED_SIZE  7
#  else
#    error Undefined MCAN0 RX FIFO1 element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_RXFIFO1_SIZE
#    define CONFIG_SAMA5_MCAN0_RXFIFO1_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN0_RXFIFO1_SIZE > 64
#    error Invalid MCAN0 number of RX FIFO1 elements
#  endif

#  define MCAN0_RXFIFO1_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_RXFIFO1_SIZE * \
                   (MCAN0_RXFIFO1_ELEMENT_SIZE + 8))
#  define MCAN0_RXFIFO1_WORDS (MCAN0_RXFIFO1_BYTES >> 2)

/* MCAN0 Filters */

#  ifndef CONFIG_SAMA5_MCAN0_NSTDFILTERS
#    define CONFIG_SAMA5_MCAN0_NSTDFILTERS 0
#  endif

#  if (CONFIG_SAMA5_MCAN0_NSTDFILTERS > 128)
#    error Invalid MCAN0 number of Standard Filters
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_NEXTFILTERS
#    define CONFIG_SAMA5_MCAN0_NEXTFILTERS 0
#  endif

#  if (CONFIG_SAMA5_MCAN0_NEXTFILTERS > 64)
#    error Invalid MCAN0 number of Extended Filters
#  endif

#  define MCAN0_STDFILTER_BYTES \
      MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_NSTDFILTERS << 2)
#  define MCAN0_STDFILTER_WORDS (MCAN0_STDFILTER_BYTES >> 2)

#  define MCAN0_EXTFILTER_BYTES \
      MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_NEXTFILTERS << 3)
#  define MCAN0_EXTFILTER_WORDS (MCAN0_EXTFILTER_BYTES >> 2)

/* MCAN0 RX buffer element size */

#  if defined(CONFIG_SAMA5_MCAN0_RXBUFFER_8BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  8
#    define MCAN0_RXBUFFER_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_12BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  12
#    define MCAN0_RXBUFFER_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_16BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  16
#    define MCAN0_RXBUFFER_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_20BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  20
#    define MCAN0_RXBUFFER_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_24BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  24
#    define MCAN0_RXBUFFER_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_32BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  32
#    define MCAN0_RXBUFFER_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_48BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  48
#    define MCAN0_RXBUFFER_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN0_RXBUFFER_64BYTES)
#    define MCAN0_RXBUFFER_ELEMENT_SIZE  64
#    define MCAN0_RXBUFFER_ENCODED_SIZE  7
#  else
#    error Undefined MCAN0 RX buffer element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_DEDICATED_RXBUFFER_SIZE
#    define CONFIG_SAMA5_MCAN0_DEDICATED_RXBUFFER_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN0_DEDICATED_RXBUFFER_SIZE > 64
#    error Invalid MCAN0 number of RX BUFFER elements
#  endif

#  define MCAN0_DEDICATED_RXBUFFER_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_DEDICATED_RXBUFFER_SIZE * \
                   (MCAN0_RXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN0_DEDICATED_RXBUFFER_WORDS \
     (MCAN0_DEDICATED_RXBUFFER_BYTES >> 2)

/* MCAN0 TX buffer element size */

#  if defined(CONFIG_SAMA5_MCAN0_TXBUFFER_8BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  8
#    define MCAN0_TXBUFFER_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_12BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  12
#    define MCAN0_TXBUFFER_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_16BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  16
#    define MCAN0_TXBUFFER_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_20BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  20
#    define MCAN0_TXBUFFER_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_24BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  24
#    define MCAN0_TXBUFFER_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_32BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  32
#    define MCAN0_TXBUFFER_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_48BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  48
#    define MCAN0_TXBUFFER_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN0_TXBUFFER_64BYTES)
#    define MCAN0_TXBUFFER_ELEMENT_SIZE  64
#    define MCAN0_TXBUFFER_ENCODED_SIZE  7
#  else
#    error Undefined MCAN0 TX buffer element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_DEDICATED_TXBUFFER_SIZE
#    define CONFIG_SAMA5_MCAN0_DEDICATED_TXBUFFER_SIZE 0
#  endif

#  define MCAN0_DEDICATED_TXBUFFER_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_DEDICATED_TXBUFFER_SIZE * \
                   (MCAN0_TXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN0_DEDICATED_TXBUFFER_WORDS \
     (MCAN0_DEDICATED_TXBUFFER_BYTES >> 2)

/* MCAN0 TX FIFOs */

#  ifndef CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE
#    define CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE 0
#  endif

#  if (CONFIG_SAMA5_MCAN0_DEDICATED_TXBUFFER_SIZE + \
       CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE) > 32
#    error Invalid MCAN0 number of TX BUFFER elements
#  endif

#  ifndef CONFIG_SAMA5_MCAN0_TXEVENTFIFO_SIZE
#    define CONFIG_SAMA5_MCAN0_TXEVENTFIFO_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN0_TXEVENTFIFO_SIZE > 32
#    error Invalid MCAN0 number of TX EVENT FIFO elements
#  endif

#  define MCAN0_TXEVENTFIFO_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_TXEVENTFIFO_SIZE << 3)
#  define MCAN0_TXEVENTFIFO_WORDS \
     (MCAN0_TXEVENTFIFO_BYTES >> 2)

#  define MCAN0_TXFIFIOQ_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE *  \
                   (MCAN0_TXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN0_TXFIFIOQ_WORDS (MCAN0_TXFIFIOQ_BYTES >> 2)

/* MCAN0 Message RAM */

#  define MCAN0_STDFILTER_INDEX   0
#  define MCAN0_EXTFILTERS_INDEX  (MCAN0_STDFILTER_INDEX + MCAN0_STDFILTER_WORDS)
#  define MCAN0_RXFIFO0_INDEX     (MCAN0_EXTFILTERS_INDEX + MCAN0_EXTFILTER_WORDS)
#  define MCAN0_RXFIFO1_INDEX     (MCAN0_RXFIFO0_INDEX + MCAN0_RXFIFO0_WORDS)
#  define MCAN0_RXDEDICATED_INDEX (MCAN0_RXFIFO1_INDEX + MCAN0_RXFIFO1_WORDS)
#  define MCAN0_TXEVENTFIFO_INDEX (MCAN0_RXDEDICATED_INDEX + MCAN0_DEDICATED_RXBUFFER_WORDS)
#  define MCAN0_TXDEDICATED_INDEX (MCAN0_TXEVENTFIFO_INDEX + MCAN0_TXEVENTFIFO_WORDS)
#  define MCAN0_TXFIFOQ_INDEX     (MCAN0_TXDEDICATED_INDEX + MCAN0_DEDICATED_TXBUFFER_WORDS)
#  define MCAN0_MSGRAM_WORDS      (MCAN0_TXFIFOQ_INDEX + MCAN0_TXFIFIOQ_WORDS)

#endif /* CONFIG_SAMA5_MCAN0 */

/* Loopback mode */

#undef SAMA5_MCAN_LOOPBACK
#if defined(CONFIG_SAMA5_MCAN0_LOOPBACK) || defined(CONFIG_SAMA5_MCAN1_LOOPBACK)
#  define SAMA5_MCAN_LOOPBACK 1
#endif

/* MCAN1 Configuration */

#ifdef CONFIG_SAMA5_MCAN1

  /* Bit timing */

#  define MCAN1_NTSEG1  (CONFIG_SAMA5_MCAN1_PROPSEG + CONFIG_SAMA5_MCAN1_PHASESEG1 - 1)
#  define MCAN1_NTSEG2  (CONFIG_SAMA5_MCAN1_PHASESEG2 - 1)
#  define MCAN1_NBRP    ((uint32_t)(((float)SAMA5_MCANCLK_FREQUENCY / \
                         ((float)(MCAN1_NTSEG1 + MCAN1_NTSEG2 + 3) * \
                          (float)CONFIG_SAMA5_MCAN1_BITRATE)) - 1))

#  define MCAN1_NSJW    (CONFIG_SAMA5_MCAN1_SJW - 1)

#  if ((MCAN1_NTSEG1 > 255) || (MCAN1_NTSEG1 < 1))
#    error Invalid MCAN1 TSEG1
#  endif
#  if MCAN1_NTSEG2 > 127
#    error Invalid MCAN1 TSEG2
#  endif
#  if MCAN1_NSJW > 127
#    error Invalid MCAN1 SJW
#  endif

#  define MCAN1_FTSEG1 (CONFIG_SAMA5_MCAN1_FPROPSEG + CONFIG_SAMA5_MCAN1_FPHASESEG1 - 1)
#  define MCAN1_FTSEG2 (CONFIG_SAMA5_MCAN1_FPHASESEG2 - 1)
#  define MCAN1_FBRP   ((uint32_t)(((float)SAMA5_MCANCLK_FREQUENCY / \
                       ((float)(MCAN1_FTSEG1 + MCAN1_FTSEG2 + 3) * \
                        (float)CONFIG_SAMA5_MCAN1_FBITRATE)) - 1))
#  define MCAN1_FSJW   (CONFIG_SAMA5_MCAN1_FSJW - 1)

#  if ((MCAN1_FTSEG1 > 31) || (MCAN1_FTSEG1 < 1))
#    error Invalid MCAN1 FTSEG1
#  endif
#  if MCAN1_FTSEG2 > 15
#    error Invalid MCAN1 FTSEG2
#  endif
#  if MCAN1_FSJW > 7
#    error Invalid MCAN FSJW
#  endif

/* MCAN1 RX FIFO0 element size */

#  if defined(CONFIG_SAMA5_MCAN1_RXFIFO0_8BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  8
#    define MCAN1_RXFIFO0_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_12BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  12
#    define MCAN1_RXFIFO0_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_16BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  16
#    define MCAN1_RXFIFO0_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_20BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  20
#    define MCAN1_RXFIFO0_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_24BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  24
#    define MCAN1_RXFIFO0_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_32BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  32
#    define MCAN1_RXFIFO0_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_48BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  48
#    define MCAN1_RXFIFO0_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO0_64BYTES)
#    define MCAN1_RXFIFO0_ELEMENT_SIZE  64
#    define MCAN1_RXFIFO0_ENCODED_SIZE  7
#  else
#    error Undefined MCAN1 RX FIFO0 element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_RXFIFO0_SIZE
#    define CONFIG_SAMA5_MCAN1_RXFIFO0_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN1_RXFIFO0_SIZE > 64
#    error Invalid MCAN1 number of RX FIFO 0 elements
#  endif

#  define MCAN1_RXFIFO0_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_RXFIFO0_SIZE * \
                   (MCAN1_RXFIFO0_ELEMENT_SIZE + 8))
#  define MCAN1_RXFIFO0_WORDS (MCAN1_RXFIFO0_BYTES >> 2)

/* MCAN1 RX FIFO1 element size */

#  if defined(CONFIG_SAMA5_MCAN1_RXFIFO1_8BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  8
#    define MCAN1_RXFIFO1_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_12BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  12
#    define MCAN1_RXFIFO1_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_16BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  16
#    define MCAN1_RXFIFO1_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_20BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  20
#    define MCAN1_RXFIFO1_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_24BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  24
#    define MCAN1_RXFIFO1_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_32BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  32
#    define MCAN1_RXFIFO1_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_48BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  48
#    define MCAN1_RXFIFO1_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN1_RXFIFO1_64BYTES)
#    define MCAN1_RXFIFO1_ELEMENT_SIZE  64
#    define MCAN1_RXFIFO1_ENCODED_SIZE  7
#  else
#    error Undefined MCAN1 RX FIFO1 element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_RXFIFO1_SIZE
#    define CONFIG_SAMA5_MCAN1_RXFIFO1_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN1_RXFIFO1_SIZE > 64
#    error Invalid MCAN1 number of RX FIFO 0 elements
#  endif

#  define MCAN1_RXFIFO1_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_RXFIFO1_SIZE * \
                   (MCAN1_RXFIFO1_ELEMENT_SIZE + 8))
#  define MCAN1_RXFIFO1_WORDS (MCAN1_RXFIFO1_BYTES >> 2)

/* MCAN1 Filters */

#  ifndef CONFIG_SAMA5_MCAN1_NSTDFILTERS
#    define CONFIG_SAMA5_MCAN1_NSTDFILTERS 0
#  endif

#  if CONFIG_SAMA5_MCAN1_NSTDFILTERS > 128
#    error Invalid MCAN1 number of Standard Filters
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_NEXTFILTERS
#    define CONFIG_SAMA5_MCAN1_NEXTFILTERS 0
#  endif

#  if CONFIG_SAMA5_MCAN1_NEXTFILTERS > 64
#    error Invalid MCAN1 number of Extended Filters
#  endif

#  define MCAN1_STDFILTER_BYTES \
      MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_NSTDFILTERS << 2)
#  define MCAN1_STDFILTER_WORDS (MCAN1_STDFILTER_BYTES >> 2)

#  define MCAN1_EXTFILTER_BYTES \
      MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_NEXTFILTERS << 3)
#  define MCAN1_EXTFILTER_WORDS (MCAN1_EXTFILTER_BYTES >> 2)

/* MCAN1 RX buffer element size */

#  if defined(CONFIG_SAMA5_MCAN1_RXBUFFER_8BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  8
#    define MCAN1_RXBUFFER_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_12BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  12
#    define MCAN1_RXBUFFER_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_16BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  16
#    define MCAN1_RXBUFFER_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_20BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  20
#    define MCAN1_RXBUFFER_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_24BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  24
#    define MCAN1_RXBUFFER_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_32BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  32
#    define MCAN1_RXBUFFER_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_48BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  48
#    define MCAN1_RXBUFFER_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN1_RXBUFFER_64BYTES)
#    define MCAN1_RXBUFFER_ELEMENT_SIZE  64
#    define MCAN1_RXBUFFER_ENCODED_SIZE  7
#  else
#    error Undefined MCAN1 RX buffer element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_DEDICATED_RXBUFFER_SIZE
#    define CONFIG_SAMA5_MCAN1_DEDICATED_RXBUFFER_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN1_DEDICATED_RXBUFFER_SIZE > 64
#    error Invalid MCAN1 number of RX BUFFER elements
#  endif

#  define MCAN1_DEDICATED_RXBUFFER_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_DEDICATED_RXBUFFER_SIZE * \
                   (MCAN1_RXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN1_DEDICATED_RXBUFFER_WORDS \
     (MCAN1_DEDICATED_RXBUFFER_BYTES >> 2)

/* MCAN1 TX buffer element size */

#  if defined(CONFIG_SAMA5_MCAN1_TXBUFFER_8BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  8
#    define MCAN1_TXBUFFER_ENCODED_SIZE  0
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_12BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  12
#    define MCAN1_TXBUFFER_ENCODED_SIZE  1
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_16BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  16
#    define MCAN1_TXBUFFER_ENCODED_SIZE  2
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_20BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  20
#    define MCAN1_TXBUFFER_ENCODED_SIZE  3
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_24BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  24
#    define MCAN1_TXBUFFER_ENCODED_SIZE  4
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_32BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  32
#    define MCAN1_TXBUFFER_ENCODED_SIZE  5
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_48BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  48
#    define MCAN1_TXBUFFER_ENCODED_SIZE  6
#  elif defined(CONFIG_SAMA5_MCAN1_TXBUFFER_64BYTES)
#    define MCAN1_TXBUFFER_ELEMENT_SIZE  64
#    define MCAN1_TXBUFFER_ENCODED_SIZE  7
#  else
#    error Undefined MCAN1 TX buffer element size
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_DEDICATED_TXBUFFER_SIZE
#    define CONFIG_SAMA5_MCAN1_DEDICATED_TXBUFFER_SIZE 0
#  endif

#  define MCAN1_DEDICATED_TXBUFFER_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_DEDICATED_TXBUFFER_SIZE * \
                   (MCAN1_TXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN1_DEDICATED_TXBUFFER_WORDS \
     (MCAN1_DEDICATED_TXBUFFER_BYTES >> 2)

/* MCAN1 TX FIFOs */

#  ifndef CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE
#    define CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE 0
#  endif

#  if (CONFIG_SAMA5_MCAN1_DEDICATED_TXBUFFER_SIZE + \
       CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE) > 32
#    error Invalid MCAN1 number of TX BUFFER elements
#  endif

#  ifndef CONFIG_SAMA5_MCAN1_TXEVENTFIFO_SIZE
#    define CONFIG_SAMA5_MCAN1_TXEVENTFIFO_SIZE 0
#  endif

#  if CONFIG_SAMA5_MCAN1_TXEVENTFIFO_SIZE > 32
#    error Invalid MCAN1 number of TX EVENT FIFO elements
#  endif

#  define MCAN1_TXEVENTFIFO_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_TXEVENTFIFO_SIZE << 3)
#  define MCAN1_TXEVENTFIFO_WORDS \
     (MCAN1_TXEVENTFIFO_BYTES >> 2)

#  define MCAN1_TXFIFIOQ_BYTES \
     MCAN_ALIGN_UP(CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE *  \
                   (MCAN1_TXBUFFER_ELEMENT_SIZE + 8))
#  define MCAN1_TXFIFIOQ_WORDS (MCAN1_TXFIFIOQ_BYTES >> 2)

/* MCAN1 Message RAM */

#  define MCAN1_STDFILTER_INDEX   0
#  define MCAN1_EXTFILTERS_INDEX  (MCAN1_STDFILTER_INDEX + MCAN1_STDFILTER_WORDS)
#  define MCAN1_RXFIFO0_INDEX     (MCAN1_EXTFILTERS_INDEX + MCAN1_EXTFILTER_WORDS)
#  define MCAN1_RXFIFO1_INDEX     (MCAN1_RXFIFO0_INDEX + MCAN1_RXFIFO0_WORDS)
#  define MCAN1_RXDEDICATED_INDEX (MCAN1_RXFIFO1_INDEX + MCAN1_RXFIFO1_WORDS)
#  define MCAN1_TXEVENTFIFO_INDEX (MCAN1_RXDEDICATED_INDEX + MCAN1_DEDICATED_RXBUFFER_WORDS)
#  define MCAN1_TXDEDICATED_INDEX (MCAN1_TXEVENTFIFO_INDEX + MCAN1_TXEVENTFIFO_WORDS)
#  define MCAN1_TXFIFOQ_INDEX     (MCAN1_TXDEDICATED_INDEX + MCAN1_DEDICATED_TXBUFFER_WORDS)
#  define MCAN1_MSGRAM_WORDS      (MCAN1_TXFIFOQ_INDEX + MCAN1_TXFIFIOQ_WORDS)

#endif /* CONFIG_SAMA5_MCAN1 */

/* MCAN helpers */

#define MAILBOX_ADDRESS(a)        ((uint32_t)(a) & 0x0000fffc)

/* Interrupts */

/* Common interrupts
 *
 *   MCAN_INT_TSW  - Timestamp Wraparound
 *   MCAN_INT_MRAF - Message RAM Access Failure
 *   MCAN_INT_TOO  - Timeout Occurred
 *   MCAN_INT_ELO  - Error Logging Overflow
 *   MCAN_INT_EP   - Error Passive
 *   MCAN_INT_EW   - Warning Status
 *   MCAN_INT_BO   - Bus_Off Status
 *   MCAN_INT_WDI  - Watchdog Interrupt
 */

#define MCAN_CMNERR_INTS   (MCAN_INT_MRAF | MCAN_INT_TOO | MCAN_INT_EP | \
                            MCAN_INT_BO | MCAN_INT_WDI)
#define MCAN_COMMON_INTS   MCAN_CMNERR_INTS

/* RXFIFO mode interrupts
 *
 *   MCAN_INT_RF0N - Receive FIFO 0 New Message
 *   MCAN_INT_RF0W - Receive FIFO 0 Watermark Reached
 *   MCAN_INT_RF0F - Receive FIFO 0 Full
 *   MCAN_INT_RF0L - Receive FIFO 0 Message Lost
 *   MCAN_INT_RF1N - Receive FIFO 1 New Message
 *   MCAN_INT_RF1W - Receive FIFO 1 Watermark Reached
 *   MCAN_INT_RF1F - Receive FIFO 1 Full
 *   MCAN_INT_RF1L - Receive FIFO 1 Message Lost
 *   MCAN_INT_HPM  - High Priority Message Received
 *
 * Dedicated RX Buffer mode interrupts
 *
 *   MCAN_INT_DRX  - Message stored to Dedicated Receive Buffer
 *
 * Mode-independent RX-related interrupts
 *
 *   MCAN_INT_CRCE - Receive CRC Error
 *   MCAN_INT_FOE  - Format Error
 *   MCAN_INT_STE  - Stuff Error
 */

#define MCAN_RXCOMMON_INTS (MCAN_INT_CRCE | MCAN_INT_FOE | MCAN_INT_STE)
#define MCAN_RXFIFO0_INTS  (MCAN_INT_RF0N | MCAN_INT_RF0W | MCAN_INT_RF0L)
#define MCAN_RXFIFO1_INTS  (MCAN_INT_RF1N | MCAN_INT_RF1W | MCAN_INT_RF1L)
#define MCAN_RXFIFO_INTS   (MCAN_RXFIFO0_INTS | MCAN_RXFIFO1_INTS | \
                            MCAN_INT_HPM | MCAN_RXCOMMON_INTS)
#define MCAN_RXDEDBUF_INTS (MCAN_INT_DRX | MCAN_RXCOMMON_INTS)

#define MCAN_RXERR_INTS    (MCAN_INT_RF0L | MCAN_INT_RF1L | MCAN_INT_CRCE | \
                            MCAN_INT_FOE | MCAN_INT_STE)

/* TX FIFOQ mode interrupts
 *
 *   MCAN_INT_TFE  - Tx FIFO Empty
 *
 * TX Event FIFO interrupts
 *
 *   MCAN_INT_TEFN - Tx Event FIFO New Entry
 *   MCAN_INT_TEFW - Tx Event FIFO Watermark Reached
 *   MCAN_INT_TEFF - Tx Event FIFO Full
 *   MCAN_INT_TEFL - Tx Event FIFO Element Lost
 *
 * Mode-independent TX-related interrupts
 *
 *   MCAN_INT_TC   - Transmission Completed
 *   MCAN_INT_TCF  - Transmission Cancellation Finished
 *   MCAN_INT_BE   - Bit Error
 *   MCAN_INT_ACKE - Acknowledge Error
 */

#define MCAN_TXCOMMON_INTS (MCAN_INT_TC | MCAN_INT_TCF | MCAN_INT_BE | \
                            MCAN_INT_ACKE)
#define MCAN_TXFIFOQ_INTS  (MCAN_INT_TFE | MCAN_TXCOMMON_INTS)
#define MCAN_TXEVFIFO_INTS (MCAN_INT_TEFN | MCAN_INT_TEFW | MCAN_INT_TEFF | \
                            MCAN_INT_TEFL)
#define MCAN_TXDEDBUF_INTS MCAN_TXCOMMON_INTS

#define MCAN_TXERR_INTS    (MCAN_INT_TEFL | MCAN_INT_BE | MCAN_INT_ACKE)

/* Common-, TX- and RX-Error-Mask */

#define MCAN_ANYERR_INTS (MCAN_CMNERR_INTS | MCAN_RXERR_INTS | MCAN_TXERR_INTS)

/* Debug */

/* Debug configurations that may be enabled just for testing MCAN */

#ifndef CONFIG_DEBUG_CAN_INFO
#  undef CONFIG_SAMA5_MCAN_REGDEBUG
#endif

#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
#  define reginfo caninfo
#else
#  define reginfo(x...)
#endif

/****************************************************************************
 * Private Types
 ****************************************************************************/

/* CAN mode of operation */

enum sam_canmode_e
{
  MCAN_ISO11898_1_MODE = 0, /* CAN operation according to ISO11898-1 */
  MCAN_FD_MODE         = 1, /* CAN FD operation */
  MCAN_FD_BSW_MODE     = 2  /* CAN FD operation with bit rate switching */
};

/* CAN driver state */

enum can_state_s
{
  MCAN_STATE_UNINIT = 0,    /* Not yet initialized */
  MCAN_STATE_RESET,         /* Initialized, reset state */
  MCAN_STATE_SETUP,         /* can_setup() has been called */
};

/* This structure describes the MCAN message RAM layout */

struct sam_msgram_s
{
  uint32_t *stdfilters;    /* Standard filters */
  uint32_t *extfilters;    /* Extended filters */
  uint32_t *rxfifo0;       /* RX FIFO0 */
  uint32_t *rxfifo1;       /* RX FIFO1 */
  uint32_t *rxdedicated;   /* RX dedicated buffers */
  uint32_t *txeventfifo;   /* TX event FIFO */
  uint32_t *txdedicated;   /* TX dedicated buffers */
  uint32_t *txfifoq;       /* TX FIFO queue */
};

/* This structure provides the constant configuration of a MCAN peripheral */

struct sam_config_s
{
  uint32_t  rxpinset;      /* RX pin configuration */
  uint32_t  txpinset;      /* TX pin configuration */
  uintptr_t base;          /* Base address of the MCAN registers */
  uint32_t  baud;          /* Configured baud */
  uint32_t  btp;           /* Bit timing/prescaler register setting */
  uint32_t  fbtp;          /* Fast bit timing/prescaler register setting */
  uint8_t   port;          /* MCAN port number (1 or 2) */
  uint8_t   pid;           /* MCAN peripheral ID */
  uint8_t   irq0;          /* MCAN peripheral IRQ number for int. line 0 */
  uint8_t   irq1;          /* MCAN peripheral IRQ number for int. line 1 */
  uint8_t   mode;          /* See enum sam_canmode_e */
  uint8_t   nstdfilters;   /* Number of standard filters (up to 128) */
  uint8_t   nextfilters;   /* Number of extended filters (up to 64) */
  uint8_t   nrxfifo0;      /* Number of RX FIFO0 elements (up to 64) */
  uint8_t   nrxfifo1;      /* Number of RX FIFO1 elements (up to 64) */
  uint8_t   nrxdedicated;  /* Number of dedicated RX buffers (up to 64) */
  uint8_t   ntxeventfifo;  /* Number of TXevent FIFO elements (up to 32) */
  uint8_t   ntxdedicated;  /* Number of dedicated TX buffers (up to 64) */
  uint8_t   ntxfifoq;      /* Number of TX FIFO queue elements (up to 32) */
  uint8_t   rxfifo0ecode;  /* Encoded RX FIFO0 element size */
  uint8_t   rxfifo0esize;  /* RX FIFO0 element size (words) */
  uint8_t   rxfifo1ecode;  /* Encoded RX FIFO1 element size */
  uint8_t   rxfifo1esize;  /* RX FIFO1 element size (words) */
  uint8_t   rxbufferecode; /* Encoded RX buffer element size */
  uint8_t   rxbufferesize; /* RX buffer element size (words) */
  uint8_t   txbufferecode; /* Encoded TX buffer element size */
  uint8_t   txbufferesize; /* TX buffer element size (words) */
#ifdef SAMA5_MCAN_LOOPBACK
  bool      loopback;      /* True: Loopback mode */
#endif

  /* MCAN message RAM layout */

  struct sam_msgram_s msgram;
};

/* This structure provides the current state of a MCAN peripheral */

struct sam_mcan_s
{
  /* The constant configuration */

  const struct sam_config_s *config;

  enum can_state_s state;          /* See enum can_state_s                  */
#ifdef CONFIG_CAN_EXTID
  uint8_t          nextalloc;      /* Number of allocated extended filters  */
#endif
  uint8_t          nstdalloc;      /* Number of allocated standard filters  */
  mutex_t          lock;           /* Enforces mutually exclusive access    */
  sem_t            txfsem;         /* Used to wait for TX FIFO availability */
  uint32_t         btp;            /* Current bit timing                    */
  uint32_t         fbtp;           /* Current fast bit timing               */
  uint32_t         rxints;         /* Configured RX interrupts              */
  uint32_t         txints;         /* Configured TX interrupts              */

#ifdef CONFIG_CAN_EXTID
  uint32_t extfilters[2];   /* Extended filter bit allocator.  2*32=64 */
#endif
  uint32_t stdfilters[4];   /* Standard filter bit allocator.  4*32=128 */

#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
  uintptr_t regaddr;        /* Last register address read */
  uint32_t regval;          /* Last value read from the register */
  unsigned int count;       /* Number of times that the value was read */
#endif
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/* MCAN Register access */

static uint32_t mcan_getreg(struct sam_mcan_s *priv, int offset);
static void mcan_putreg(struct sam_mcan_s *priv, int offset,
                        uint32_t regval);
#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
static void mcan_dumpregs(struct sam_mcan_s *priv, const char *msg);
#else
#  define mcan_dumpregs(priv,msg)
#endif

static void mcan_buffer_reserve(struct sam_mcan_s *priv);
static void mcan_buffer_release(struct sam_mcan_s *priv);

/* MCAN helpers */

static uint8_t mcan_dlc2bytes(struct sam_mcan_s *priv, uint8_t dlc);
#if 0 /* Not used */
static uint8_t mcan_bytes2dlc(struct sam_mcan_s *priv, uint8_t nbytes);
#endif

#ifdef CONFIG_CAN_EXTID
static int mcan_add_extfilter(struct sam_mcan_s *priv,
                              struct canioc_extfilter_s *extconfig);
static int mcan_del_extfilter(struct sam_mcan_s *priv, int ndx);
#endif
static int mcan_add_stdfilter(struct sam_mcan_s *priv,
                              struct canioc_stdfilter_s *stdconfig);
static int mcan_del_stdfilter(struct sam_mcan_s *priv, int ndx);

static int mcan_set_nart(struct sam_mcan_s *priv, bool enable);
static int mcan_cancel_tx_buffers(struct sam_mcan_s *priv);
static int mcan_cancel_rx_fifos(struct sam_mcan_s *priv);

/* CAN driver methods */

static void mcan_reset(struct can_dev_s *dev);
static int  mcan_setup(struct can_dev_s *dev);
static void mcan_shutdown(struct can_dev_s *dev);
static void mcan_rxint(struct can_dev_s *dev, bool enable);
static void mcan_txint(struct can_dev_s *dev, bool enable);
static int  mcan_ioctl(struct can_dev_s *dev, int cmd,
                       unsigned long arg);
static int  mcan_remoterequest(struct can_dev_s *dev, uint16_t id);
static int  mcan_send(struct can_dev_s *dev, struct can_msg_s *msg);
static bool mcan_txready(struct can_dev_s *dev);
static bool mcan_txempty(struct can_dev_s *dev);

/* MCAN interrupt handling */

#ifndef DEDICATED_BUFFERS_NOT_USED
static bool mcan_dedicated_rxbuffer_available(struct sam_mcan_s *priv,
                                              int bufndx);
#endif
#ifdef CONFIG_CAN_ERRORS
static void mcan_error(struct can_dev_s *dev, uint32_t status);
#endif
static void mcan_receive(struct can_dev_s *dev,
                         uint32_t *rxbuffer, unsigned long nwords);
static int  mcan_interrupt(int irq, void *context, void *arg);

/* Hardware initialization */

static int  mcan_hw_initialize(struct sam_mcan_s *priv);

/****************************************************************************
 * Private Data
 ****************************************************************************/

static const struct can_ops_s g_mcanops =
{
  .co_reset         = mcan_reset,
  .co_setup         = mcan_setup,
  .co_shutdown      = mcan_shutdown,
  .co_rxint         = mcan_rxint,
  .co_txint         = mcan_txint,
  .co_ioctl         = mcan_ioctl,
  .co_remoterequest = mcan_remoterequest,
  .co_send          = mcan_send,
  .co_txready       = mcan_txready,
  .co_txempty       = mcan_txempty,
};

#ifdef CONFIG_SAMA5_MCAN0

/* MCAN0 message RAM allocation */

static uint32_t g_mcan0_msgram[MCAN0_MSGRAM_WORDS]
#ifdef CONFIG_ARCH_DCACHE
  __attribute__((aligned(MCAN_ALIGN)));
#else
  ;
#endif

/* Constant configuration */

static const struct sam_config_s g_mcan0const =
{
  .rxpinset         = PIO_CAN0_RX,
  .txpinset         = PIO_CAN0_TX,
  .base             = SAM_MCAN0_VBASE,
  .baud             = CONFIG_SAMA5_MCAN0_BITRATE,
  .btp              = MCAN_BTP_BRP(MCAN0_NBRP) |
                      MCAN_BTP_TSEG1(MCAN0_NTSEG1) |
                      MCAN_BTP_TSEG2(MCAN0_NTSEG2) |
                      MCAN_BTP_SJW(MCAN0_NSJW),
  .fbtp             = MCAN_FBTP_FBRP(MCAN0_FBRP) |
                      MCAN_FBTP_FTSEG1(MCAN0_FTSEG1) |
                      MCAN_FBTP_FTSEG2(MCAN0_FTSEG2) |
                      MCAN_FBTP_FSJW(MCAN0_FSJW),
  .port             = 0,
  .pid              = SAM_PID_MCAN00,
  .irq0             = SAM_IRQ_MCAN00,
  .irq1             = SAM_IRQ_MCAN01,
#if defined(CONFIG_SAMA5_MCAN0_ISO11898_1)
  .mode             = MCAN_ISO11898_1_MODE,
#elif defined(CONFIG_SAMA5_MCAN0_FD)
  .mode             = MCAN_FD_MODE,
#else /* if defined(CONFIG_SAMA5_MCAN0_FD_BSW) */
  .mode             = MCAN_FD_BSW_MODE,
#endif
  .nstdfilters      = CONFIG_SAMA5_MCAN0_NSTDFILTERS,
  .nextfilters      = CONFIG_SAMA5_MCAN0_NEXTFILTERS,
  .nrxfifo0         = CONFIG_SAMA5_MCAN0_RXFIFO0_SIZE,
  .nrxfifo1         = CONFIG_SAMA5_MCAN0_RXFIFO1_SIZE,
  .nrxdedicated     = CONFIG_SAMA5_MCAN0_DEDICATED_RXBUFFER_SIZE,
  .ntxeventfifo     = CONFIG_SAMA5_MCAN0_TXEVENTFIFO_SIZE,
  .ntxdedicated     = CONFIG_SAMA5_MCAN0_DEDICATED_TXBUFFER_SIZE,
  .ntxfifoq         = CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE,
  .rxfifo0ecode     = MCAN0_RXFIFO0_ENCODED_SIZE,
  .rxfifo0esize     = (MCAN0_RXFIFO0_ELEMENT_SIZE / 4) + 2,
  .rxfifo1ecode     = MCAN0_RXFIFO1_ENCODED_SIZE,
  .rxfifo1esize     = (MCAN0_RXFIFO1_ELEMENT_SIZE / 4) + 2,
  .rxbufferecode    = MCAN0_RXBUFFER_ENCODED_SIZE,
  .rxbufferesize    = (MCAN0_RXBUFFER_ELEMENT_SIZE / 4) + 2,
  .txbufferecode    = MCAN0_TXBUFFER_ENCODED_SIZE,
  .txbufferesize    = (MCAN0_TXBUFFER_ELEMENT_SIZE / 4) + 2,

#ifdef CONFIG_SAMA5_MCAN0_LOOPBACK
  .loopback         = true,
#endif

  /* MCAN0 Message RAM */

  .msgram =
  {
     &g_mcan0_msgram[MCAN0_STDFILTER_INDEX],
     &g_mcan0_msgram[MCAN0_EXTFILTERS_INDEX],
     &g_mcan0_msgram[MCAN0_RXFIFO0_INDEX],
     &g_mcan0_msgram[MCAN0_RXFIFO1_INDEX],
     &g_mcan0_msgram[MCAN0_RXDEDICATED_INDEX],
     &g_mcan0_msgram[MCAN0_TXEVENTFIFO_INDEX],
     &g_mcan0_msgram[MCAN0_TXDEDICATED_INDEX],
     &g_mcan0_msgram[MCAN0_TXFIFOQ_INDEX]
  }
};

/* MCAN0 variable driver state */

static struct sam_mcan_s g_mcan0priv =
{
  .config           = &g_mcan0const,
  .lock             = NXMUTEX_INITIALIZER,
  .txfsem           = SEM_INITIALIZER(CONFIG_SAMA5_MCAN0_TXFIFOQ_SIZE),
};

static struct can_dev_s g_mcan0dev =
{
  .cd_ops           = &g_mcanops,
  .cd_priv          = &g_mcan0priv,
};

#endif /* CONFIG_SAMA5_MCAN0 */

#ifdef CONFIG_SAMA5_MCAN1

/* MCAN1 message RAM allocation */

static uint32_t g_mcan1_msgram[MCAN1_MSGRAM_WORDS]
#ifdef CONFIG_ARCH_DCACHE
  __attribute__((aligned(MCAN_ALIGN)));
#else
  ;
#endif

/* MCAN1 constant configuration */

static const struct sam_config_s g_mcan1const =
{
  .rxpinset         = PIO_CAN1_RX,
  .txpinset         = PIO_CAN1_TX,
  .base             = SAM_MCAN1_VBASE,
  .baud             = CONFIG_SAMA5_MCAN1_BITRATE,
  .btp              = MCAN_BTP_BRP(MCAN1_NBRP) |
                      MCAN_BTP_TSEG1(MCAN1_NTSEG1) |
                      MCAN_BTP_TSEG2(MCAN1_NTSEG2) |
                      MCAN_BTP_SJW(MCAN1_NSJW),
  .fbtp             = MCAN_FBTP_FBRP(MCAN1_FBRP) |
                      MCAN_FBTP_FTSEG1(MCAN1_FTSEG1) |
                      MCAN_FBTP_FTSEG2(MCAN1_FTSEG2) |
                      MCAN_FBTP_FSJW(MCAN1_FSJW),
  .port             = 1,
  .pid              = SAM_PID_MCAN10,
  .irq0             = SAM_IRQ_MCAN10,
  .irq1             = SAM_IRQ_MCAN11,
#if defined(CONFIG_SAMA5_MCAN1_ISO11898_1)
  .mode             = MCAN_ISO11898_1_MODE,
#elif defined(CONFIG_SAMA5_MCAN1_FD)
  .mode             = MCAN_FD_MODE,
#else /* if defined(CONFIG_SAMA5_MCAN1_FD_BSW) */
  .mode             = MCAN_FD_BSW_MODE,
#endif
  .nstdfilters      = CONFIG_SAMA5_MCAN1_NSTDFILTERS,
  .nextfilters      = CONFIG_SAMA5_MCAN1_NEXTFILTERS,
  .nrxfifo0         = CONFIG_SAMA5_MCAN1_RXFIFO0_SIZE,
  .nrxfifo1         = CONFIG_SAMA5_MCAN1_RXFIFO1_SIZE,
  .nrxdedicated     = CONFIG_SAMA5_MCAN1_DEDICATED_RXBUFFER_SIZE,
  .ntxeventfifo     = CONFIG_SAMA5_MCAN1_TXEVENTFIFO_SIZE,
  .ntxdedicated     = CONFIG_SAMA5_MCAN1_DEDICATED_TXBUFFER_SIZE,
  .ntxfifoq         = CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE,
  .rxfifo0ecode     = MCAN1_RXFIFO0_ENCODED_SIZE,
  .rxfifo0esize     = (MCAN1_RXFIFO0_ELEMENT_SIZE / 4) + 2,
  .rxfifo1ecode     = MCAN1_RXFIFO1_ENCODED_SIZE,
  .rxfifo1esize     = (MCAN1_RXFIFO1_ELEMENT_SIZE / 4) + 2,
  .rxbufferecode    = MCAN1_RXBUFFER_ENCODED_SIZE,
  .rxbufferesize    = (MCAN1_RXBUFFER_ELEMENT_SIZE / 4) + 2,
  .txbufferecode    = MCAN1_TXBUFFER_ENCODED_SIZE,
  .txbufferesize    = (MCAN1_TXBUFFER_ELEMENT_SIZE / 4) + 2,

#ifdef CONFIG_SAMA5_MCAN1_LOOPBACK
  .loopback         = true,
#endif
  /* MCAN1 Message RAM */

  .msgram =
  {
     &g_mcan1_msgram[MCAN1_STDFILTER_INDEX],
     &g_mcan1_msgram[MCAN1_EXTFILTERS_INDEX],
     &g_mcan1_msgram[MCAN1_RXFIFO0_INDEX],
     &g_mcan1_msgram[MCAN1_RXFIFO1_INDEX],
     &g_mcan1_msgram[MCAN1_RXDEDICATED_INDEX],
     &g_mcan1_msgram[MCAN1_TXEVENTFIFO_INDEX],
     &g_mcan1_msgram[MCAN1_TXDEDICATED_INDEX],
     &g_mcan1_msgram[MCAN1_TXFIFOQ_INDEX]
  }
};

/* MCAN1 variable driver state */

static struct sam_mcan_s g_mcan1priv =
{
  .config           = &g_mcan1const,
  .lock             = NXMUTEX_INITIALIZER,
  .txfsem           = SEM_INITIALIZER(CONFIG_SAMA5_MCAN1_TXFIFOQ_SIZE),
};

static struct can_dev_s g_mcan1dev =
{
  .cd_ops           = &g_mcanops,
  .cd_priv          = &g_mcan1priv,
};

#endif /* CONFIG_SAMA5_MCAN1 */

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: mcan_getreg
 *
 * Description:
 *   Read the value of a MCAN register.
 *
 * Input Parameters:
 *   priv - A reference to the MCAN peripheral state
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
static uint32_t mcan_getreg(struct sam_mcan_s *priv, int offset)
{
  const struct sam_config_s *config = priv->config;
  uintptr_t regaddr;
  uint32_t regval;

  /* Read the value from the register */

  regaddr = config->base + offset;
  regval  = getreg32(regaddr);

  /* Is this the same value that we read from the same register last time?
   * Are we polling the register?  If so, suppress some of the output.
   */

  if (regaddr == priv->regaddr && regval == priv->regval)
    {
      if (priv->count == 0xffffffff || ++priv->count > 3)
        {
          if (priv->count == 4)
            {
              caninfo("...\n");
            }

          return regval;
        }
    }

  /* No this is a new address or value */

  else
    {
      /* Did we print "..." for the previous value? */

      if (priv->count > 3)
        {
          /* Yes.. then show how many times the value repeated */

          caninfo("[repeats %d more times]\n", priv->count - 3);
        }

      /* Save the new address, value, and count */

      priv->regaddr = regaddr;
      priv->regval  = regval;
      priv->count   = 1;
    }

  /* Show the register value read */

  caninfo("%08" PRIx32 "->%08" PRIx32 "\n", regaddr, regval);
  return regval;
}

#else
static uint32_t mcan_getreg(struct sam_mcan_s *priv, int offset)
{
  const struct sam_config_s *config = priv->config;
  return getreg32(config->base + offset);
}

#endif /* CONFIG_SAMA5_MCAN_REGDEBUG */

/****************************************************************************
 * Name: mcan_putreg
 *
 * Description:
 *   Set the value of a MCAN register.
 *
 * Input Parameters:
 *   priv - A reference to the MCAN peripheral state
 *   offset - The offset to the register to write
 *   regval - The value to write to the register
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
static void mcan_putreg(struct sam_mcan_s *priv, int offset,
                        uint32_t regval)
{
  const struct sam_config_s *config = priv->config;
  uintptr_t regaddr = config->base + offset;

  /* Show the register value being written */

  caninfo("%08" PRIx32 "<-%08" PRIx32 "\n", regaddr, regval);

  /* Write the value */

  putreg32(regval, regaddr);
}

#else
static void mcan_putreg(struct sam_mcan_s *priv, int offset,
                        uint32_t regval)
{
  const struct sam_config_s *config = priv->config;

  putreg32(regval, config->base + offset);
}

#endif

/****************************************************************************
 * Name: mcan_dumpregs
 *
 * Description:
 *   Dump the contents of all MCAN control registers
 *
 * Input Parameters:
 *   priv - A reference to the MCAN peripheral state
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_SAMA5_MCAN_REGDEBUG
static void mcan_dumpregs(struct sam_mcan_s *priv, const char *msg)
{
  const struct sam_config_s *config = priv->config;

  caninfo("MCAN%d Registers: %s\n", config->port, msg);
  caninfo("   Base: %08x\n", config->base);

  caninfo ("UTMI ref clk=%d\n", getreg32(SAM_SFR_VBASE +
                                        SAM_SFR_UTMICKTRIM_OFFSET) &
                                        0x03);

  putreg32(PMC_PCR_PID(SAM_PID_MCAN0), SAM_PMC_PCR);
  caninfo("  MCREL: %08x  ENDN: %08x PMC_PCR %08x\n",
          getreg32(config->base + SAM_MCAN_CREL_OFFSET),
          getreg32(config->base + SAM_MCAN_ENDN_OFFSET),
          getreg32(SAM_PMC_PCR));

  caninfo("   CUST: %08x  FBTP: %08x TEST: %08x    RWD: %08x\n",
          getreg32(config->base + SAM_MCAN_CUST_OFFSET),
          getreg32(config->base + SAM_MCAN_FBTP_OFFSET),
          getreg32(config->base + SAM_MCAN_TEST_OFFSET),
          getreg32(config->base + SAM_MCAN_RWD_OFFSET));

  caninfo("  CCCR: %08x   NBTP: %08x  TSCC: %08x   TSCV: %08x\n",
          getreg32(config->base + SAM_MCAN_CCCR_OFFSET),
          getreg32(config->base + SAM_MCAN_BTP_OFFSET),
          getreg32(config->base + SAM_MCAN_TSCC_OFFSET),
          getreg32(config->base + SAM_MCAN_TSCV_OFFSET));

  caninfo("  TOCC: %08x  TOCV: %08x   ECR: %08x    PSR: %08x\n",
          getreg32(config->base + SAM_MCAN_TOCC_OFFSET),
          getreg32(config->base + SAM_MCAN_TOCV_OFFSET),
          getreg32(config->base + SAM_MCAN_ECR_OFFSET),
          getreg32(config->base + SAM_MCAN_PSR_OFFSET));

  caninfo("    IR: %08x    IE: %08x   ILS: %08x    ILE: %08x\n",
          getreg32(config->base + SAM_MCAN_IR_OFFSET),
          getreg32(config->base + SAM_MCAN_IE_OFFSET),
          getreg32(config->base + SAM_MCAN_ILS_OFFSET),
          getreg32(config->base + SAM_MCAN_ILE_OFFSET));

  caninfo("   GFC: %08x SIDFC: %08x XIDFC: %08x  XIDAM: %08x\n",
          getreg32(config->base + SAM_MCAN_GFC_OFFSET),
          getreg32(config->base + SAM_MCAN_SIDFC_OFFSET),
          getreg32(config->base + SAM_MCAN_XIDFC_OFFSET),
          getreg32(config->base + SAM_MCAN_XIDAM_OFFSET));

  caninfo("  HPMS: %08x NDAT1: %08x NDAT2: %08x  RXF0C: %08x\n",
          getreg32(config->base + SAM_MCAN_HPMS_OFFSET),
          getreg32(config->base + SAM_MCAN_NDAT1_OFFSET),
          getreg32(config->base + SAM_MCAN_NDAT2_OFFSET),
          getreg32(config->base + SAM_MCAN_RXF0C_OFFSET));

  caninfo(" RXF0S: %08x FXF0A: %08x  RXBC: %08x  RXF1C: %08x\n",
          getreg32(config->base + SAM_MCAN_RXF0S_OFFSET),
          getreg32(config->base + SAM_MCAN_RXF0A_OFFSET),
          getreg32(config->base + SAM_MCAN_RXBC_OFFSET),
          getreg32(config->base + SAM_MCAN_RXF1C_OFFSET));

  caninfo(" RXF1S: %08x FXF1A: %08x RXESC: %08x   TXBC: %08x\n",
          getreg32(config->base + SAM_MCAN_RXF1S_OFFSET),
          getreg32(config->base + SAM_MCAN_RXF1A_OFFSET),
          getreg32(config->base + SAM_MCAN_RXESC_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBC_OFFSET));

  caninfo(" TXFQS: %08x TXESC: %08x TXBRP: %08x  TXBAR: %08x\n",
          getreg32(config->base + SAM_MCAN_TXFQS_OFFSET),
          getreg32(config->base + SAM_MCAN_TXESC_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBRP_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBAR_OFFSET));

  caninfo(" TXBCR: %08x TXBTO: %08x TXBCF: %08x TXBTIE: %08x\n",
          getreg32(config->base + SAM_MCAN_TXBCR_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBTO_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBCF_OFFSET),
          getreg32(config->base + SAM_MCAN_TXBTIE_OFFSET));

  caninfo("TXBCIE: %08x TXEFC: %08x TXEFS: %08x  TXEFA: %08x\n",
          getreg32(config->base + SAM_MCAN_TXBCIE_OFFSET),
          getreg32(config->base + SAM_MCAN_TXEFC_OFFSET),
          getreg32(config->base + SAM_MCAN_TXEFS_OFFSET),
          getreg32(config->base + SAM_MCAN_TXEFA_OFFSET));
}
#endif

/****************************************************************************
 * Name: mcan_buffer_reserve
 *
 * Description:
 *   Take the semaphore, decrementing the semaphore count to indicate that
 *   one fewer TX FIFOQ buffer is available.  Handles any exceptional
 *   conditions.
 *
 * Input Parameters:
 *   priv - A reference to the MCAN peripheral state
 *
 * Returned Value:
 *  None
 *
 * Assumptions:
 *  Called only non-interrupt logic via mcan_write().  We do not have
 *  exclusive access to the MCAN hardware and interrupts are not disabled.
 *  mcan_write() does lock the scheduler for reasons noted below.
 *
 ****************************************************************************/

static void mcan_buffer_reserve(struct sam_mcan_s *priv)
{
  irqstate_t flags;
  uint32_t txfqs1;
  uint32_t txfqs2;
#ifndef CONFIG_SAMA5_MCAN_QUEUE_MODE
  int tffl;
#endif
  int sval;
  int ret = 0;

  /* Wait until we successfully get the semaphore.  EINTR is the only
   * expected 'failure' (meaning that the wait for the semaphore was
   * interrupted by a signal.
   */

  do
    {
      /* We take some extra precautions here because it is possible that on
       * certain error conditions, the semaphore count could get out of
       * phase with the actual count of elements in the TX FIFO (I have
       * never seen this happen, however.  My paranoia).
       *
       * An missed TX interrupt could cause the semaphore count to fail to
       * be incremented and, hence, to be too low.
       */

      for (; ; )
        {
          /* Get the current queue status and semaphore count. */

          flags = enter_critical_section();
          txfqs1 = mcan_getreg(priv, SAM_MCAN_TXFQS_OFFSET);
          nxsem_get_value(&priv->txfsem, &sval);
          txfqs2 = mcan_getreg(priv, SAM_MCAN_TXFQS_OFFSET);

          /* If the semaphore count and the TXFQS samples are in
           * sync, then break out of the look with interrupts
           * disabled.
           */

          if (txfqs1 == txfqs2)
            {
              break;
            }

          /* Otherwise, re-enable interrupts to interrupts that may
           * resynchronize, the semaphore count and try again.
           */

          leave_critical_section(flags);
        }

#ifdef CONFIG_SAMA5_MCAN_QUEUE_MODE
      /* We only have one useful bit of information in the TXFQS:
       * Is the TX FIFOQ full or not?  We can only do limited checks
       * with that single bit of information.
       */

      if ((txfqs1 & MCAN_TXFQS_TFQF) != 0)
        {
          /* The TX FIFOQ is full.  The semaphore count should then be
           * less than or equal to zero.  If it is greater than zero,
           * then reinitialize it to 0.
           */

          if (sval > 0)
            {
              canerr("ERROR: TX FIFOQ full but txfsem is %d\n", sval);
              nxsem_reset(&priv->txfsem, 0);
            }
        }

      /* The FIFO is not full so the semaphore count should be greater
       * than zero.  If it is not, then we have missed a call to
       * mcan_buffer_release(0).
       *
       * NOTE: Since there is no mutual exclusion, it might be possible
       * that mcan_write() could be re-entered AFTER taking the semaphore
       * and dropping the count to zero, but BEFORE adding the message
       * to the TX FIFOQ.  That corner case is handled in mcan_write() by
       * locking the scheduler.
       */

      else if (sval <= 0)
        {
          canerr("ERROR: TX FIFOQ not full but txfsem is %d\n", sval);

          /* Less than zero means that another thread is waiting */

          if (sval < 0)
            {
              /* Bump up the count by one and try again */

              nxsem_post(&priv->txfsem);
              leave_critical_section(flags);
              continue;
            }

          /* Exactly zero but the FIFO is not full.  Just return without
           * decrementing the count.
           */

          leave_critical_section(flags);
          return;
        }
#else
      /* Tx FIFO Free Level */

      tffl = (txfqs1 & MCAN_TXFQS_TFFL_MASK) >> MCAN_TXFQS_TFFL_SHIFT;

      /* Check if the configured number is less than the number of buffers
       * in the chip
       */

      if (tffl > priv->config->ntxfifoq)
        {
          canerr("ERROR: TX FIFO reports %d but max is %d\n",
                 tffl, priv->config->ntxfifoq);
          tffl = priv->config->ntxfifoq;
        }

      /* REVISIT:  There may be issues with this logic in a multi-thread
       * environment.  If there is only a single thread, then certainly
       * sval and tff1 should match, but that may not be true in any multi-
       * threaded use of this driver.
       */

      if (sval != tffl)
        {
          canerr("ERROR: TX FIFO reports %d but txfsem is %d\n", tffl, sval);

          /* Reset the semaphore count to the Tx FIFO free level. */

          nxsem_reset(&priv->txfsem, tffl);
        }
#endif

      /* The semaphore value is reasonable. Wait for the next TX interrupt. */

      ret = nxsem_wait(&priv->txfsem);
      leave_critical_section(flags);
    }
  while (ret < 0);
}

/****************************************************************************
 * Name: mcan_buffer_release
 *
 * Description:
 *   Release the semaphore, increment the semaphore count to indicate that
 *   one more TX FIFOQ buffer is available.
 *
 * Input Parameters:
 *   priv - A reference to the MCAN peripheral state
 *
 * Returned Value:
 *  None
 *
 * Assumptions:
 *  This function is called only from the interrupt level in response to the
 *  complete of a transmission.
 *
 ****************************************************************************/

static void mcan_buffer_release(struct sam_mcan_s *priv)
{
  int sval;

  /* We take some extra precautions here because it is possible that on
   * certain error conditions, the semaphore count could get out of phase
   * with the actual count of elements in the TX FIFO (I have never seen
   * this happen, however.  My paranoia).
   *
   * An extra TC interrupt could cause the count to be incremented too
   * many times.
   */

  nxsem_get_value(&priv->txfsem, &sval);
  if (sval < priv->config->ntxfifoq)
    {
      nxsem_post(&priv->txfsem);
    }
  else
    {
      canerr("ERROR: txfsem would increment beyond %d\n",
             priv->config->ntxfifoq);
    }
}

/****************************************************************************
 * Name: mcan_dlc2bytes
 *
 * Description:
 *   In the CAN FD format, the coding of the DLC differs from the standard
 *   CAN format. The DLC codes 0 to 8 have the same coding as in standard
 *   CAN.  But the codes 9 to 15 all imply a data field of 8 bytes with
 *   standard CAN.  In CAN FD mode, the values 9 to 15 are encoded to values
 *   in the range 12 to 64.
 *
 * Input Parameters:
 *   dlc    - the DLC value to convert to a byte count
 *
 * Returned Value:
 *   The number of bytes corresponding to the DLC value.
 *
 ****************************************************************************/

static uint8_t mcan_dlc2bytes(struct sam_mcan_s *priv, uint8_t dlc)
{
  if (dlc > 8)
    {
#ifdef CONFIG_CAN_FD
      if (priv->config->mode == MCAN_ISO11898_1_MODE)
        {
          return 8;
        }
      else
        {
          switch (dlc)
            {
              case 9:
                return 12;
              case 10:
                return 16;
              case 11:
                return 20;
              case 12:
                return 24;
              case 13:
                return 32;
              case 14:
                return 48;
              default:
              case 15:
                return 64;
            }
        }
#else
      return 8;
#endif
    }

  return dlc;
}

/****************************************************************************
 * Name: mcan_bytes2dlc
 *
 * Description:
 *   In the CAN FD format, the coding of the DLC differs from the standard
 *   CAN format. The DLC codes 0 to 8 have the same coding as in standard
 *   CAN.  But the codes 9 to 15 all imply a data field of 8 bytes with
 *   standard CAN.  In CAN FD mode, the values 9 to 15 are encoded to values
 *   in the range 12 to 64.
 *
 * Input Parameters:
 *   nbytes - the byte count to convert to a DLC value
 *
 * Returned Value:
 *   The encoded DLC value corresponding to at least that number of bytes.
 *
 ****************************************************************************/

#if 0 /* Not used */
static uint8_t mcan_bytes2dlc(struct sam_mcan_s *priv, uint8_t nbytes)
{
  if (nbytes <= 8)
    {
      return nbytes;
    }
#ifdef CONFIG_CAN_FD
  else if (priv->mode == MCAN_ISO11898_1_MODE)
    {
      return 8;
    }
  else if (nbytes <= 12)
    {
      return 9;
    }
  else if (nbytes <= 16)
    {
      return 10;
    }
  else if (nbytes <= 20)
    {
      return 11;
    }
  else if (nbytes <= 24)
    {
      return 12;
    }
  else if (nbytes <= 32)
    {
      return 13;
    }
  else if (nbytes <= 48)
    {
      return 14;
    }
  else /* if (nbytes <= 64) */
    {
      return 15;
    }
#else
  else
    {
      return 8;
    }
#endif
}
#endif

/****************************************************************************
 * Name: mcan_add_extfilter
 *
 * Description:
 *   Add an address filter for a extended 29 bit address.
 *
 * Input Parameters:
 *   priv      - An instance of the MCAN driver state structure.
 *   extconfig - The configuration of the extended filter
 *
 * Returned Value:
 *   A non-negative filter ID is returned on success.  Otherwise a negated
 *   errno value is returned to indicate the nature of the error.
 *
 ****************************************************************************/

#ifdef CONFIG_CAN_EXTID
static int mcan_add_extfilter(struct sam_mcan_s *priv,
                              struct canioc_extfilter_s *extconfig)
{
  const struct sam_config_s *config;
  uint32_t *extfilter;
  uint32_t regval;
  int word;
  int bit;
  int ndx;
  int ret;

  DEBUGASSERT(priv != NULL && priv->config != NULL && extconfig != NULL);
  config = priv->config;

  /* Get exclusive excess to the MCAN hardware */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Find an unused standard filter */

  for (ndx = 0; ndx <  config->nextfilters; ndx++)
    {
      /* Is this filter assigned? */

      word = ndx >> 5;
      bit  = ndx & 0x1f;

      if ((priv->extfilters[word] & (1 << bit)) == 0)
        {
          /* No, assign the filter */

          DEBUGASSERT(priv->nextalloc < priv->config->nstdfilters);
          priv->extfilters[word] |= (1 << bit);
          priv->nextalloc++;

          extfilter = config->msgram.extfilters + (ndx << 1);

          /* Format and write filter word F0 */

          DEBUGASSERT(extconfig->xf_id1 <= CAN_MAX_EXTMSGID);
          regval = EXTFILTER_F0_EFID1(extconfig->xf_id1);

          if (extconfig->xf_prio == 0)
            {
              regval |= EXTFILTER_F0_EFEC_FIFO0;
            }
          else
            {
              regval |= EXTFILTER_F0_EFEC_FIFO0;
            }

          extfilter[0] = regval;

          /* Format and write filter word F1 */

          DEBUGASSERT(extconfig->xf_id2 <= CAN_MAX_EXTMSGID);
          regval = EXTFILTER_F1_EFID2(extconfig->xf_id2);

          switch (extconfig->xf_type)
            {
              default:
              case CAN_FILTER_DUAL:
                regval |= EXTFILTER_F1_EFT_DUAL;
                break;

              case CAN_FILTER_MASK:
                regval |= EXTFILTER_F1_EFT_CLASSIC;
                break;
              case CAN_FILTER_RANGE:
                regval |= EXTFILTER_F1_EFT_RANGE;
                break;
            }

          extfilter[1] = regval;

          /* Flush the filter entry into physical RAM */

          up_clean_dcache((uintptr_t)extfilter, (uintptr_t)extfilter + 8);

          /* Is this the first extended filter? */

          if (priv->nextalloc == 1)
            {
              /* Enable the Initialization state */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval |= MCAN_CCCR_INIT;
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

              /* Wait for initialization mode to take effect */

              while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) &
                     MCAN_CCCR_INIT) == 0)
                {
                }

              /* Enable writing to configuration registers */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval |= (MCAN_CCCR_INIT | MCAN_CCCR_CCE);
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

             /* Update the Global Filter Configuration so that received
              * messages are rejected if they do not match the acceptance
              * filter.
              *
              *   ANFE=2: Discard all rejected frames
              */

              regval  = mcan_getreg(priv, SAM_MCAN_GFC_OFFSET);
              regval &= ~MCAN_GFC_ANFE_MASK;
              regval |= MCAN_GFC_ANFE_REJECTED;
              mcan_putreg(priv, SAM_MCAN_GFC_OFFSET, regval);

              /* Disable writing to configuration registers */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval &= ~(MCAN_CCCR_INIT | MCAN_CCCR_CCE);
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);
            }

          nxmutex_unlock(&priv->lock);
          return ndx;
        }
    }

  DEBUGASSERT(priv->nextalloc == priv->config->nextfilters);
  nxmutex_unlock(&priv->lock);
  return -EAGAIN;
}
#endif

/****************************************************************************
 * Name: mcan_del_extfilter
 *
 * Description:
 *   Remove an address filter for a standard 29 bit address.
 *
 * Input Parameters:
 *   priv - An instance of the MCAN driver state structure.
 *   ndx  - The filter index previously returned by the mcan_add_extfilter().
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise a negated errno value is
 *   returned to indicate the nature of the error.
 *
 ****************************************************************************/

#ifdef CONFIG_CAN_EXTID
static int mcan_del_extfilter(struct sam_mcan_s *priv, int ndx)
{
  const struct sam_config_s *config;
  uint32_t *extfilter;
  uint32_t regval;
  int word;
  int bit;
  int ret;

  DEBUGASSERT(priv != NULL && priv->config != NULL);
  config = priv->config;

  /* Check user Parameters */

  DEBUGASSERT(ndx >= 0 || ndx < config->nextfilters);

  if (ndx < 0 || ndx >= config->nextfilters)
    {
      return -EINVAL;
    }

  /* Get exclusive excess to the MCAN hardware */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  word = ndx >> 5;
  bit  = ndx & 0x1f;

  /* Check if this filter is really assigned */

  if ((priv->extfilters[word] & (1 << bit)) == 0)
    {
      /* No, error out */

      nxmutex_unlock(&priv->lock);
      return -ENOENT;
    }

  /* Release the filter */

  priv->extfilters[word] &= ~(1 << bit);

  DEBUGASSERT(priv->nextalloc > 0);
  priv->nextalloc--;

  /* Was that the last extended filter? */

  if (priv->nextalloc == 0)
    {
      /* Enable the Initialization state */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval |= MCAN_CCCR_INIT;
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

      /* Wait for initialization mode to take effect */

      while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) &
             MCAN_CCCR_INIT) == 0)
        {
        }

      /* Enable writing to configuration registers */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval |= (MCAN_CCCR_INIT | MCAN_CCCR_CCE);
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

      /* If there are no extended filters, then modify Global Filter
       * Configuration so that all rejected messages are places in RX
       * FIFO0.
       *
       *   ANFE=0: Store all rejected extended frame in RX FIFO0
       */

      regval  = mcan_getreg(priv, SAM_MCAN_GFC_OFFSET);
      regval &= ~MCAN_GFC_ANFE_MASK;
      regval |= MCAN_GFC_ANFE_RX_FIFO0;
      mcan_putreg(priv, SAM_MCAN_GFC_OFFSET, regval);

      /* Disable writing to configuration registers */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval &= ~(MCAN_CCCR_INIT | MCAN_CCCR_CCE);
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);
    }

  /* Deactivate the filter last so that no messages are lost. */

  extfilter    = config->msgram.extfilters + (ndx << 1);
  *extfilter++ = 0;
  *extfilter   = 0;

  nxmutex_unlock(&priv->lock);
  return OK;
}
#endif

/****************************************************************************
 * Name: mcan_add_stdfilter
 *
 * Description:
 *   Add an address filter for a standard 11 bit address.
 *
 * Input Parameters:
 *   priv      - An instance of the MCAN driver state structure.
 *   stdconfig - The configuration of the standard filter
 *
 * Returned Value:
 *   A non-negative filter ID is returned on success.  Otherwise a negated
 *   errno value is returned to indicate the nature of the error.
 *
 ****************************************************************************/

static int mcan_add_stdfilter(struct sam_mcan_s *priv,
                              struct canioc_stdfilter_s *stdconfig)
{
  const struct sam_config_s *config;
  uint32_t *stdfilter;
  uint32_t regval;
  int word;
  int bit;
  int ndx;
  int ret;

  DEBUGASSERT(priv != NULL && priv->config != NULL);
  config = priv->config;

  /* Get exclusive excess to the MCAN hardware */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* Find an unused standard filter */

  for (ndx = 0; ndx < config->nstdfilters; ndx++)
    {
      /* Is this filter assigned? */

      word = ndx >> 5;
      bit  = ndx & 0x1f;

      if ((priv->stdfilters[word] & (1 << bit)) == 0)
        {
          /* No, assign the filter */

          DEBUGASSERT(priv->nstdalloc < priv->config->nstdfilters);
          priv->stdfilters[word] |= (1 << bit);
          priv->nstdalloc++;

          /* Format and write filter word S0 */

          stdfilter = config->msgram.stdfilters + ndx;

          DEBUGASSERT(stdconfig->sf_id1 <= CAN_MAX_STDMSGID);
          regval = STDFILTER_S0_SFID1(stdconfig->sf_id1);

          DEBUGASSERT(stdconfig->sf_id2 <= CAN_MAX_STDMSGID);
          regval |= STDFILTER_S0_SFID2(stdconfig->sf_id2);

          if (stdconfig->sf_prio == 0)
            {
              regval |= STDFILTER_S0_SFEC_FIFO0;
            }
          else
            {
              regval |= STDFILTER_S0_SFEC_FIFO1;
            }

          switch (stdconfig->sf_type)
            {
              default:
              case CAN_FILTER_DUAL:
                regval |= STDFILTER_S0_SFT_DUAL;
                break;

              case CAN_FILTER_MASK:
                regval |= STDFILTER_S0_SFT_CLASSIC;
                break;
              case CAN_FILTER_RANGE:
                regval |= STDFILTER_S0_SFT_RANGE;
                break;
            }

          *stdfilter = regval;

          /* Flush the filter entry into physical RAM */

          up_clean_dcache((uintptr_t)stdfilter, (uintptr_t)stdfilter + 4);

          /* Is this the first standard filter? */

          if (priv->nstdalloc == 1)
            {
              /* Enable the Initialization state */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval |= MCAN_CCCR_INIT;
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

              /* Wait for initialization mode to take effect */

              while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) &
                     MCAN_CCCR_INIT) == 0)
                {
                }

              /* Enable writing to configuration registers */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval |= (MCAN_CCCR_INIT | MCAN_CCCR_CCE);
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

             /* Update the Global Filter Configuration so that received
              * messages are rejected if they do not match the acceptance
              * filter.
              *
              *   ANFS=2: Discard all rejected frames
              */

              regval  = mcan_getreg(priv, SAM_MCAN_GFC_OFFSET);
              regval &= ~MCAN_GFC_ANFS_MASK;
              regval |= MCAN_GFC_ANFS_REJECTED;
              mcan_putreg(priv, SAM_MCAN_GFC_OFFSET, regval);

              /* Disable writing to configuration registers */

              regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
              regval &= ~(MCAN_CCCR_INIT | MCAN_CCCR_CCE);
              mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);
            }

          nxmutex_unlock(&priv->lock);
          return ndx;
        }
    }

  DEBUGASSERT(priv->nstdalloc == priv->config->nstdfilters);
  nxmutex_unlock(&priv->lock);
  return -EAGAIN;
}

/****************************************************************************
 * Name: mcan_del_stdfilter
 *
 * Description:
 *   Remove an address filter for a standard 29 bit address.
 *
 * Input Parameters:
 *   priv - An instance of the MCAN driver state structure.
 *   ndx  - The filter index previously returned by the mcan_add_stdfilter().
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise a negated errno value is
 *   returned to indicate the nature of the error.
 *
 ****************************************************************************/

static int mcan_del_stdfilter(struct sam_mcan_s *priv, int ndx)
{
  const struct sam_config_s *config;
  uint32_t *stdfilter;
  uint32_t regval;
  int word;
  int bit;
  int ret;

  DEBUGASSERT(priv != NULL && priv->config != NULL);
  config = priv->config;

  /* Check Userspace Parameters */

  DEBUGASSERT(ndx >= 0 || ndx < config->nstdfilters);

  if (ndx < 0 || ndx >= config->nstdfilters)
    {
      return -EINVAL;
    }

  /* Get exclusive excess to the MCAN hardware */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  word = ndx >> 5;
  bit  = ndx & 0x1f;

  /* Check if this filter is really assigned */

  if ((priv->stdfilters[word] & (1 << bit)) == 0)
    {
      /* No, error out */

      nxmutex_unlock(&priv->lock);
      return -ENOENT;
    }

  /* Release the filter */

  priv->stdfilters[word] &= ~(1 << bit);

  DEBUGASSERT(priv->nstdalloc > 0);
  priv->nstdalloc--;

  /* Was that the last standard filter? */

  if (priv->nstdalloc == 0)
    {
      /* Enable the Initialization state */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval |= MCAN_CCCR_INIT;
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

      /* Wait for initialization mode to take effect */

      while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) &
              MCAN_CCCR_INIT) == 0)
        {
        }

      /* Enable writing to configuration registers */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval |= (MCAN_CCCR_INIT | MCAN_CCCR_CCE);
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

      /* If there are no standard filters, then modify Global Filter
       * Configuration so that all rejected messages are places in RX
       * FIFO0.
       *
       *   ANFS=0: Store all rejected extended frame in RX FIFO0
       */

      regval  = mcan_getreg(priv, SAM_MCAN_GFC_OFFSET);
      regval &= ~MCAN_GFC_ANFS_MASK;
      regval |= MCAN_GFC_ANFS_RX_FIFO0;
      mcan_putreg(priv, SAM_MCAN_GFC_OFFSET, regval);

      /* Disable writing to configuration registers */

      regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval &= ~(MCAN_CCCR_INIT | MCAN_CCCR_CCE);
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);
    }

  /* Deactivate the filter last so that no messages are lost. */

  stdfilter  = config->msgram.stdfilters + ndx;
  *stdfilter = 0;

  nxmutex_unlock(&priv->lock);
  return OK;
}

/****************************************************************************
 * Name: mcan_start_busoff_recovery_sequence
 *
 * Description:
 *   This function initiates the BUS-OFF recovery sequence.
 *   CAN Specification Rev. 2.0 or ISO11898-1:2015
 *   According the SAMV71 datasheet:
 *
 *   "If the device goes Bus_Off, it will set MCAN_CCCR.INIT of its own
 *    accord, stopping all bus activities. Once MCAN_CCCR.INIT has been
 *    cleared by the processor (application), the device will then wait for
 *    129 occurrences of Bus Idle (129 * 11 consecutive recessive bits)
 *    before resuming normal operation. At the end of the Bus_Off recovery
 *    sequence, the Error Management Counters will be reset. During the
 *    waiting time after the resetting of MCAN_CCCR.INIT, each time a
 *    sequence of 11 recessive bits has been monitored, a Bit0 Error code is
 *    written to MCAN_PSR.LEC, enabling the processor to readily check up
 *    whether the CAN bus is stuck at dominant or continuously disturbed and
 *    to monitor the Bus_Off recovery sequence.  MCAN_ECR.REC is used to
 *    count these sequences."
 *
 * Input Parameters:
 *   priv - An instance of the MCAN driver state structure.
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise a negated errno value is
 *   returned to indicate the nature of the error.
 *
 ****************************************************************************/

static int mcan_start_busoff_recovery_sequence(struct sam_mcan_s *priv)
{
  uint32_t regval;
  int ret;

  DEBUGASSERT(priv);

  /* Get exclusive access to the MCAN peripheral */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* only start BUS-OFF recovery if we are in BUS-OFF state */

  regval = mcan_getreg(priv, SAM_MCAN_PSR_OFFSET);
  if (!(regval & MCAN_PSR_BO))
    {
      nxmutex_unlock(&priv->lock);
      return -EPERM;
    }

  /* Disable initialization mode to issue the recovery sequence */

  regval = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  regval &= ~MCAN_CCCR_INIT;
  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  nxmutex_unlock(&priv->lock);
  return OK;
}

/****************************************************************************
 * Name: mcan_reset
 *
 * Description:
 *   Reset the MCAN device.  Called early to initialize the hardware. This
 *   function is called, before mcan_setup() and on error conditions.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *  None
 *
 ****************************************************************************/

static void mcan_reset(struct can_dev_s *dev)
{
  struct sam_mcan_s *priv;
  const struct sam_config_s *config;

  DEBUGASSERT(dev);
  priv = dev->cd_priv;
  DEBUGASSERT(priv);
  config = priv->config;
  DEBUGASSERT(config);

  caninfo("MCAN%d\n", config->port);
  UNUSED(config);

  /* Get exclusive access to the MCAN peripheral */

  nxmutex_lock(&priv->lock);

  /* Disable all interrupts */

  mcan_putreg(priv, SAM_MCAN_IE_OFFSET, 0);
  mcan_putreg(priv, SAM_MCAN_TXBTIE_OFFSET, 0);

  /* Make sure that all buffers are released.
   *
   * REVISIT: What if a thread is waiting for a buffer?  The following
   * will not wake up any waiting threads.
   */

  nxsem_reset(&priv->txfsem, config->ntxfifoq);

  /* Disable peripheral clocking to the MCAN controller */

  sam_disableperiph1(priv->config->pid);
  priv->state = MCAN_STATE_RESET;
  nxmutex_unlock(&priv->lock);
}

/****************************************************************************
 * Name: mcan_setup
 *
 * Description:
 *   Configure the MCAN. This method is called the first time that the MCAN
 *   device is opened.  This will occur when the port is first opened.
 *   This setup includes configuring and attaching MCAN interrupts.
 *   All MCAN interrupts are disabled upon return.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int mcan_setup(struct can_dev_s *dev)
{
  struct sam_mcan_s *priv;
  const struct sam_config_s *config;
  int ret;

  DEBUGASSERT(dev);
  priv = dev->cd_priv;
  DEBUGASSERT(priv);
  config = priv->config;
  DEBUGASSERT(config);

  caninfo("MCAN%d pid: %d\n", config->port, config->pid);

  /* Get exclusive access to the MCAN peripheral */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return ret;
    }

  /* MCAN hardware initialization */

  ret = mcan_hw_initialize(priv);
  if (ret < 0)
    {
      canerr("ERROR: MCAN%d H/W initialization failed: %d\n",
             config->port, ret);
      return ret;
    }

  mcan_dumpregs(priv, "After hardware initialization");

  /* Attach the MCAN interrupt handlers */

  ret = irq_attach(config->irq0, mcan_interrupt, dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach MCAN%d line 0 IRQ (%d)",
      config->port, config->irq0);
      return ret;
    }

  ret = irq_attach(config->irq1, mcan_interrupt, dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach MCAN%d line 1 IRQ (%d)",
      config->port, config->irq1);
      return ret;
    }

  /* Enable receive interrupts */

  priv->state = MCAN_STATE_SETUP;
  mcan_rxint(dev, true);

  mcan_dumpregs(priv, "After receive setup");

  /* Enable the interrupts at the NVIC (they are still disabled at the MCAN
   * peripheral).
   */

  up_enable_irq(config->irq0);
  up_enable_irq(config->irq1);
  nxmutex_unlock(&priv->lock);
  return OK;
}

/****************************************************************************
 * Name: mcan_shutdown
 *
 * Description:
 *   Disable the MCAN.  This method is called when the MCAN device is closed.
 *   This method reverses the operation the setup method.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void mcan_shutdown(struct can_dev_s *dev)
{
  struct sam_mcan_s *priv;
  const struct sam_config_s *config;

  DEBUGASSERT(dev);
  priv = dev->cd_priv;
  DEBUGASSERT(priv);
  config = priv->config;
  DEBUGASSERT(config);

  caninfo("MCAN%d\n", config->port);

  /* Get exclusive access to the MCAN peripheral */

  nxmutex_lock(&priv->lock);

  /* Disable MCAN interrupts at the NVIC */

  up_disable_irq(config->irq0);
  up_disable_irq(config->irq1);

  /* Disable all interrupts from the MCAN peripheral */

  mcan_putreg(priv, SAM_MCAN_IE_OFFSET, 0);
  mcan_putreg(priv, SAM_MCAN_TXBTIE_OFFSET, 0);

  /* Detach the MCAN interrupt handler */

  irq_detach(config->irq0);
  irq_detach(config->irq1);

  /* Disable peripheral clocking to the MCAN controller */

  sam_disableperiph1(priv->config->pid);
  nxmutex_unlock(&priv->lock);
}

/****************************************************************************
 * Name: mcan_rxint
 *
 * Description:
 *   Call to enable or disable RX interrupts.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void mcan_rxint(struct can_dev_s *dev, bool enable)
{
  struct sam_mcan_s *priv = dev->cd_priv;
  irqstate_t flags;
  uint32_t regval;

  DEBUGASSERT(priv && priv->config);

  caninfo("MCAN%d enable: %d\n", priv->config->port, enable);

  /* Enable/disable the receive interrupts */

  flags = enter_critical_section();
  regval = mcan_getreg(priv, SAM_MCAN_IE_OFFSET);

  if (enable)
    {
      regval |= priv->rxints | MCAN_COMMON_INTS;
    }
  else
    {
      regval &= ~priv->rxints;
    }

  mcan_putreg(priv, SAM_MCAN_IE_OFFSET, regval);
  leave_critical_section(flags);
}

/****************************************************************************
 * Name: mcan_txint
 *
 * Description:
 *   Call to enable or disable TX interrupts.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void mcan_txint(struct can_dev_s *dev, bool enable)
{
  struct sam_mcan_s *priv = dev->cd_priv;
  irqstate_t flags;
  uint32_t regval;

  DEBUGASSERT(priv && priv->config);

  caninfo("MCAN%d enable: %d\n", priv->config->port, enable);

  /* Enable/disable the receive interrupts */

  flags = enter_critical_section();
  regval = mcan_getreg(priv, SAM_MCAN_IE_OFFSET);

  if (enable)
    {
      regval |= priv->txints | MCAN_COMMON_INTS;
    }
  else
    {
      regval &= ~priv->txints;
    }

  mcan_putreg(priv, SAM_MCAN_IE_OFFSET, regval);
  leave_critical_section(flags);
}

/****************************************************************************
 * Name: mcan_ioctl
 *
 * Description:
 *   All ioctl calls will be routed through this method
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int mcan_ioctl(struct can_dev_s *dev, int cmd, unsigned long arg)
{
  struct sam_mcan_s *priv;
  int ret = -ENOTTY;

  caninfo("cmd=%04x arg=%lu\n", cmd, arg);

  DEBUGASSERT(dev && dev->cd_priv);
  priv = dev->cd_priv;

  /* Handle the command */

  switch (cmd)
    {
      /* CANIOC_GET_BITTIMING:
       *   Description:    Return the current bit timing settings
       *   Argument:       A pointer to a write-able instance of struct
       *                   canioc_bittiming_s in which current bit timing
       *                   values will be returned.
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       *   Dependencies:   None
       */

      case CANIOC_GET_BITTIMING:
        {
          struct canioc_bittiming_s *bt =
                 (struct canioc_bittiming_s *)arg;
          uint32_t regval;
          uint32_t brp;

          DEBUGASSERT(bt != NULL);

#ifdef CONFIG_CAN_FD
          if (bt->type == CAN_BITTIMING_DATA)
            {
              regval       = mcan_getreg(priv, SAM_MCAN_FBTP_OFFSET);
              bt->bt_sjw   = ((regval & MCAN_FBTP_FSJW_MASK) >>
                              MCAN_FBTP_FSJW_SHIFT) + 1;
              bt->bt_tseg1 = ((regval & MCAN_FBTP_FTSEG1_MASK) >>
                              MCAN_FBTP_FTSEG1_SHIFT) + 1;
              bt->bt_tseg2 = ((regval & MCAN_FBTP_FTSEG2_MASK) >>
                              MCAN_FBTP_FTSEG2_SHIFT) + 1;

              brp          = ((regval & MCAN_FBTP_FBRP_MASK) >>
                              MCAN_FBTP_FBRP_SHIFT) + 1;
              bt->bt_baud  = SAMA5_MCANCLK_FREQUENCY / brp /
                            (bt->bt_tseg1 + bt->bt_tseg2 + 1);
            }
          else
#endif
            {
              regval       = mcan_getreg(priv, SAM_MCAN_BTP_OFFSET);
              bt->bt_sjw   = ((regval & MCAN_BTP_SJW_MASK) >>
                              MCAN_BTP_SJW_SHIFT) + 1;
              bt->bt_tseg1 = ((regval & MCAN_BTP_TSEG1_MASK) >>
                              MCAN_BTP_TSEG1_SHIFT) + 1;
              bt->bt_tseg2 = ((regval & MCAN_BTP_TSEG2_MASK) >>
                              MCAN_BTP_TSEG2_SHIFT) + 1;

              brp          = ((regval & MCAN_BTP_BRP_MASK) >>
                              MCAN_BTP_BRP_SHIFT) + 1;
              bt->bt_baud  = SAMA5_MCANCLK_FREQUENCY / brp /
                            (bt->bt_tseg1 + bt->bt_tseg2 + 1);
            }

          ret = OK;
        }
        break;

      /* CANIOC_SET_BITTIMING:
       *   Description:    Set new current bit timing values
       *   Argument:       A pointer to a read-able instance of struct
       *                   canioc_bittiming_s in which the new bit timing
       *                   values are provided.
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       *   Dependencies:   None
       *
       * REVISIT: There is probably a limitation here:  If there are
       * multiple threads trying to send CAN packets, when one of these
       * threads reconfigures the bitrate, the MCAN hardware will be reset
       * and the context of operation will be lost.  Hence, this IOCTL can
       * only safely be executed in quiescent time periods.
       */

      case CANIOC_SET_BITTIMING:
        {
          const struct canioc_bittiming_s *bt =
                       (const struct canioc_bittiming_s *)arg;
          irqstate_t flags;
          uint32_t brp;
          uint32_t tseg1;
          uint32_t tseg2;
          uint32_t sjw;
          uint32_t ie;
          uint8_t state;

          DEBUGASSERT(bt != NULL);
          DEBUGASSERT(bt->bt_baud < SAMA5_MCANCLK_FREQUENCY);
          DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 16);
          DEBUGASSERT(bt->bt_tseg1 > 1 && bt->bt_tseg1 <= 64);
          DEBUGASSERT(bt->bt_tseg2 > 0 && bt->bt_tseg2 <= 16);

          /* Extract bit timing data */

          tseg1 = bt->bt_tseg1 - 1;
          tseg2 = bt->bt_tseg2 - 1;
          sjw   = bt->bt_sjw   - 1;

          brp = (uint32_t)
            (((float) SAMA5_MCANCLK_FREQUENCY /
             ((float)(tseg1 + tseg2 + 3) * (float)bt->bt_baud)) - 1);

          /* Save the value of the new bit timing register */

          flags = enter_critical_section();
#ifdef CONFIG_CAN_FD
          if (bt->type == CAN_BITTIMING_DATA)
            {
              priv->fbtp = MCAN_FBTP_FBRP(brp) | MCAN_FBTP_FTSEG1(tseg1) |
                           MCAN_FBTP_FTSEG2(tseg2) | MCAN_FBTP_FSJW(sjw);
            }
          else
#endif
            {
              priv->btp = MCAN_BTP_BRP(brp) | MCAN_BTP_TSEG1(tseg1) |
                          MCAN_BTP_TSEG2(tseg2) | MCAN_BTP_SJW(sjw);
            }

          /* We need to reset to instantiate the new timing.  Save
           * current state information so that recover to this
           * state.
           */

          ie    = mcan_getreg(priv, SAM_MCAN_IE_OFFSET);
          state = priv->state;

          /* Reset the MCAN */

          mcan_reset(dev);
          ret = OK;

          /* If we have previously been setup, then setup again */

          if (state == MCAN_STATE_SETUP)
            {
              ret = mcan_setup(dev);
            }

          /* We we have successfully re-initialized, then restore the
           * interrupt state.
           *
           * REVISIT: Since the hardware was reset, any pending TX
           * activity was lost.  Should we disable TX interrupts?
           */

          if (ret == OK)
            {
              mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie & ~priv->txints);
            }

          leave_critical_section(flags);
        }
        break;

#ifdef CONFIG_CAN_EXTID
      /* CANIOC_ADD_EXTFILTER:
       *   Description:    Add an address filter for a extended 29 bit
       *                   address.
       *   Argument:       A reference to struct canioc_extfilter_s
       *   Returned Value: A non-negative filter ID is returned on success.
       *                   Otherwise -1 (ERROR) is returned with the errno
       *                   variable set to indicate the nature of the error.
       */

      case CANIOC_ADD_EXTFILTER:
        {
          DEBUGASSERT(arg != 0);

          ret = mcan_add_extfilter(priv,
                                   (struct canioc_extfilter_s *)arg);
        }
        break;

      /* CANIOC_DEL_EXTFILTER:
       *   Description:    Remove an address filter for a standard 29 bit
       *                   address.
       *   Argument:       The filter index previously returned by the
       *                   CANIOC_ADD_EXTFILTER command
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       */

      case CANIOC_DEL_EXTFILTER:
        {
          DEBUGASSERT(arg <= priv->config->nextfilters);
          ret = mcan_del_extfilter(priv, (int)arg);
        }
        break;
#endif

      /* CANIOC_ADD_STDFILTER:
       *   Description:    Add an address filter for a standard 11 bit
       *                   address.
       *   Argument:       A reference to struct canioc_stdfilter_s
       *   Returned Value: A non-negative filter ID is returned on success.
       *                   Otherwise -1 (ERROR) is returned with the errno
       *                   variable set to indicate the nature of the error.
       */

      case CANIOC_ADD_STDFILTER:
        {
          DEBUGASSERT(arg != 0);

          ret = mcan_add_stdfilter(priv,
                                   (struct canioc_stdfilter_s *)arg);
        }
        break;

      /* CANIOC_DEL_STDFILTER:
       *   Description:    Remove an address filter for a standard 11 bit
       *                   address.
       *   Argument:       The filter index previously returned by the
       *                   CANIOC_ADD_STDFILTER command
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       */

      case CANIOC_DEL_STDFILTER:
        {
          DEBUGASSERT(arg <= priv->config->nstdfilters);
          ret = mcan_del_stdfilter(priv, (int)arg);
        }
        break;

      /* CANIOC_BUSOFF_RECOVERY:
       *   Description : Initiates the BUS - OFF recovery sequence
       *   Argument : None
       *   Returned Value : Zero (OK) is returned on success. Otherwise -1
       *                    (ERROR) is returned with the errno variable set
       *                    to indicate the nature of the error.
       *   Dependencies : None
       */

      case CANIOC_BUSOFF_RECOVERY:
        {
          ret = mcan_start_busoff_recovery_sequence(priv);
        }
        break;

        /* CANIOC_SET_NART:
         *   Description:    Enable/Disable NART (No Automatic Retry)
         *   Argument:       Set to 1 to enable NART, 0 to disable.
         *                   Default is disabled.
         *   Returned Value: Zero (OK) is returned on success. Otherwise -1
         *                   (ERROR) is returned with the errno variable set
         *                   to indicate the nature of the error.
         *   Dependencies:   None
         */

      case CANIOC_SET_NART:
        {
          ret = mcan_set_nart(priv, (bool)arg);
        }
        break;

      /* CANIOC_IFLUSH
       *   Description:    Flush data received but not read
       *   Argument:       None
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       *   Dependencies:   None
       */

      case CANIOC_IFLUSH:
        {
          ret = mcan_cancel_rx_fifos(priv);
        }
        break;

      /* CANIOC_OFLUSH
       *   Description:    Flush data queued but not transmitted
       *   Argument:       None
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       *   Dependencies:   None
       */

      case CANIOC_OFLUSH:
        {
          ret = mcan_cancel_tx_buffers(priv);
        }
        break;

      /* CANIOC_IOFLUSH
       *   Description:    Flush data received but not read, and data queued
                           but not transmitted
       *   Argument:       None
       *   Returned Value: Zero (OK) is returned on success.  Otherwise -1
       *                   (ERROR) is returned with the errno variable set
       *                   to indicate the nature of the error.
       *   Dependencies:   None
       */

      case CANIOC_IOFLUSH:
        {
          ret = mcan_cancel_tx_buffers(priv);
          ret = mcan_cancel_rx_fifos(priv);
        }
        break;

      /* Unsupported/unrecognized command */

      default:
        canerr("ERROR: Unrecognized command: %04x\n", cmd);
        break;
    }

  return ret;
}

/****************************************************************************
 * Name: mcan_remoterequest
 *
 * Description:
 *   Send a remote request
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int mcan_remoterequest(struct can_dev_s *dev, uint16_t id)
{
  /* REVISIT:  Remote request not implemented */

  return -ENOSYS;
}

/****************************************************************************
 * Name: mcan_send
 *
 * Description:
 *    Send one can message.
 *
 *    One CAN-message consists of a maximum of 10 bytes.  A message is
 *    composed of at least the  && priv->config != NULLfirst 2 bytes
 *    (when there are no data bytes).
 *
 *    Byte 0:      Bits 0-7: Bits 3-10 of the 11-bit CAN identifier
 *    Byte 1:      Bits 5-7: Bits 0-2 of the 11-bit CAN identifier
 *                 Bit 4:    Remote Transmission Request (RTR)
 *                 Bits 0-3: Data Length Code (DLC)
 *    Bytes 2-10: CAN data
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int mcan_send(struct can_dev_s *dev, struct can_msg_s *msg)
{
  struct sam_mcan_s *priv;
  const struct sam_config_s *config;
  uint32_t *txbuffer = 0;
  const uint8_t *src;
  uint8_t *dest;
  uint32_t regval;
  unsigned int msglen;
  unsigned int ndx;
  unsigned int nbytes;
  unsigned int i;
  int ret;

  DEBUGASSERT(dev);
  priv = dev->cd_priv;
  DEBUGASSERT(priv && priv->config);
  config = priv->config;

  caninfo("MCAN%d\n", config->port);
  caninfo("MCAN%d ID: %" PRIu32 " DLC: %u\n",
          config->port, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);

  /* That that FIFO elements were configured.
   *
   * REVISIT: Dedicated TX buffers are not used by this driver.
   */

  DEBUGASSERT(config->ntxfifoq > 0);

  /* Reserve a buffer for the transmission, waiting if necessary.  When
   * mcan_buffer_reserve() returns, we are guaranteed that the TX FIFOQ is
   * not full and cannot become full at least until we add our packet to
   * the FIFO.
   *
   * We can't get exclusive access to MCAN resources here because that
   * lock the MCAN while we wait for a free buffer.  Instead, the
   * scheduler is locked here momentarily.  See discussion in
   * mcan_buffer_reserve() for an explanation.
   *
   * REVISIT: This needs to be extended in order to handler case where
   * the MCAN device was opened O_NONBLOCK.
   */

  mcan_buffer_reserve(priv);

  /* Get exclusive access to the MCAN peripheral */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      mcan_buffer_release(priv);
      return ret;
    }

  /* Get our reserved Tx FIFO/queue put index */

  regval = mcan_getreg(priv, SAM_MCAN_TXFQS_OFFSET);
  DEBUGASSERT((regval & MCAN_TXFQS_TFQF) == 0);

  ndx = (regval & MCAN_TXFQS_TFQPI_MASK) >> MCAN_TXFQS_TFQPI_SHIFT;

  /* And the TX buffer corresponding to this index */

  txbuffer = config->msgram.txdedicated + ndx * config->txbufferesize;

  /* Format the TX FIFOQ entry
   *
   * Format word T1:
   *   Transfer message ID (ID)          - Value from message structure
   *   Remote Transmission Request (RTR) - Value from message structure
   *   Extended Identifier (XTD)         - Depends on configuration.
   */

#ifdef CONFIG_CAN_EXTID
  if (msg->cm_hdr.ch_extid)
    {
      DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID);

      regval = BUFFER_R0_EXTID(msg->cm_hdr.ch_id) | BUFFER_R0_XTD;
    }
  else
#endif
    {
      DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID);

      regval = BUFFER_R0_STDID(msg->cm_hdr.ch_id);
    }

  if (msg->cm_hdr.ch_rtr)
    {
      regval |= BUFFER_R0_RTR;
    }

  txbuffer[0] = regval;
  reginfo("T0: %08" PRIx32 "\n", regval);

  /* Format word T1:
   *   Data Length Code (DLC)            - Value from message structure
   *   Event FIFO Control (EFC)          - Do not store events.
   *   Message Marker (MM)               - Always zero
   */

  txbuffer[1] = BUFFER_R1_DLC(msg->cm_hdr.ch_dlc);
  reginfo("T1: %08" PRIx32 "\n", txbuffer[1]);

  /* Followed by the amount of data corresponding to the DLC (T2..) */

  dest   = (uint8_t *)&txbuffer[2];
  src    = msg->cm_data;
  nbytes = mcan_dlc2bytes(priv, msg->cm_hdr.ch_dlc);

  for (i = 0; i < nbytes; i++)
    {
      /* Little endian is assumed */

      *dest++ = *src++;
    }

  /* Flush the D-Cache to memory before initiating the transfer */

  msglen = 2 * sizeof(uint32_t) + nbytes;
  up_clean_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + msglen);
  UNUSED(msglen);

  /* Enable transmit interrupts from the TX FIFOQ buffer by setting TC
   * interrupt bit in IR (also requires that the TC interrupt is enabled)
   */

  mcan_dumpregs(priv, "before tx req");
  mcan_putreg(priv, SAM_MCAN_TXBTIE_OFFSET, (1 << ndx));

  /* And request to send the packet */

  mcan_putreg(priv, SAM_MCAN_TXBAR_OFFSET, (1 << ndx));
  mcan_dumpregs(priv, "After tx done");
  nxmutex_unlock(&priv->lock);

  /* Report that the TX transfer is complete to the upper half logic.  Of
   * course, the transfer is not complete, but this early notification
   * allows the upper half logic to free resources sooner.
   *
   * REVISIT:  Should we disable interrupts?  can_txdone() was designed to
   * be called from an interrupt handler and, hence, may be unsafe when
   * called from the tasking level.
   */

  can_txdone(dev);
  return OK;
}

/****************************************************************************
 * Name: mcan_txready
 *
 * Description:
 *   Return true if the MCAN hardware can accept another TX message.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   True if the MCAN hardware is ready to accept another TX message.
 *
 ****************************************************************************/

static bool mcan_txready(struct can_dev_s *dev)
{
  struct sam_mcan_s *priv = dev->cd_priv;
  uint32_t regval;
  bool notfull;
#ifdef CONFIG_DEBUG_FEATURES
  int sval;
#endif
  int ret;

  /* Get exclusive access to the MCAN peripheral */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return false;
    }

  /* Return the state of the TX FIFOQ.  Return TRUE if the TX FIFO/Queue is
   * not full.
   *
   * REVISIT: Dedicated TX buffers are not supported.
   */

  regval  = mcan_getreg(priv, SAM_MCAN_TXFQS_OFFSET);
  notfull = ((regval & MCAN_TXFQS_TFQF) == 0);

#ifdef CONFIG_DEBUG_FEATURES
  /* As a sanity check, the txfsem should also track the number of elements
   * the TX FIFO/queue.  Make sure that they are consistent.
   */

  nxsem_get_value(&priv->txfsem, &sval);
  DEBUGASSERT(((notfull && sval > 0) || (!notfull && sval <= 0)) &&
              (sval <= priv->config->ntxfifoq));
#endif

  nxmutex_unlock(&priv->lock);
  return notfull;
}

/****************************************************************************
 * Name: mcan_txempty
 *
 * Description:
 *   Return true if all message have been sent.  If for example, the MCAN
 *   hardware implements FIFOs, then this would mean the transmit FIFO is
 *   empty.  This method is called when the driver needs to make sure that
 *   all characters are "drained" from the TX hardware before calling
 *   co_shutdown().
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   True if there are no pending TX transfers in the MCAN hardware.
 *
 ****************************************************************************/

static bool mcan_txempty(struct can_dev_s *dev)
{
  struct sam_mcan_s *priv = dev->cd_priv;
  uint32_t regval;
#ifdef CONFIG_SAMA5_MCAN_QUEUE_MODE
  int sval;
#else
  int tffl;
#endif
  bool empty;
  int ret;

  DEBUGASSERT(priv != NULL && priv->config != NULL);

  /* Get exclusive access to the MCAN peripheral */

  ret = nxmutex_lock(&priv->lock);
  if (ret < 0)
    {
      return false;
    }

  /* Return the state of the TX FIFOQ.  Return TRUE if the TX FIFO/Queue is
   * empty.  We don't have a reliable indication that the FIFO is empty, so
   * we have to use some heuristics.
   *
   * REVISIT: Dedicated TX buffers are not supported.
   */

  regval = mcan_getreg(priv, SAM_MCAN_TXFQS_OFFSET);
  if ((regval & MCAN_TXFQS_TFQF) != 0)
    {
      nxmutex_unlock(&priv->lock);
      return false;
    }

#ifdef CONFIG_SAMA5_MCAN_QUEUE_MODE
  /* The TX FIFO/Queue is not full, but is it empty?  The txfsem should
   * track the number of elements the TX FIFO/queue in use.
   *
   * Since the FIFO is not full, the semaphore count should be greater
   * than zero.  If it is equal to the full count of TX FIFO/Queue
   * elements, then there is no transfer in progress.
   */

  nxsem_get_value(&priv->txfsem, &sval);
  DEBUGASSERT(sval > 0 && sval <= priv->config->ntxfifoq);

  empty = (sval ==  priv->config->ntxfifoq);
#else
  /* Tx FIFO Free Level */

  tffl  = (regval & MCAN_TXFQS_TFFL_MASK) >> MCAN_TXFQS_TFFL_SHIFT;
  empty = (tffl >= priv->config->ntxfifoq);
#endif

  nxmutex_unlock(&priv->lock);
  return empty;
}

/****************************************************************************
 * Name: mcan_dedicated_rxbuffer_available
 *
 * Description:
 *   Check if data is available in a dedicated RX buffer.
 *
 * Input Parameters:
 *   priv   - MCAN-specific private data
 *   bufndx - Buffer index
 *
 *   None
 * Returned Value:
 *   True: Data is available
 *
 ****************************************************************************/

#ifndef DEDICATED_BUFFERS_NOT_USED
bool mcan_dedicated_rxbuffer_available(struct sam_mcan_s *priv,
                                       int bufndx)
{
  if (bufndx < 32)
    {
      return (bool)(mcan->MCAN_NDAT1 & (1 << bufndx));
    }
  else if (bufndx < 64)
    {
      return (bool)(mcan->MCAN_NDAT1 & (1 << (bufndx - 32)));
    }
  else
    {
      return false;
    }
}
#endif

/****************************************************************************
 * Name: mcan_error
 *
 * Description:
 *   Report a CAN error
 *
 * Input Parameters:
 *   dev        - CAN-common state data
 *   status     - Interrupt status with error bits set
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_CAN_ERRORS
static void mcan_error(struct can_dev_s *dev, uint32_t status)
{
  struct sam_mcan_s *priv = dev->cd_priv;
  struct can_hdr_s hdr;
  uint32_t psr;
  uint16_t errbits;
  uint8_t data[CAN_ERROR_DLC];
  int ret;

  /* Encode error bits */

  errbits = 0;
  memset(data, 0, sizeof(data));

  /* Always fill in "static" error conditions, but set the signaling bit
   * only if the condition has changed (see IRQ-Flags below)
   * They have to be filled in every time CAN_ERROR_CONTROLLER is set.
   */

  psr = mcan_getreg(priv, SAM_MCAN_PSR_OFFSET);
  if ((psr & MCAN_PSR_EP) != 0)
    {
      data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
    }

  if (psr & MCAN_PSR_EW)
    {
      data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
    }

  if ((status & (MCAN_INT_EP | MCAN_INT_EW)) != 0)
    {
      /* "Error Passive" or "Error Warning" status changed */

      errbits |= CAN_ERROR_CONTROLLER;
    }

  if ((status & MCAN_INT_BO) != 0)
    {
      /* Bus_Off Status changed */

      if ((psr & MCAN_PSR_BO) != 0)
        {
          errbits |= CAN_ERROR_BUSOFF;
        }
      else
        {
          errbits |= CAN_ERROR_RESTARTED;
        }
    }

  if ((status & (MCAN_INT_RF0L | MCAN_INT_RF1L)) != 0)
    {
      /* Receive FIFO 0/1 Message Lost
       * Receive FIFO 1 Message Lost
       */

      errbits |= CAN_ERROR_CONTROLLER;
      data[1] |= CAN_ERROR1_RXOVERFLOW;
    }

  if ((status & MCAN_INT_TEFL) != 0)
    {
      /* Tx Event FIFO Element Lost */

      errbits |= CAN_ERROR_CONTROLLER;
      data[1] |= CAN_ERROR1_TXOVERFLOW;
    }

  if ((status & MCAN_INT_TOO) != 0)
    {
      /* Timeout Occurred */

      errbits |= CAN_ERROR_TXTIMEOUT;
    }

  if ((status & (MCAN_INT_MRAF | MCAN_INT_ELO)) != 0)
    {
      /* Message RAM Access Failure
       * Error Logging Overflow
       */

      errbits |= CAN_ERROR_CONTROLLER;
      data[1] |= CAN_ERROR1_UNSPEC;
    }

  if ((status & MCAN_INT_CRCE) != 0)
    {
      /* Receive CRC Error */

      errbits |= CAN_ERROR_PROTOCOL;
      data[3] |= (CAN_ERROR3_CRCSEQ | CAN_ERROR3_CRCDEL);
    }

  if ((status & MCAN_INT_BE) != 0)
    {
      /* Bit Error */

      errbits |= CAN_ERROR_PROTOCOL;
      data[2] |= CAN_ERROR2_BIT;
    }

  if ((status & MCAN_INT_ACKE) != 0)
    {
      /* Acknowledge Error */

      errbits |= CAN_ERROR_NOACK;
    }

  if ((status & MCAN_INT_FOE) != 0)
    {
      /* Format Error */

      errbits |= CAN_ERROR_PROTOCOL;
      data[2] |= CAN_ERROR2_FORM;
    }

  if ((status & MCAN_INT_STE) != 0)
    {
      /* Stuff Error */

      errbits |= CAN_ERROR_PROTOCOL;
      data[2] |= CAN_ERROR2_STUFF;
    }

  if (errbits != 0)
    {
      /* Format the CAN header for the error report. */

      hdr.ch_id    = errbits;
      hdr.ch_dlc   = CAN_ERROR_DLC;
      hdr.ch_rtr   = 0;
      hdr.ch_error = 1;
#ifdef CONFIG_CAN_EXTID
      hdr.ch_extid = 0;
#endif
      hdr.ch_tcf   = 0;

      /* And provide the error report to the upper half logic */

      ret = can_receive(dev, &hdr, data);
      if (ret < 0)
        {
          canerr("ERROR: can_receive failed: %d\n", ret);
        }
    }
}
#endif /* CONFIG_CAN_ERRORS */

/****************************************************************************
 * Name: mcan_set_nart
 *
 * Description:
 *   Enable/Disable NART (No Automatic Retry),
 *   AKA "DAR" - Disable Automatic Retry
 *
 * Input Parameters:
 *   priv      - An instance of the MCAN driver state structure.
 *   enable - enable or disable.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int mcan_set_nart(struct sam_mcan_s *priv, bool enable)
{
  uint32_t regval;

  DEBUGASSERT(priv != NULL);

  regval = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  if (enable)
    {
      regval |= MCAN_CCCR_DAR;
    }
  else
    {
      regval &= ~MCAN_CCCR_DAR;
    }

  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  return OK;
}

/****************************************************************************
 * Name: mcan_cancel_tx_buffers
 *
 * Description:
 *   Cancel all pending, buffered, transmissions
 *
 * Input Parameters:
 *   priv      - An instance of the MCAN driver state structure.
 *
 * Returned Value:
 *   Success
 *
 ****************************************************************************/

static int mcan_cancel_tx_buffers(struct sam_mcan_s *priv)
{
  uint32_t regval;

  DEBUGASSERT(priv != NULL);

  mcan_putreg(priv, SAM_MCAN_TXBCR_OFFSET, 0xffff);

  do
    {
      regval = mcan_getreg(priv, SAM_MCAN_TXBRP_OFFSET);
    }
  while (regval != 0);

  return OK;
}

/****************************************************************************
 * Name: mcan_cancel_rx_fifos
 *
 * Description:
 *   Cancel all queued/received messages
 *
 * Input Parameters:
 *   priv      - An instance of the MCAN driver state structure.
 *
 * Returned Value:
 *   Success
 *
 ****************************************************************************/

static int mcan_cancel_rx_fifos(struct sam_mcan_s *priv)
{
  uint32_t ir;
  uint32_t ie;
  uint32_t pending;

  DEBUGASSERT(priv != NULL);

  mcan_putreg(priv, SAM_MCAN_RXF0A_OFFSET, 0);
  mcan_putreg(priv, SAM_MCAN_RXF1A_OFFSET, 0);

  /* Clear RX interrupts */

  ir = mcan_getreg(priv, SAM_MCAN_IR_OFFSET);
  ie = mcan_getreg(priv, SAM_MCAN_IE_OFFSET);

  pending = (ir & ie);

  if ((pending & priv->rxints) != 0)
    {
      mcan_putreg(priv, SAM_MCAN_IR_OFFSET, priv->rxints);
    }

  return OK;
}

/****************************************************************************
 * Name: mcan_receive
 *
 * Description:
 *   Receive an MCAN messages
 *
 * Input Parameters:
 *   dev      - CAN-common state data
 *   rxbuffer - The RX buffer containing the received messages
 *   nwords   - The length of the RX buffer (element size in words).
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void mcan_receive(struct can_dev_s *dev, uint32_t *rxbuffer,
                         unsigned long nwords)
{
  struct can_hdr_s hdr;
  uint32_t regval;
  unsigned int nbytes;
  int ret;

  /* Invalidate the D-Cache to ensure
   * that we reread the RX buffer data from memory.
   */

  nbytes = (nwords << 2);
  up_invalidate_dcache((uintptr_t)rxbuffer, (uintptr_t)rxbuffer + nbytes);

  /* Format the CAN header */

  /* Word R0 contains the CAN ID */

  regval = *rxbuffer++;
  reginfo("R0: %08" PRIx32 "\n", regval);

#ifdef CONFIG_CAN_ERRORS
  hdr.ch_error  = 0;
#endif
  hdr.ch_tcf    = 0;

  if ((regval & BUFFER_R0_RTR) != 0)
    {
      hdr.ch_rtr = true;
    }
  else
    {
      hdr.ch_rtr = false;
    }

#ifdef CONFIG_CAN_EXTID
  if ((regval & BUFFER_R0_XTD) != 0)
    {
      /* Save the extended ID of the newly received message */

      hdr.ch_id    = (regval & BUFFER_R0_EXTID_MASK) >>
                     BUFFER_R0_EXTID_SHIFT;
      hdr.ch_extid = true;
    }
  else
    {
      hdr.ch_id    = (regval & BUFFER_R0_STDID_MASK) >>
                     BUFFER_R0_STDID_SHIFT;
      hdr.ch_extid = false;
    }

#else
  if ((regval & BUFFER_R0_XTD) != 0)
    {
      /* Drop any messages with extended IDs */

      return;
    }

  /* Save the standard ID of the newly received message */

  hdr.ch_id = (regval & BUFFER_R0_STDID_MASK) >> BUFFER_R0_STDID_SHIFT;
#endif

  /* Word R1 contains the DLC and timestamp */

  regval = *rxbuffer++;
  reginfo("R1: %08" PRIx32 "\n", regval);

  hdr.ch_dlc = (regval & BUFFER_R1_DLC_MASK) >> BUFFER_R1_DLC_SHIFT;

  /* And provide the CAN message to the upper half logic */

  ret = can_receive(dev, &hdr, (uint8_t *)rxbuffer);
  if (ret < 0)
    {
      canerr("ERROR: can_receive failed: %d\n", ret);
    }
}

/****************************************************************************
 * Name: mcan_interrupt
 *
 * Description:
 *   Common MCAN interrupt handler
 *
 * Input Parameters:
 *   dev - CAN-common state data
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static int mcan_interrupt(int irq, void *context, void *arg)
{
  struct can_dev_s *dev = (struct can_dev_s *)arg;
  struct sam_mcan_s *priv;
  uint32_t ir;
  uint32_t ie;
  uint32_t pending;
  uint32_t regval;
  unsigned int nelem;
  unsigned int ndx;
  bool handled;

  DEBUGASSERT(dev != NULL);
  priv = dev->cd_priv;
  DEBUGASSERT(priv && priv->config);

  /* Loop while there are pending interrupt events */

  do
    {
      /* Get the set of pending interrupts. */

      ir = mcan_getreg(priv, SAM_MCAN_IR_OFFSET);
      ie = mcan_getreg(priv, SAM_MCAN_IE_OFFSET);

      pending = (ir & ie);
      handled = false;

      /* Check for any errors */

      if ((pending & MCAN_ANYERR_INTS) != 0)
        {
          /* Check for common errors */

          if ((pending & MCAN_CMNERR_INTS) != 0)
            {
              canerr("ERROR: Common %08" PRIx32 "\n",
                     pending & MCAN_CMNERR_INTS);

              /* Clear the error indications */

              mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_CMNERR_INTS);
            }

          /* Check for transmission errors */

          if ((pending & MCAN_TXERR_INTS) != 0)
            {
              canerr("ERROR: TX %08" PRIx32 "\n", pending & MCAN_TXERR_INTS);

              /* An Acknowledge-Error will occur if for example the device
               * is not connected to the bus.
               *
               * The CAN-Standard states that the Chip has to retry the
               * message forever, which will produce an ACKE every time.
               * To prevent this Interrupt-Flooding and the high CPU-Load
               * we disable the ACKE here as long we didn't transfer at
               * least one message successfully (see MCAN_INT_TC below).
               */

              ie &= ~MCAN_INT_ACKE;
              mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie);

              /* Clear the error indications */

              mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_TXERR_INTS);

              /* REVISIT:  Will MCAN_INT_TC also be set in the event of
               * a transmission error?  Each write must conclude with a
               * call to man_buffer_release(), whether or not the write
               * was successful.
               *
               * We assume that MCAN_INT_TC will be called for each
               * message buffer. Except the transfer is cancelled.
               * TODO: add handling for MCAN_INT_TCF
               */
            }

          /* Check for reception errors */

          if ((pending & MCAN_RXERR_INTS) != 0)
            {
              canerr("ERROR: RX %08" PRIx32 "\n", pending & MCAN_RXERR_INTS);

              /* To prevent Interrupt-Flooding the current active
               * RX error interrupts are disabled. After successfully
               * receiving at least one CAN packet all RX error interrupts
               * are turned back on.
               *
               * The Interrupt-Flooding can for example occur if the
               * configured CAN speed does not match the speed of the other
               * CAN nodes in the network.
               */

              ie &= ~(pending & MCAN_RXERR_INTS);
              mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie);

              /* Clear the error indications */

              mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_RXERR_INTS);
            }

#ifdef CONFIG_CAN_ERRORS
          /* Report errors */

          mcan_error(dev, pending & MCAN_ANYERR_INTS);
#endif
          handled = true;
        }

      /* Check for successful completion of a transmission */

      if ((pending & MCAN_INT_TC) != 0)
        {
          /* Check if we have disabled the ACKE in the error-handling above
           * (see MCAN_TXERR_INTS) to prevent Interrupt-Flooding and
           * re-enable the error interrupt here again.
           */

          if ((ie & MCAN_INT_ACKE) == 0)
            {
                ie |= MCAN_INT_ACKE;
                mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie);
            }

          /* Clear the pending TX completion interrupt (and all
           * other TX-related interrupts)
           */

          mcan_putreg(priv, SAM_MCAN_IR_OFFSET, priv->txints);

          /* Indicate that there is one more buffer free in the TX FIFOQ by
           * "releasing" it.  This may have the effect of waking up a thread
           * that has been waiting for a free TX FIFOQ buffer.
           *
           * REVISIT: TX dedicated buffers are not supported.
           */

          mcan_buffer_release(priv);
          handled = true;

#ifdef CONFIG_CAN_TXREADY
          /* Inform the upper half driver that we are again ready to accept
           * data in mcan_send().
           */

          can_txready(dev);
#endif
        }
      else if ((pending & priv->txints) != 0)
        {
          /* Clear unhandled TX events */

          mcan_putreg(priv, SAM_MCAN_IR_OFFSET, priv->txints);
          handled = true;
        }

#ifndef DEDICATED_BUFFERS_NOT_USED
      /* Check if a message has been saved to the dedicated RX buffer (DRX) */

      if ((pending & MCAN_INT_DRX) != 0)
        {
          int i;

          /* Clear the pending DRX interrupt */

          mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_INT_DRX);

          /* Process each dedicated RX buffer */

          for (i = 0; i < priv->config->nrxdedicated; i++)
            {
              uint32_t *rxdedicated = &priv->config->rxdedicated[i];

              /* Check if data is available in this dedicated RX buffer */

              if (mcan_dedicated_rxbuffer_available(priv, i))
                {
                  /* Yes.. Invalidate the D-Cache to that data will be re-
                   * fetched from RAM.
                   *
                   * REVISIT:  This will require 32-byte alignment.
                   */

                  arch_invalidata_dcache();
                  mcan_receive(priv, rxdedicated,
                               priv->config->rxbufferesize);

                  /* Clear the new data flag for the buffer */

                  if (i < 32)
                    {
                      sam_putreg(priv, SAM_MCAN_NDAT1_OFFSET,
                                 (1 << i);
                    }
                  else
                    {
                      sam_putreg(priv, SAM_MCAN_NDAT1_OFFSET,
                                 (1 << (i - 32));
                    }
                }
            }

          handled = true;
        }
#endif

      /* Clear the RX FIFO1 new message interrupt */

      mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_INT_RF1N);
      pending &= ~MCAN_INT_RF1N;

      /* We treat RX FIFO1 as the "high priority" queue:  We will process
       * all messages in RX FIFO1 before processing any message from RX
       * FIFO0.
       */

      for (; ; )
        {
          /* Check if there is anything in RX FIFO1 */

          regval = mcan_getreg(priv, SAM_MCAN_RXF1S_OFFSET);
          nelem  = (regval & MCAN_RXF1S_F1FL_MASK) >> MCAN_RXF1S_F1FL_SHIFT;
          if (nelem == 0)
            {
              /* Break out of the loop if RX FIFO1 is empty */

              break;
            }

          /* Clear the RX FIFO1 interrupt (and all other FIFO1-related
           * interrupts)
           */

          /* Handle the newly received message in FIFO1 */

          ndx = (regval & MCAN_RXF1S_F1GI_MASK) >> MCAN_RXF1S_F1GI_SHIFT;

          if ((regval & MCAN_RXF1S_RF1L) != 0)
            {
              canerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
            }
          else
            {
              mcan_receive(dev,
                           priv->config->msgram.rxfifo1 +
                             (ndx * priv->config->rxfifo1esize),
                           priv->config->rxfifo1esize);

              /* Turning back on all configured RX error interrupts */

              ie |= (priv->rxints & MCAN_RXERR_INTS);
              mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie);
            }

          /* Acknowledge reading the FIFO entry */

          mcan_putreg(priv, SAM_MCAN_RXF1A_OFFSET, ndx);
          handled = true;
        }

      /* Check for successful reception of a new message in RX FIFO0 */

      /* Clear the RX FIFO0 new message interrupt */

      mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_INT_RF0N);
      pending &= ~MCAN_INT_RF0N;

      /* Check if there is anything in RX FIFO0 */

      regval = mcan_getreg(priv, SAM_MCAN_RXF0S_OFFSET);
      nelem  = (regval & MCAN_RXF0S_F0FL_MASK) >> MCAN_RXF0S_F0FL_SHIFT;
      if (nelem > 0)
        {
          /* Handle the newly received message in FIFO0 */

          ndx = (regval & MCAN_RXF0S_F0GI_MASK) >> MCAN_RXF0S_F0GI_SHIFT;

          if ((regval & MCAN_RXF0S_RF0L) != 0)
            {
              canerr("ERROR: Message lost: %08" PRIx32 "\n", regval);
            }
          else
            {
              mcan_receive(dev,
                           priv->config->msgram.rxfifo0 +
                             (ndx * priv->config->rxfifo0esize),
                           priv->config->rxfifo0esize);

              /* Turning back on all configured RX error interrupts */

              ie |= (priv->rxints & MCAN_RXERR_INTS);
              mcan_putreg(priv, SAM_MCAN_IE_OFFSET, ie);
            }

          /* Acknowledge reading the FIFO entry */

          mcan_putreg(priv, SAM_MCAN_RXF0A_OFFSET, ndx);
          handled = true;
        }

      /* Clear unhandled RX interrupts */

      if ((pending & priv->rxints) != 0)
        {
          mcan_putreg(priv, SAM_MCAN_IR_OFFSET, priv->rxints);
        }
    }
  while (handled);

  return OK;
}

/****************************************************************************
 * Name: mcan_hw_initialize
 *
 * Description:
 *   MCAN hardware initialization
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this MCAN peripheral
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int mcan_hw_initialize(struct sam_mcan_s *priv)
{
  const struct sam_config_s *config = priv->config;
  uint32_t *msgram;
  uint32_t regval;
  uint32_t cntr;
  uint32_t cmr;

  caninfo("MCAN%d\n", config->port);
  caninfo("mcan%d_msgram is at %" PRIx32 "\n", config->port,
          (uint32_t) &config->msgram);

  /* Configure MCAN pins */

  sam_configpio(config->rxpinset);
  sam_configpio(config->txpinset);

  /* Enable peripheral clocking */

  sam_enableperiph1(config->pid);

  /* Enable the Initialization state */

  regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  regval |= MCAN_CCCR_INIT;
  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  /* Wait for initialization mode to take effect */

  while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) & MCAN_CCCR_INIT) == 0);

  /* Enable writing to configuration registers */

  regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  regval |= (MCAN_CCCR_INIT | MCAN_CCCR_CCE);
  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  /* Global Filter Configuration:
   *
   *   ANFS=0: Store all non matching standard frame in RX FIFO0
   *   ANFE=0: Store all non matching extended frame in RX FIFO0
   */

  regval = MCAN_GFC_ANFE_RX_FIFO0 | MCAN_GFC_ANFS_RX_FIFO0;
  mcan_putreg(priv, SAM_MCAN_GFC_OFFSET, regval);

  /* Extended ID Filter AND mask  */

  mcan_putreg(priv, SAM_MCAN_XIDAM_OFFSET, 0x1fffffff);

  /* Disable all interrupts  */

  mcan_putreg(priv, SAM_MCAN_IE_OFFSET, 0);
  mcan_putreg(priv, SAM_MCAN_TXBTIE_OFFSET, 0);

  /* All interrupts directed to Line 0.  But disable both interrupt lines 0
   * and 1 for now.
   *
   * REVISIT: Only interrupt line 0 is used by this driver.
   */

  mcan_putreg(priv, SAM_MCAN_ILS_OFFSET, 0);
  mcan_putreg(priv, SAM_MCAN_ILE_OFFSET, 0);

  /* Clear all pending interrupts. */

  mcan_putreg(priv, SAM_MCAN_IR_OFFSET, MCAN_INT_ALL);

  /* Configure MCAN bit timing */

  mcan_putreg(priv, SAM_MCAN_BTP_OFFSET, priv->btp);
  mcan_putreg(priv, SAM_MCAN_FBTP_OFFSET, priv->fbtp);

  /* Configure message RAM starting addresses and sizes. */

  regval = MAILBOX_ADDRESS(config->msgram.stdfilters) |
           MCAN_SIDFC_LSS(config->nstdfilters);
  mcan_putreg(priv, SAM_MCAN_SIDFC_OFFSET, regval);

  regval = MAILBOX_ADDRESS(config->msgram.extfilters) |
           MCAN_XIDFC_LSE(config->nextfilters);
  mcan_putreg(priv, SAM_MCAN_XIDFC_OFFSET, regval);

  /* Configure RX FIFOs */

  regval = MAILBOX_ADDRESS(config->msgram.rxfifo0) |
           MCAN_RXF0C_F0S(config->nrxfifo0);
  mcan_putreg(priv, SAM_MCAN_RXF0C_OFFSET, regval);

  regval = MAILBOX_ADDRESS(config->msgram.rxfifo1) |
           MCAN_RXF1C_F1S(config->nrxfifo1);
  mcan_putreg(priv, SAM_MCAN_RXF1C_OFFSET, regval);

  /* Watermark interrupt off, blocking mode */

  regval = MAILBOX_ADDRESS(config->msgram.rxdedicated);
  mcan_putreg(priv, SAM_MCAN_RXBC_OFFSET, regval);

  regval = MCAN_RXESC_RBDS(config->rxbufferecode) |
           MCAN_RXESC_F1DS(config->rxfifo1ecode) |
           MCAN_RXESC_F0DS(config->rxfifo0ecode);
  mcan_putreg(priv, SAM_MCAN_RXESC_OFFSET, regval);

  /* Configure TX FIFOs */

  regval = MAILBOX_ADDRESS(config->msgram.txeventfifo) |
           MCAN_TXEFC_EFS(config->ntxeventfifo);
  mcan_putreg(priv, SAM_MCAN_TXEFC_OFFSET, regval);

  regval = MAILBOX_ADDRESS(config->msgram.txdedicated) |
           MCAN_TXBC_NDTB(config->ntxdedicated) |
           MCAN_TXBC_TFQS(config->ntxfifoq);
  mcan_putreg(priv, SAM_MCAN_TXBC_OFFSET, regval);

  regval = MCAN_TXESC_TBDS(config->txbufferecode);
  mcan_putreg(priv, SAM_MCAN_TXESC_OFFSET, regval);

  /* Configure Message Filters */

  /* Disable all standard filters */

  msgram = config->msgram.stdfilters;
  cntr   = config->nstdfilters;
  while (cntr > 0)
    {
      *msgram++ = STDFILTER_S0_SFEC_DISABLE;
      cntr--;
    }

  /* Disable all extended filters */

  msgram = config->msgram.extfilters;
  cntr = config->nextfilters;
  while (cntr > 0)
    {
      *msgram = EXTFILTER_F0_EFEC_DISABLE;
      msgram = msgram + 2;
      cntr--;
    }

  /* Clear new RX data flags */

  mcan_putreg(priv, SAM_MCAN_NDAT1_OFFSET, 0xffffffff);
  mcan_putreg(priv, SAM_MCAN_NDAT2_OFFSET, 0xffffffff);

  /* Select ISO11898-1 mode or FD mode with or without fast bit rate
   * switching
   */

  regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  regval &= ~(MCAN_CCCR_CME_MASK | MCAN_CCCR_CMR_MASK);

  switch (config->mode)
    {
    default:
    case MCAN_ISO11898_1_MODE:
      regval |= MCAN_CCCR_CME_ISO11898_1;
      cmr     = MCAN_CCCR_CMR_ISO11898_1;
      break;

#ifdef CONFIG_CAN_FD
    case MCAN_FD_MODE:
      regval |= MCAN_CCCR_CME_FD;
      cmr     = MCAN_CCCR_CMR_FD;
      break;

    case MCAN_FD_BSW_MODE:
      regval |= MCAN_CCCR_CME_FD_BSW;
      cmr     = MCAN_CCCR_CMR_FD_BSW;
      break;
#endif
    }

  /* Set the initial CAN mode */

  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  /* Request the mode change */

  regval |= cmr;
  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

  /* Wait for the mode to take effect */

  while ((mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET) &
         (MCAN_CCCR_FDBS | MCAN_CCCR_FDO)) != 0);

  /* Enable FIFO/Queue mode
   *
   * REVISIT: Dedicated TX buffers are not used.
   */

  regval  = mcan_getreg(priv, SAM_MCAN_TXBC_OFFSET);
#ifdef CONFIG_SAMA5_MCAN_QUEUE_MODE
  regval |= MCAN_TXBC_TFQM;
#else
  regval &= ~MCAN_TXBC_TFQM;
#endif
  mcan_putreg(priv, SAM_MCAN_TXBC_OFFSET, regval);

#ifdef SAMA5_MCAN_LOOPBACK
  /* Is loopback mode selected for this peripheral? */

  if (config->loopback)
    {
     /* MCAN_CCCR_TEST  - Test mode enable
      * MCAN_CCCR_MON   - Bus monitoring mode (for internal loopback)
      * MCAN_TEST_LBCK  - Loopback mode
      */

      regval = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
      regval |= (MCAN_CCCR_TEST | MCAN_CCCR_MON);
      mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);

      regval = mcan_getreg(priv, SAM_MCAN_TEST_OFFSET);
      regval |= MCAN_TEST_LBCK;
      mcan_putreg(priv, SAM_MCAN_TEST_OFFSET, regval);
    }
#endif

  /* Configure interrupt lines */

  /* Select RX-related interrupts */

#ifndef DEDICATED_BUFFERS_NOT_USED
  priv->rxints = MCAN_RXDEDBUF_INTS;
#else
  priv->rxints = MCAN_RXFIFO_INTS;
#endif

  /* Select TX-related interrupts */

#ifndef DEDICATED_BUFFERS_NOT_USED
  priv->txints = MCAN_TXDEDBUF_INTS;
#else
  priv->txints = MCAN_TXFIFOQ_INTS;
#endif

  /* Direct all interrupts to Line 0.
   *
   * Bits in the ILS register correspond to each MCAN interrupt; A bit
   * set to '1' is directed to interrupt line 1; a bit cleared to '0'
   * is directed interrupt line 0.
   *
   * REVISIT: Nothing is done here.  Only interrupt line 0 is used by
   * this driver and ILS was already cleared above.
   */

  /* Enable only interrupt line 0. */

  mcan_putreg(priv, SAM_MCAN_ILE_OFFSET, MCAN_ILE_EINT0);

  /* Disable initialization mode to enable normal operation */

  regval  = mcan_getreg(priv, SAM_MCAN_CCCR_OFFSET);
  regval &= ~MCAN_CCCR_INIT;
  mcan_putreg(priv, SAM_MCAN_CCCR_OFFSET, regval);
  return OK;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: sam_mcan_initialize
 *
 * Description:
 *   Initialize the selected MCAN port
 *
 * Input Parameters:
 *   port - Port number (for hardware that has multiple MCAN interfaces),
 *          0=MCAN0, 1=MCAN1
 *
 * Returned Value:
 *   Valid CAN device structure reference on success; a NULL on failure
 *
 ****************************************************************************/

struct can_dev_s *sam_mcan_initialize(int port)
{
  struct can_dev_s *dev;
  struct sam_mcan_s *priv;
  const struct sam_config_s *config;
  uint32_t regval;

  caninfo("MCAN%d, clk freq=%d\n", port, SAMA5_MCANCLK_FREQUENCY);

  /* Select clock source and pre-scaler value.  */

  regval = PMC_PCR_GCKEN | PMC_PCR_CMD | PMC_PCR_EN |
           PMC_PCR_GCKDIV(CONFIG_SAMA5_MCAN_CLKSRC_PRESCALER - 1) |
           SAMA5_MCAN_CLKSRC;

  switch (port)
    {
#ifdef CONFIG_SAMA5_MCAN0
      case MCAN0:

        /* Enable CLK */

        regval |= PMC_PCR_PID(SAM_PID_MCAN0);
        putreg32(regval, SAM_PMC_PCR);
        sam_mcan0_enableclk();

        /* Select the MCAN0 device structure */

        dev    = &g_mcan0dev;
        priv   = &g_mcan0priv;
        config = &g_mcan0const;

        /* Configure MCAN0 Message RAM Base Address */

        regval  = getreg32(SAM_SFR_VBASE + SAM_SFR_CAN_OFFSET) &
                           SAM_MCAN0_SFR_MASK;
        regval |= SAM_MCAN_SFR_GET_MSB(config->msgram.stdfilters);

        putreg32(regval, SAM_SFR_VBASE + SAM_SFR_CAN_OFFSET);
        break;
#endif
#ifdef CONFIG_SAMA5_MCAN1
      case MCAN1:

        /* Enable CLK */

        regval |= PMC_PCR_PID(SAM_PID_MCAN1);
        putreg32(regval, SAM_PMC_PCR);
        sam_mcan1_enableclk();

        /* Select the MCAN1 device structure */

        dev    = &g_mcan1dev;
        priv   = &g_mcan1priv;
        config = &g_mcan1const;

        /* Configure MCAN1 Message RAM Base Address */

        regval = getreg32(SAM_SFR_VBASE + SAM_SFR_CAN_OFFSET) &
                          SAM_MCAN1_SFR_MASK;
        regval |= SAM_MCAN_SFR_GET_MSB(config->msgram.stdfilters) <<
                                       SAM_MCAN_SFR_SHIFT;

        putreg32(regval, SAM_SFR_VBASE + SAM_SFR_CAN_OFFSET);
        break;
#endif
      default:
        canerr("ERROR: Unsupported mcan port %d\n", port);
        return NULL;
        break;
    }

  /* Is this the first time that we have handed out this device? */

  if (priv->state == MCAN_STATE_UNINIT)
    {
      /* Yes, then perform one time data initialization */

      /* Set the initial bit timing.  This might change subsequently
       * due to IOCTL command processing.
       */

      priv->btp    = priv->config->btp;
      priv->fbtp   = priv->config->fbtp;

      /* And put the hardware in the initial state */

      mcan_reset(dev);
    }

  return dev;
}

#endif /* CONFIG_CAN && CONFIG_SAMA5_MCAN */