/****************************************************************************
 * arch/arm/src/at32/at32_can.c
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.  The
 * ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the
 * License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 ****************************************************************************/

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

#include <nuttx/config.h>

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

#include <arch/board/board.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/can/can.h>

#include "arm_internal.h"
#include "chip.h"
#include "at32.h"
#include "at32_rcc.h"
#include "at32_can.h"

#if defined(CONFIG_CAN) && \
    (defined(CONFIG_AT32_CAN1) || defined(CONFIG_AT32_CAN2))

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

/* Delays *******************************************************************/

/* Time out for INAK bit */

#define INAK_TIMEOUT 65535

/* Bit timing ***************************************************************/

#define CAN_BIT_QUANTA (CONFIG_AT32_CAN_TSEG1 + CONFIG_AT32_CAN_TSEG2 + 1)

#ifndef CONFIG_DEBUG_CAN_INFO
#  undef CONFIG_AT32_CAN_REGDEBUG
#endif

/* CAN error interrupts */

#ifdef CONFIG_CAN_ERRORS
#  define AT32_CAN_ERRINT (CAN_INTEN_ETRIEN | CAN_INTEN_EOIEN |             \
                            CAN_INTEN_BOIEN | CAN_INTEN_EPIEN |             \
                            CAN_INTEN_EAIEN)
#endif

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

struct at32_can_s
{
  uint8_t  port;     /* CAN port number (1 or 2) */
  uint8_t  canrx[2]; /* CAN RX FIFO 0/1 IRQ number */
  uint8_t  cantx;    /* CAN TX IRQ number */
#ifdef CONFIG_CAN_ERRORS
  uint8_t  cansce;   /* CAN SCE IRQ number */
#endif
  uint8_t  filter;   /* Filter number */
  uint32_t base;     /* Base address of the CAN control registers */
  uint32_t fbase;    /* Base address of the CAN filter registers */
  uint32_t baud;     /* Configured baud */
};

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

/* CAN Register access */

static uint32_t at32can_getreg(struct at32_can_s *priv,
                                int offset);
static uint32_t at32can_getfreg(struct at32_can_s *priv,
                                 int offset);
static void at32can_putreg(struct at32_can_s *priv, int offset,
                            uint32_t value);
static void at32can_putfreg(struct at32_can_s *priv, int offset,
                             uint32_t value);
#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpctrlregs(struct at32_can_s *priv,
                                  const char *msg);
static void at32can_dumpmbregs(struct at32_can_s *priv,
                                const char *msg);
static void at32can_dumpfiltregs(struct at32_can_s *priv,
                                  const char *msg);
#else
#  define at32can_dumpctrlregs(priv,msg)
#  define at32can_dumpmbregs(priv,msg)
#  define at32can_dumpfiltregs(priv,msg)
#endif

/* Filtering (todo) */

#ifdef CONFIG_CAN_EXTID
static int  at32can_addextfilter(struct at32_can_s *priv,
                                  struct canioc_extfilter_s *arg);
static int  at32can_delextfilter(struct at32_can_s *priv,
                                  int arg);
#endif
static int  at32can_addstdfilter(struct at32_can_s *priv,
                                  struct canioc_stdfilter_s *arg);
static int  at32can_delstdfilter(struct at32_can_s *priv,
                                  int arg);

/* CAN driver methods */

static void at32can_reset(struct can_dev_s *dev);
static int  at32can_setup(struct can_dev_s *dev);
static void at32can_shutdown(struct can_dev_s *dev);
static void at32can_rxint(struct can_dev_s *dev, bool enable);
static void at32can_txint(struct can_dev_s *dev, bool enable);
static int  at32can_ioctl(struct can_dev_s *dev, int cmd,
                           unsigned long arg);
static int  at32can_remoterequest(struct can_dev_s *dev,
                                   uint16_t id);
static int  at32can_send(struct can_dev_s *dev,
                          struct can_msg_s *msg);
static bool at32can_txready(struct can_dev_s *dev);
static bool at32can_txempty(struct can_dev_s *dev);

#ifdef CONFIG_CAN_ERRORS
static void at32can_errint(struct can_dev_s *dev, bool enable);
#endif

/* CAN interrupt handling */

static int  at32can_rxinterrupt(struct can_dev_s *dev, int rxmb);
static int  at32can_rx0interrupt(int irq, void *context, void *arg);
static int  at32can_rx1interrupt(int irq, void *context, void *arg);
static int  at32can_txinterrupt(int irq, void *context, void *arg);
#ifdef CONFIG_CAN_ERRORS
static int  at32can_sceinterrupt(int irq, void *context, void *arg);
#endif

/* Initialization */

static int  at32can_enterinitmode(struct at32_can_s *priv);
static int  at32can_exitinitmode(struct at32_can_s *priv);
static int  at32can_bittiming(struct at32_can_s *priv);
static int  at32can_cellinit(struct at32_can_s *priv);
static int  at32can_filterinit(struct at32_can_s *priv);

/* TX mailbox status */

static bool at32can_txmb0empty(uint32_t tsr_regval);
static bool at32can_txmb1empty(uint32_t tsr_regval);
static bool at32can_txmb2empty(uint32_t tsr_regval);

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

static const struct can_ops_s g_canops =
{
  .co_reset         = at32can_reset,
  .co_setup         = at32can_setup,
  .co_shutdown      = at32can_shutdown,
  .co_rxint         = at32can_rxint,
  .co_txint         = at32can_txint,
  .co_ioctl         = at32can_ioctl,
  .co_remoterequest = at32can_remoterequest,
  .co_send          = at32can_send,
  .co_txready       = at32can_txready,
  .co_txempty       = at32can_txempty,
};

#ifdef CONFIG_AT32_CAN1
static struct at32_can_s g_can1priv =
{
  .port             = 1,
  .canrx            =
  {
                      AT32_IRQ_CAN1RX0,
                      AT32_IRQ_CAN1RX1,
  },
  .cantx            = AT32_IRQ_CAN1TX,
#ifdef CONFIG_CAN_ERRORS
  .cansce           = AT32_IRQ_CAN1SCE,
#endif
  .filter           = 0,
  .base             = AT32_CAN1_BASE,
  .fbase            = AT32_CAN1_BASE,
  .baud             = CONFIG_AT32_CAN1_BAUD,
};

static struct can_dev_s g_can1dev =
{
  .cd_ops           = &g_canops,
  .cd_priv          = &g_can1priv,
};
#endif

#ifdef CONFIG_AT32_CAN2
static struct at32_can_s g_can2priv =
{
  .port             = 2,
  .canrx            =
  {
                      AT32_IRQ_CAN2RX0,
                      AT32_IRQ_CAN2RX1,
  },
  .cantx            = AT32_IRQ_CAN2TX,
#ifdef CONFIG_CAN_ERRORS
  .cansce           = AT32_IRQ_CAN2SCE,
#endif
  .filter           = CAN_NFILTERS / 2,
  .base             = AT32_CAN2_BASE,
  .fbase            = AT32_CAN1_BASE,
  .baud             = CONFIG_AT32_CAN2_BAUD,
};

static struct can_dev_s g_can2dev =
{
  .cd_ops           = &g_canops,
  .cd_priv          = &g_can2priv,
};
#endif

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

/****************************************************************************
 * Name: at32can_getreg
 * Name: at32can_getfreg
 *
 * Description:
 *   Read the value of a CAN register or filter block register.
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

#ifdef CONFIG_AT32_CAN_REGDEBUG
static uint32_t at32can_vgetreg(uint32_t addr)
{
  static uint32_t prevaddr = 0;
  static uint32_t preval   = 0;
  static uint32_t count    = 0;

  /* Read the value from the register */

  uint32_t val = getreg32(addr);

  /* 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 (addr == prevaddr && val == preval)
    {
      if (count == 0xffffffff || ++count > 3)
        {
          if (count == 4)
            {
              caninfo("...\n");
            }

          return val;
        }
    }

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

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

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

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

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

      prevaddr = addr;
      preval   = val;
      count    = 1;
    }

  /* Show the register value read */

  caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);
  return val;
}

static uint32_t at32can_getreg(struct at32_can_s *priv, int offset)
{
  return at32can_vgetreg(priv->base + offset);
}

static uint32_t at32can_getfreg(struct at32_can_s *priv, int offset)
{
  return at32can_vgetreg(priv->fbase + offset);
}

#else
static uint32_t at32can_getreg(struct at32_can_s *priv, int offset)
{
  return getreg32(priv->base + offset);
}

static uint32_t at32can_getfreg(struct at32_can_s *priv, int offset)
{
  return getreg32(priv->fbase + offset);
}

#endif

/****************************************************************************
 * Name: at32can_putreg
 * Name: at32can_putfreg
 *
 * Description:
 *   Set the value of a CAN register or filter block register.
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *   offset - The offset to the register to write
 *   value - The value to write to the register
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_vputreg(uint32_t addr, uint32_t value)
{
  /* Show the register value being written */

  caninfo("%08" PRIx32 "->%08" PRIx32 "\n", addr, val);

  /* Write the value */

  putreg32(value, addr);
}

static void at32can_putreg(struct at32_can_s *priv, int offset,
                            uint32_t value)
{
  at32can_vputreg(priv->base + offset, value);
}

static void at32can_putfreg(struct at32_can_s *priv, int offset,
                             uint32_t value)
{
  at32can_vputreg(priv->fbase + offset, value);
}

#else
static void at32can_putreg(struct at32_can_s *priv, int offset,
                            uint32_t value)
{
  putreg32(value, priv->base + offset);
}

static void at32can_putfreg(struct at32_can_s *priv, int offset,
                             uint32_t value)
{
  putreg32(value, priv->fbase + offset);
}
#endif

/****************************************************************************
 * Name: at32can_dumpctrlregs
 *
 * Description:
 *   Dump the contents of all CAN control registers
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpctrlregs(struct at32_can_s *priv,
                                  const char *msg)
{
  if (msg)
    {
      caninfo("Control Registers: %s\n", msg);
    }
  else
    {
      caninfo("Control Registers:\n");
    }

  /* CAN control and status registers */

  caninfo("  MCTRL: %08" PRIx32 "   MSTS: %08" PRIx32 "   \
  TSTS: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_MCTRL_OFFSET),
          getreg32(priv->base + AT32_CAN_MSTS_OFFSET),
          getreg32(priv->base + AT32_CAN_TSTS_OFFSET));

  caninfo(" RF0: %08" PRIx32 "  RF1: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_RF0_OFFSET),
          getreg32(priv->base + AT32_CAN_RF1_OFFSET));

  caninfo("  INTEN: %08" PRIx32 "   ESTS: %08" PRIx32 "   \
  BTMG: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_INTEN_OFFSET),
          getreg32(priv->base + AT32_CAN_ESTS_OFFSET),
          getreg32(priv->base + AT32_CAN_BTMG_OFFSET));
}
#endif

/****************************************************************************
 * Name: at32can_dumpmbregs
 *
 * Description:
 *   Dump the contents of all CAN mailbox registers
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpmbregs(struct at32_can_s *priv,
                                const char *msg)
{
  if (msg)
    {
      caninfo("Mailbox Registers: %s\n", msg);
    }
  else
    {
      caninfo("Mailbox Registers:\n");
    }

  /* CAN mailbox registers (3 TX and 2 RX) */

  caninfo(" TMI0: %08" PRIx32 " TMC0: %08" PRIx32 " TMDTL0: %08"
          PRIx32 " TMDTH0: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_TMI0_FFSET),
          getreg32(priv->base + AT32_CAN_TMC0_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTL0_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTH0_OFFSET));

  caninfo(" TMI1: %08" PRIx32 " TMC1: %08" PRIx32 " TMDTL1: %08"
          PRIx32 " TMDTH1: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_TMI1_FFSET),
          getreg32(priv->base + AT32_CAN_TMC1_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTL1_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTH1_OFFSET));

  caninfo(" TMI2: %08" PRIx32 " TMC2: %08" PRIx32 " TMDTL2: %08"
          PRIx32 " TMDTH2: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_TMI2_FFSET),
          getreg32(priv->base + AT32_CAN_TMC2_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTL2_OFFSET),
          getreg32(priv->base + AT32_CAN_TMDTH2_OFFSET));

  caninfo(" RFI0: %08" PRIx32 " RFC0: %08" PRIx32 " RFDTL0: %08"
          PRIx32 " RFDTH0: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_RFI0_OFFSET),
          getreg32(priv->base + AT32_CAN_RFC0_OFFSET),
          getreg32(priv->base + AT32_CAN_RFDTL0_OFFSET),
          getreg32(priv->base + AT32_CAN_RFDTH0_OFFSET));

  caninfo(" RFI1: %08" PRIx32 " RFC1: %08" PRIx32 " RFDTL1: %08"
          PRIx32 " RFDTH1: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_RFI1_OFFSET),
          getreg32(priv->base + AT32_CAN_RFC1_OFFSET),
          getreg32(priv->base + AT32_CAN_RFDTL1_OFFSET),
          getreg32(priv->base + AT32_CAN_RFDTH1_OFFSET));
}
#endif

/****************************************************************************
 * Name: at32can_dumpfiltregs
 *
 * Description:
 *   Dump the contents of all CAN filter registers
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_AT32_CAN_REGDEBUG
static void at32can_dumpfiltregs(struct at32_can_s *priv,
                                  const char *msg)
{
  int i;

  if (msg)
    {
      caninfo("Filter Registers: %s\n", msg);
    }
  else
    {
      caninfo("Filter Registers:\n");
    }

  caninfo(" FCTRL: %08" PRIx32 "   FMCFG: %08" PRIx32 "  FSCFG: %08"
          PRIx32 " FRF: %08" PRIx32 "  FACFG: %08" PRIx32 "\n",
          getreg32(priv->base + AT32_CAN_FCTRL_OFFSET),
          getreg32(priv->base + AT32_CAN_FMCFG_OFFSET),
          getreg32(priv->base + AT32_CAN_FSCFG_OFFSET),
          getreg32(priv->base + AT32_CAN_FRF_OFFSET),
          getreg32(priv->base + AT32_CAN_FACFG_OFFSET));

  for (i = 0; i < CAN_NFILTERS; i++)
    {
      caninfo(" F%dR1: %08" PRIx32 " F%dR2: %08" PRIx32 "\n",
              i, getreg32(priv->base + AT32_CAN_FBF_OFFSET(i, 1)),
              i, getreg32(priv->base + AT32_CAN_FBF_OFFSET(i, 2)));
    }
}
#endif

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

static void at32can_reset(struct can_dev_s *dev)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval;
  uint32_t regbit = 0;
  irqstate_t flags;

  caninfo("CAN%" PRIu8 "\n", priv->port);

  /* Get the bits in the AHB1RSTR register needed to reset this CAN device */

#ifdef CONFIG_AT32_CAN1
  if (priv->port == 1)
    {
      regbit = CRM_APB1RST_CAN1RST;
    }
  else
#endif
#ifdef CONFIG_AT32_CAN2
  if (priv->port == 2)
    {
      regbit = CRM_APB1RST_CAN1RST;
    }
  else
#endif
    {
      canerr("ERROR: Unsupported port %d\n", priv->port);
      return;
    }

  /* Disable interrupts momentarily to stop any ongoing CAN event processing
   * and to prevent any concurrent access to the AHB1RSTR register.
   */

  flags = enter_critical_section();

  /* Reset the CAN */

  regval  = getreg32(AT32_CRM_APB1RST);
  regval |= regbit;
  putreg32(regval, AT32_CRM_APB1RST);

  regval &= ~regbit;
  putreg32(regval, AT32_CRM_APB1RST);
  leave_critical_section(flags);
}

/****************************************************************************
 * Name: at32can_setup
 *
 * Description:
 *   Configure the CAN. This method is called the first time that the CAN
 *   device is opened.  This will occur when the port is first opened.
 *   This setup includes configuring and attaching CAN interrupts.
 *   All CAN 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 at32can_setup(struct can_dev_s *dev)
{
  struct at32_can_s *priv = dev->cd_priv;
  int ret;

#ifdef CONFIG_CAN_ERRORS
  ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
        " TX irq: %" PRIu8 " SCE irq: %" PRIu8 "\n",
        priv->port, priv->canrx[0], priv->canrx[1], priv->cantx,
        priv->cansce);
#else
  ninfo("CAN%" PRIu8 " RX0 irq: %" PRIu8 " RX1 irq: %" PRIu8
        " TX irq: %" PRIu8 "\n",
        priv->port, priv->canrx[0], priv->canrx[1], priv->cantx);
#endif

  /* CAN cell initialization */

  ret = at32can_cellinit(priv);
  if (ret < 0)
    {
      canerr("ERROR: CAN%" PRId8 " cell initialization failed: %d\n",
             priv->port, ret);
      return ret;
    }

  at32can_dumpctrlregs(priv, "After cell initialization");
  at32can_dumpmbregs(priv, NULL);

  /* CAN filter initialization */

  ret = at32can_filterinit(priv);
  if (ret < 0)
    {
      canerr("ERROR: CAN%" PRIu8 " filter initialization failed: %d\n",
             priv->port, ret);
      return ret;
    }

  at32can_dumpfiltregs(priv, "After filter initialization");

  /* Attach the CAN RX FIFO 0/1 interrupts and TX interrupts.
   * The others are not used.
   */

  ret = irq_attach(priv->canrx[0], at32can_rx0interrupt, dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach CAN%" PRIu8 " RX0 IRQ (%" PRIu8 ")",
             priv->port, priv->canrx[0]);
      return ret;
    }

  ret = irq_attach(priv->canrx[1], at32can_rx1interrupt, dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach CAN%" PRIu8 " RX1 IRQ (%" PRIu8 ")",
             priv->port, priv->canrx[1]);
      return ret;
    }

  ret = irq_attach(priv->cantx, at32can_txinterrupt, dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach CAN%" PRIu8 " TX IRQ (%" PRIu8 ")",
             priv->port, priv->cantx);
      return ret;
    }

#ifdef CONFIG_CAN_ERRORS
  ret = irq_attach(priv->cansce, at32can_sceinterrupt, dev);
  if (ret < 0)
    {
      nerr("ERROR: Failed to attach CAN%" PRIu8 " SCE IRQ (%" PRIu8 ")",
           priv->port, priv->cansce);
      return ret;
    }

  /* Enable CAN error interrupts */

  at32can_errint(dev, true);
#endif

  /* Enable the interrupts at the NVIC.  Interrupts are still disabled in
   * the CAN module.  Since we coming out of reset here, there should be
   * no pending interrupts.
   */

  up_enable_irq(priv->canrx[0]);
  up_enable_irq(priv->canrx[1]);
  up_enable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
  up_enable_irq(priv->cansce);
#endif
  return OK;
}

/****************************************************************************
 * Name: at32can_shutdown
 *
 * Description:
 *   Disable the CAN.  This method is called when the CAN 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 at32can_shutdown(struct can_dev_s *dev)
{
  struct at32_can_s *priv = dev->cd_priv;

  caninfo("CAN%" PRIu8 "\n", priv->port);

  /* Disable the RX FIFO 0/1, TX and SCE interrupts */

  up_disable_irq(priv->canrx[0]);
  up_disable_irq(priv->canrx[1]);
  up_disable_irq(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
  up_disable_irq(priv->cansce);
#endif

  /* Detach the RX FIFO 0/1, TX and SCE interrupts */

  irq_detach(priv->canrx[0]);
  irq_detach(priv->canrx[1]);
  irq_detach(priv->cantx);
#ifdef CONFIG_CAN_ERRORS
  irq_detach(priv->cansce);
#endif

  /* And reset the hardware */

  at32can_reset(dev);
}

/****************************************************************************
 * Name: at32can_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 at32can_rxint(struct can_dev_s *dev, bool enable)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval;

  caninfo("CAN%" PRIu8 " rxint enable: %d\n", priv->port, enable);

  /* Enable/disable the FIFO 0/1 message pending interrupt */

  regval = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
  if (enable)
    {
      regval |= CAN_INTEN_RF0MIEN | CAN_INTEN_RF1MIEN;
    }
  else
    {
      regval &= ~(CAN_INTEN_RF0MIEN | CAN_INTEN_RF1MIEN);
    }

  at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
}

/****************************************************************************
 * Name: at32can_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 at32can_txint(struct can_dev_s *dev, bool enable)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval;

  caninfo("CAN%" PRIu8 " txint enable: %d\n", priv->port, enable);

  /* Support only disabling the transmit mailbox interrupt */

  if (!enable)
    {
      regval  = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
      regval &= ~CAN_INTEN_TCIEN;
      at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
    }
}

#ifdef CONFIG_CAN_ERRORS
/****************************************************************************
 * Name: at32can_errint
 *
 * Description:
 *   Call to enable or disable CAN error interrupts.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void at32can_errint(struct can_dev_s *dev, bool enable)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval = 0;

  caninfo("CAN%" PRIu8 " errint enable: %d\n", priv->port, enable);

  /* Enable/disable the transmit mailbox interrupt */

  regval  = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
  if (enable)
    {
      regval |= AT32_CAN_ERRINT;
    }
  else
    {
      regval &= ~AT32_CAN_ERRINT;
    }

  at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);
}
#endif

/****************************************************************************
 * Name: at32can_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 at32can_ioctl(struct can_dev_s *dev, int cmd,
                          unsigned long arg)
{
  struct at32_can_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);
          regval       = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);
          bt->bt_sjw   = ((regval & CAN_BTMG_RSAW_MASK) >>
                          CAN_BTMG_RSAW_SHIFT) + 1;
          bt->bt_tseg1 = ((regval & CAN_BTMG_BTS1_MASK) >>
                          CAN_BTMG_BTS1_SHIFT) + 1;
          bt->bt_tseg2 = ((regval & CAN_BTMG_BTS2_MASK) >>
                          CAN_BTMG_BTS2_SHIFT) + 1;

          brp          = ((regval & CAN_BTMG_BRDIV_MASK) >>
                          CAN_BTMG_BRDIV_SHIFT) + 1;
          bt->bt_baud  = AT32_PCLK1_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 thenature 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;
          uint32_t brp;
          uint32_t can_bit_quanta;
          uint32_t tmp;
          uint32_t regval;

          DEBUGASSERT(bt != NULL);
          DEBUGASSERT(bt->bt_baud < AT32_PCLK1_FREQUENCY);
          DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 4);
          DEBUGASSERT(bt->bt_tseg1 > 0 && bt->bt_tseg1 <= 16);
          DEBUGASSERT(bt->bt_tseg2 > 0 && bt->bt_tseg2 <=  8);

          regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);

          /* Extract bit timing data
           * tmp is in clocks per bit time
           */

          tmp = AT32_PCLK1_FREQUENCY / bt->bt_baud;

          /* This value is dynamic as requested by user */

          can_bit_quanta = bt->bt_tseg1 + bt->bt_tseg2 + 1;

          if (tmp < can_bit_quanta)
            {
              /* This timing is not possible */

              ret = -EINVAL;
              break;
            }

          /* Otherwise, nquanta is can_bit_quanta, ts1 and ts2 are
           * provided by the user and we calculate brp to achieve
           * can_bit_quanta quanta in the bit times
           */

          else
            {
              brp = (tmp + (can_bit_quanta / 2)) / can_bit_quanta;
              DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
            }

          caninfo("TS1: %"PRIu8 " TS2: %" PRIu8 " BRP: %" PRIu32 "\n",
                  bt->bt_tseg1, bt->bt_tseg2, brp);

          /* Configure bit timing. */

          regval &= ~(CAN_BTMG_BRDIV_MASK | CAN_BTMG_BTS1_MASK |
                      CAN_BTMG_BTS2_MASK | CAN_BTMG_RSAW_MASK);
          regval |= ((brp          - 1) << CAN_BTMG_BRDIV_SHIFT) |
                    ((bt->bt_tseg1 - 1) << CAN_BTMG_BTS1_SHIFT) |
                    ((bt->bt_tseg2 - 1) << CAN_BTMG_BTS2_SHIFT) |
                    ((bt->bt_sjw   - 1) << CAN_BTMG_RSAW_SHIFT);

          /* Bit timing can only be configured in init mode. */

          ret = at32can_enterinitmode(priv);
          if (ret < 0)
            {
              break;
            }

          at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, regval);

          ret = at32can_exitinitmode(priv);
          if (ret >= 0)
            {
              priv->baud  = AT32_PCLK1_FREQUENCY /
                (brp * (bt->bt_tseg1 + bt->bt_tseg2 + 1));
            }
        }
        break;

      /* CANIOC_GET_CONNMODES:
       *   Description:    Get the current bus connection modes
       *   Argument:       A pointer to a write-able instance of struct
       *                   canioc_connmodes_s in which the new bus modes 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_CONNMODES:
        {
          struct canioc_connmodes_s *bm =
            (struct canioc_connmodes_s *)arg;
          uint32_t regval;

          DEBUGASSERT(bm != NULL);

          regval          = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);

          bm->bm_loopback = ((regval & CAN_BTMG_LBEN) == CAN_BTMG_LBEN);
          bm->bm_silent   = ((regval & CAN_BTMG_LOEN) == CAN_BTMG_LOEN);
          ret = OK;
          break;
        }

      /* CANIOC_SET_CONNMODES:
       *   Description:    Set new bus connection modes values
       *   Argument:       A pointer to a read-able instance of struct
       *                   canioc_connmodes_s in which the new bus modes
       *                   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
       */

      case CANIOC_SET_CONNMODES:
        {
          struct canioc_connmodes_s *bm =
            (struct canioc_connmodes_s *)arg;
          uint32_t regval;

          DEBUGASSERT(bm != NULL);

          regval = at32can_getreg(priv, AT32_CAN_BTMG_OFFSET);

          if (bm->bm_loopback)
            {
              regval |= CAN_BTMG_LBEN;
            }
          else
            {
              regval &= ~CAN_BTMG_LBEN;
            }

          if (bm->bm_silent)
            {
              regval |= CAN_BTMG_LOEN;
            }
          else
            {
              regval &= ~CAN_BTMG_LOEN;
            }

          /* This register can only be configured in init mode. */

          ret = at32can_enterinitmode(priv);
          if (ret < 0)
            {
              break;
            }

          at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, regval);

          ret = at32can_exitinitmode(priv);
        }
        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 = at32can_addextfilter(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:
        {
#if 0 /* Unimplemented */
          DEBUGASSERT(arg <= priv->config->nextfilters);
#endif
          ret = at32can_delextfilter(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 = at32can_addstdfilter(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:
        {
#if 0 /* Unimplemented */
          DEBUGASSERT(arg <= priv->config->nstdfilters);
#endif
          ret = at32can_delstdfilter(priv, (int)arg);
        }
        break;

      case CANIOC_SET_NART:
        {
          uint32_t regval;

          ret = at32can_enterinitmode(priv);
          if (ret != 0)
            {
              return ret;
            }

          regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
          if (arg == 1)
            {
              regval |= CAN_MCTRL_PRSFEN;
            }
          else
            {
              regval &= ~CAN_MCTRL_PRSFEN;
            }

          at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
          return at32can_exitinitmode(priv);
        }
        break;

      case CANIOC_SET_ABOM:
        {
          uint32_t regval;

          ret = at32can_enterinitmode(priv);
          if (ret != 0)
            {
              return ret;
            }

          regval = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
          if (arg == 1)
            {
              regval |= CAN_MCTRL_AEBOEN;
            }
          else
            {
              regval &= ~CAN_MCTRL_AEBOEN;
            }

          at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);
          return at32can_exitinitmode(priv);
        }
        break;

      /* Unsupported/unrecognized command */

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

  return ret;
}

/****************************************************************************
 * Name: at32can_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 at32can_remoterequest(struct can_dev_s *dev, uint16_t id)
{
#warning "Remote request not implemented"
  return -ENOSYS;
}

/****************************************************************************
 * Name: at32can_send
 *
 * Description:
 *    Send one can message.
 *
 *    One CAN-message consists of a maximum of 10 bytes.  A message is
 *    composed of at least the first 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 at32can_send(struct can_dev_s *dev,
                         struct can_msg_s *msg)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint8_t *ptr;
  uint32_t regval;
  uint32_t tmp;
  int dlc;
  int txmb;

  caninfo("CAN%" PRIu8 " ID: %" PRIu32 " DLC: %" PRIu8 "\n",
          priv->port, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);

  /* Select one empty transmit mailbox */

  regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
  if (at32can_txmb0empty(regval))
    {
      txmb = 0;
    }
  else if (at32can_txmb1empty(regval))
    {
      txmb = 1;
    }
  else if (at32can_txmb2empty(regval))
    {
      txmb = 2;
    }
  else
    {
      canerr("ERROR: No available mailbox\n");
      return -EBUSY;
    }

  /* Clear TXRQ, RTR, IDE, EXID, and STID fields */

  regval  = at32can_getreg(priv, AT32_CAN_TMI_OFFSET(txmb));
  regval &= ~(CAN_TMI_TMSR | CAN_TMI_TMFRSEL | CAN_TMI_TMIDSEL |
              CAN_TMI_TMEID_MASK | CAN_TMI_TMSID_TMEID_MASK);
  at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);

  /* Set up the ID, standard 11-bit or extended 29-bit. */

#ifdef CONFIG_CAN_EXTID
  regval &= ~CAN_TMI_TMEID_MASK;
  if (msg->cm_hdr.ch_extid)
    {
      DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 29));
      regval |= (msg->cm_hdr.ch_id << CAN_TMI_TMEID_SHIFT) | CAN_TMI_TMIDSEL;
    }
  else
    {
      DEBUGASSERT(msg->cm_hdr.ch_id < (1 << 11));
      regval |= msg->cm_hdr.ch_id << CAN_TMI_TMSID_TMEID_SHIFT;
    }

#else
  regval |= (((uint32_t) msg->cm_hdr.ch_id << CAN_TMI_TMSID_TMEID_SHIFT) &
               CAN_TMI_TMSID_TMEID_MASK);

#endif

#ifdef CONFIG_CAN_USE_RTR
  regval |= (msg->cm_hdr.ch_rtr ? CAN_TMI_TMFRSEL : 0);
#endif

  at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);

  /* Set up the DLC */

  dlc     = msg->cm_hdr.ch_dlc;
  regval  = at32can_getreg(priv, AT32_CAN_TMC_OFFSET(txmb));
  regval &= ~(CAN_TMC_TMDTBL_MASK | CAN_TMC_TMTSTEN);
  regval |= (uint32_t)dlc << CAN_TMC_TMDTBL_SHIFT;
  at32can_putreg(priv, AT32_CAN_TMC_OFFSET(txmb), regval);

  /* Set up the data fields */

  ptr    = msg->cm_data;
  regval = 0;

  if (dlc > 0)
    {
      tmp    = (uint32_t)*ptr++;
      regval = tmp << CAN_TMDTL_TMDT0_SHIFT;

      if (dlc > 1)
        {
          tmp     = (uint32_t)*ptr++;
          regval |= tmp << CAN_TMDTL_TMDT1_SHIFT;

          if (dlc > 2)
            {
              tmp     = (uint32_t)*ptr++;
              regval |= tmp << CAN_TMDTL_TMDT2_SHIFT;

              if (dlc > 3)
                {
                  tmp     = (uint32_t)*ptr++;
                  regval |= tmp << CAN_TMDTL_TMDT3_SHIFT;
                }
            }
        }
    }

  at32can_putreg(priv, AT32_CAN_TMDTL_OFFSET(txmb), regval);

  regval = 0;
  if (dlc > 4)
    {
      tmp    = (uint32_t)*ptr++;
      regval = tmp << CAN_TMDTH_TMDT4_SHIFT;

      if (dlc > 5)
        {
          tmp     = (uint32_t)*ptr++;
          regval |= tmp << CAN_TMDTH_TMDT5_SHIFT;

          if (dlc > 6)
            {
              tmp     = (uint32_t)*ptr++;
              regval |= tmp << CAN_TMDTH_TMDT6_SHIFT;

              if (dlc > 7)
                {
                  tmp     = (uint32_t)*ptr++;
                  regval |= tmp << CAN_TMDTH_TMDT7_SHIFT;
                }
            }
        }
    }

  at32can_putreg(priv, AT32_CAN_TMDTH_OFFSET(txmb), regval);

  /* Enable the transmit mailbox empty interrupt (may already be enabled) */

  regval  = at32can_getreg(priv, AT32_CAN_INTEN_OFFSET);
  regval |= CAN_INTEN_TCIEN;
  at32can_putreg(priv, AT32_CAN_INTEN_OFFSET, regval);

  /* Request transmission */

  regval  = at32can_getreg(priv, AT32_CAN_TMI_OFFSET(txmb));
  regval |= CAN_TMI_TMSR;  /* Transmit Mailbox Request */
  at32can_putreg(priv, AT32_CAN_TMI_OFFSET(txmb), regval);

  at32can_dumpmbregs(priv, "After send");
  return OK;
}

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

static bool at32can_txready(struct can_dev_s *dev)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval;

  /* Return true if any mailbox is available */

  regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
  caninfo("CAN%" PRIu8 " TSTS: %08" PRIx32 "\n", priv->port, regval);

  return at32can_txmb0empty(regval) || at32can_txmb1empty(regval) ||
         at32can_txmb2empty(regval);
}

/****************************************************************************
 * Name: at32can_txempty
 *
 * Description:
 *   Return true if all message have been sent.  If for example, the CAN
 *   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 CAN hardware.
 *
 ****************************************************************************/

static bool at32can_txempty(struct can_dev_s *dev)
{
  struct at32_can_s *priv = dev->cd_priv;
  uint32_t regval;

  /* Return true if all mailboxes are available */

  regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);
  caninfo("CAN%" PRIu8 " TSTS: %08" PRIx32 "\n", priv->port, regval);

  return at32can_txmb0empty(regval) && at32can_txmb1empty(regval) &&
         at32can_txmb2empty(regval);
}

/****************************************************************************
 * Name: at32can_rxinterrupt
 *
 * Description:
 *   CAN RX FIFO 0/1 interrupt handler
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *   rxmb - The RX mailbox number.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_rxinterrupt(struct can_dev_s *dev, int rxmb)
{
  struct at32_can_s *priv;
  struct can_hdr_s hdr;
  uint8_t data[CAN_MAXDATALEN];
  uint32_t regval;
  int npending;
  int ret;

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

  /* Verify that a message is pending in the FIFO */

  regval   = at32can_getreg(priv, AT32_CAN_RF_OFFSET(rxmb));
  npending = (regval & CAN_RF_RFMN_MASK) >> CAN_RF_RFMN_SHIFT;
  if (npending < 1)
    {
      canwarn("WARNING: No messages pending\n");
      return OK;
    }

  if (rxmb == 0)
    {
      at32can_dumpmbregs(priv, "RX0 interrupt");
    }
  else
    {
      at32can_dumpmbregs(priv, "RX1 interrupt");
    }

  /* Get the CAN identifier. */

  regval = at32can_getreg(priv, AT32_CAN_RFI_OFFSET(rxmb));

#ifdef CONFIG_CAN_EXTID
  if ((regval & CAN_RDI_RFIDI) != 0)
    {
      hdr.ch_id    = \
      (regval & CAN_RDI_RFEID_MASK) >> CAN_RDI_RFEID_SHIFT;
      hdr.ch_extid = true;
    }
  else
    {
      hdr.ch_id    = \
      (regval & CAN_RDI_RFSID_RFEID_MASK) >> CAN_RDI_RFSID_RFEID_SHIFT;
      hdr.ch_extid = false;
    }
#else
  if ((regval & CAN_RDI_RFIDI) != 0)
    {
      canerr("ERROR: Received message with extended identifier.  Dropped\n");
      ret = -ENOSYS;
      goto errout;
    }

  hdr.ch_id = \
  (regval & CAN_RDI_RFSID_RFEID_MASK) >> CAN_RDI_RFSID_RFEID_SHIFT;
#endif

  /* Clear the error indication and unused bits */

#ifdef CONFIG_CAN_ERRORS
  hdr.ch_error  = 0; /* Error reporting not supported */
#endif
  hdr.ch_tcf    = 0;

  /* Extract the RTR bit */

  hdr.ch_rtr = (regval & CAN_RDI_RFFRI) != 0;

  /* Get the DLC */

  regval     = at32can_getreg(priv, AT32_CAN_RFC_OFFSET(rxmb));
  hdr.ch_dlc = (regval & CAN_RFC_RFDTL_MASK) >> CAN_RFC_RFDTL_SHIFT;

  /* Save the message data */

  regval  = at32can_getreg(priv, AT32_CAN_RFDTL_OFFSET(rxmb));
  data[0] = (regval & CAN_RFDTL_RFDT0_MASK) >> CAN_RFDTL_RFDT0_SHIFT;
  data[1] = (regval & CAN_RFDTL_RFDT1_MASK) >> CAN_RFDTL_RFDT1_SHIFT;
  data[2] = (regval & CAN_RFDTL_RFDT2_MASK) >> CAN_RFDTL_RFDT2_SHIFT;
  data[3] = (regval & CAN_RFDTL_RFDT3_MASK) >> CAN_RFDTL_RFDT3_SHIFT;

  regval  = at32can_getreg(priv, AT32_CAN_RFDTH_OFFSET(rxmb));
  data[4] = (regval & CAN_RFDTH_RFDT4_MASK) >> CAN_RFDTH_RFDT4_SHIFT;
  data[5] = (regval & CAN_RFDTH_RFDT5_MASK) >> CAN_RFDTH_RFDT5_SHIFT;
  data[6] = (regval & CAN_RFDTH_RFDT6_MASK) >> CAN_RFDTH_RFDT6_SHIFT;
  data[7] = (regval & CAN_RFDTH_RFDT7_MASK) >> CAN_RFDTH_RFDT7_SHIFT;

  /* Provide the data to the upper half driver */

  ret = can_receive(dev, &hdr, data);

  /* Release the FIFO */

#ifndef CONFIG_CAN_EXTID
errout:
#endif
  regval  = at32can_getreg(priv, AT32_CAN_RF_OFFSET(rxmb));
  regval |= CAN_RF_RFR;
  at32can_putreg(priv, AT32_CAN_RF_OFFSET(rxmb), regval);
  return ret;
}

/****************************************************************************
 * Name: at32can_rx0interrupt
 *
 * Description:
 *   CAN RX FIFO 0 interrupt handler
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_rx0interrupt(int irq, void *context, void *arg)
{
  struct can_dev_s *dev = (struct can_dev_s *)arg;
  return at32can_rxinterrupt(dev, 0);
}

/****************************************************************************
 * Name: at32can_rx1interrupt
 *
 * Description:
 *   CAN RX FIFO 1 interrupt handler
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_rx1interrupt(int irq, void *context, void *arg)
{
  struct can_dev_s *dev = (struct can_dev_s *)arg;
  return at32can_rxinterrupt(dev, 1);
}

/****************************************************************************
 * Name: at32can_txinterrupt
 *
 * Description:
 *   CAN TX mailbox complete interrupt handler
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_txinterrupt(int irq, void *context, void *arg)
{
  struct can_dev_s *dev = (struct can_dev_s *)arg;
  struct at32_can_s *priv;
  uint32_t regval;

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

  /* Get the transmit status */

  regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);

  /* Check for RQCP0: Request completed mailbox 0 */

  if ((regval & CAN_TSTS_TM0TCF) != 0)
    {
      /* Writing '1' to RCP0 clears RCP0 and all the status bits (TXOK0,
       * ALST0 and TERR0) for Mailbox 0.
       */

      at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM0TCF);

      /* Tell the upper half that the transfer is finished. */

      can_txdone(dev);
    }

  /* Check for RQCP1: Request completed mailbox 1 */

  if ((regval & CAN_TSTS_TM1TCF) != 0)
    {
      /* Writing '1' to RCP1 clears RCP1 and all the status bits (TXOK1,
       * ALST1 and TERR1) for Mailbox 1.
       */

      at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM1TCF);

      /* Tell the upper half that the transfer is finished. */

      can_txdone(dev);
    }

  /* Check for RQCP2: Request completed mailbox 2 */

  if ((regval & CAN_TSTS_TM2TCF) != 0)
    {
      /* Writing '1' to RCP2 clears RCP2 and all the status bits (TXOK2,
       * ALST2 and TERR2) for Mailbox 2.
       */

      at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, CAN_TSTS_TM2TCF);

      /* Tell the upper half that the transfer is finished. */

      can_txdone(dev);
    }

  return OK;
}

#ifdef CONFIG_CAN_ERRORS
/****************************************************************************
 * Name: at32can_sceinterrupt
 *
 * Description:
 *   CAN status change interrupt handler
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_sceinterrupt(int irq, void *context, void *arg)
{
  struct can_dev_s   *dev     = (struct can_dev_s *)arg;
  struct at32_can_s *priv    = NULL;
  struct can_hdr_s    hdr;
  uint32_t            regval  = 0;
  uint16_t            errbits = 0;
  uint8_t             data[CAN_ERROR_DLC];
  int                 ret     = OK;

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

  /* Check Error Interrupt flag */

  regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
  if (regval & CAN_MSTS_EOIF)
    {
      /* Encode error bits */

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

      /* Get Error statur register */

      regval = at32can_getreg(priv, AT32_CAN_ESTS_OFFSET);

      if (regval & CAN_ESTS_EAF)
        {
          /* Error warning flag */

          data[1] |= (CAN_ERROR1_RXWARNING | CAN_ERROR1_TXWARNING);
          errbits |= CAN_ERROR_CONTROLLER;
        }

      if (regval & CAN_ESTS_EPF)
        {
          /* Error passive flag */

          data[1] |= (CAN_ERROR1_RXPASSIVE | CAN_ERROR1_TXPASSIVE);
          errbits |= CAN_ERROR_CONTROLLER;
        }

      if (regval & CAN_ESTS_BOF)
        {
          /* Bus-off flag */

          errbits |= CAN_ERROR_BUSOFF;
        }

      /* Last error code */

      if (regval & CAN_ESTS_ETR_MASK)
        {
          if (regval & CAN_ESTS_ETR_STUFF)
            {
              /* Stuff Error */

              errbits |= CAN_ERROR_PROTOCOL;
              data[2] |= CAN_ERROR2_STUFF;
            }
          else if (regval & CAN_ESTS_ETR_FORM)
            {
              /* Format Error */

              errbits |= CAN_ERROR_PROTOCOL;
              data[2] |= CAN_ERROR2_FORM;
            }
          else if (regval & CAN_ESTS_ETR_ACK)
            {
              /* Acknowledge Error */

              errbits |= CAN_ERROR_NOACK;
            }
          else if (regval & CAN_ESTS_ETR_BREC)
            {
              /* Bit recessive Error */

              errbits |= CAN_ERROR_PROTOCOL;
              data[2] |= CAN_ERROR2_BIT1;
            }
          else if (regval & CAN_ESTS_ETR_BDOM)
            {
              /* Bit dominant Error */

              errbits |= CAN_ERROR_PROTOCOL;
              data[2] |= CAN_ERROR2_BIT0;
            }
          else if (regval & CAN_ESTS_ETR_CRC)
            {
              /* Receive CRC Error */

              errbits |= CAN_ERROR_PROTOCOL;
              data[3] |= CAN_ERROR3_CRCSEQ;
            }
        }

      /* Get transmit status register */

      regval = at32can_getreg(priv, AT32_CAN_TSTS_OFFSET);

      if (regval & CAN_TSTS_TM0ALF || regval & CAN_TSTS_TM1ALF ||
          regval & CAN_TSTS_TM2ALF)
        {
          /* Lost arbitration Error */

          errbits |= CAN_ERROR_LOSTARB;
        }

      /* Clear TSR register */

      at32can_putreg(priv, AT32_CAN_TSTS_OFFSET, regval);

      /* Clear ERRI flag */

      at32can_putreg(priv, AT32_CAN_MSTS_OFFSET, CAN_MSTS_EOIF);
    }

  /* TODO: RX overflow and TX overflow */

  /* Report a CAN error */

  if (errbits != 0)
    {
      canerr("ERROR: errbits = %08" PRIx16 "\n", errbits);

      /* 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);
        }
    }

  return ret;
}
#endif

/****************************************************************************
 * Name: at32can_bittiming
 *
 * Description:
 *   Set the CAN bit timing register (BTR) based on the configured BAUD.
 *
 * "The bit timing logic monitors the serial bus-line and performs sampling
 *  and adjustment of the sample point by synchronizing on the start-bit edge
 *  and resynchronizing on the following edges.
 *
 * "Its operation may be explained simply by splitting nominal bit time into
 *  three segments as follows:
 *
 * 1. "Synchronization segment (SYNC_SEG): a bit change is expected to occur
 *     within this time segment. It has a fixed length of one time quantum
 *     (1 x tCAN).
 * 2. "Bit segment 1 (BS1): defines the location of the sample point. It
 *     includes the PROP_SEG and PHASE_SEG1 of the CAN standard. Its duration
 *     is programmable between 1 and 16 time quanta but may be automatically
 *     lengthened to compensate for positive phase drifts due to differences
 *     in the frequency of the various nodes of the network.
 * 3. "Bit segment 2 (BS2): defines the location of the transmit point. It
 *     represents the PHASE_SEG2 of the CAN standard. Its duration is
 *     programmable between 1 and 8 time quanta but may also be automatically
 *     shortened to compensate for negative phase drifts."
 *
 * Pictorially:
 *
 *  |<----------------- NOMINAL BIT TIME ----------------->|
 *  |<- SYNC_SEG ->|<------ BS1 ------>|<------ BS2 ------>|
 *  |<---- Tq ---->|<----- Tbs1 ------>|<----- Tbs2 ------>|
 *
 * Where
 *   Tbs1 is the duration of the BS1 segment
 *   Tbs2 is the duration of the BS2 segment
 *   Tq is the "Time Quantum"
 *
 * Relationships:
 *
 *   baud = 1 / bit_time
 *   bit_time = Tq + Tbs1 + Tbs2
 *   Tbs1 = Tq * ts1
 *   Tbs2 = Tq * ts2
 *   Tq = brp * Tpclk1
 *   baud = Fpclk1 / (brp  * (1 + ts1 + ts2))
 *
 * Where:
 *   Tpclk1 is the period of the APB1 clock (PCLK1).
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int at32can_bittiming(struct at32_can_s *priv)
{
  uint32_t tmp;
  uint32_t brp;
  uint32_t ts1;
  uint32_t ts2;

  caninfo("CAN%" PRIu8 " PCLK1: %lu baud: %" PRIu32 "\n",
          priv->port, (unsigned long) AT32_PCLK1_FREQUENCY, priv->baud);

  /* Try to get CAN_BIT_QUANTA quanta in one bit_time.
   *
   *   bit_time = Tq*(ts1 + ts2 + 1)
   *   nquanta  = bit_time / Tq
   *   nquanta  = (ts1 + ts2 + 1)
   *
   *   bit_time = brp * Tpclk1 * (ts1 + ts2 + 1)
   *   nquanta  = bit_time / brp / Tpclk1
   *            = PCLK1 / baud / brp
   *   brp      = PCLK1 / baud / nquanta;
   *
   * Example:
   *   PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3
   *   PCLK1 = 42,000,000 baud =   700,000 nquanta = 14 : brp = 4
   */

  tmp = AT32_PCLK1_FREQUENCY / priv->baud;
  if (tmp < CAN_BIT_QUANTA)
    {
      /* At the smallest brp value (1), there are already too few bit times
       * (PCLCK1 / baud) to meet our goal.  brp must be one and we need
       * make some reasonable guesses about ts1 and ts2.
       */

      brp = 1;

      /* In this case, we have to guess a good value for ts1 and ts2 */

      ts1 = (tmp - 1) >> 1;
      ts2 = tmp - ts1 - 1;
      if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
        {
          ts1--;
          ts2++;
        }
    }

  /* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_AT32_CAN_TSEG1,
   * ts2 is CONFIG_AT32_CAN_TSEG2 and we calculate brp to achieve
   * CAN_BIT_QUANTA quanta in the bit time
   */

  else
    {
      ts1 = CONFIG_AT32_CAN_TSEG1;
      ts2 = CONFIG_AT32_CAN_TSEG2;
      brp = (tmp + (CAN_BIT_QUANTA / 2)) / CAN_BIT_QUANTA;
      DEBUGASSERT(brp >= 1 && brp <= CAN_BTR_BRP_MAX);
    }

  caninfo("TS1: %" PRIu32 " TS2: %" PRIu32 " BRP: %" PRIu32 "\n",
               ts1, ts2, brp);

  /* Configure bit timing.  This also does the following, less obvious
   * things.  Unless loopback mode is enabled, it:
   *
   * - Disables silent mode.
   * - Disables loopback mode.
   *
   * NOTE that for the time being, SJW is set to 1 just because I don't
   * know any better.
   */

  tmp = ((brp - 1) << CAN_BTMG_BRDIV_SHIFT) | \
  ((ts1 - 1) << CAN_BTMG_BTS1_SHIFT) | \
  ((ts2 - 1) << CAN_BTMG_BTS2_SHIFT) | \
  ((1 - 1) << CAN_BTMG_RSAW_SHIFT);

#ifdef CONFIG_CAN_LOOPBACK
  /* tmp |= (CAN_BTMG_LBEN | CAN_BTMG_LOEN); */

  tmp |= CAN_BTMG_LBEN;
#endif

  at32can_putreg(priv, AT32_CAN_BTMG_OFFSET, tmp);
  return OK;
}

/****************************************************************************
 * Name: at32can_enterinitmode
 *
 * Description:
 *   Put the CAN cell in Initialization mode. This only disconnects the CAN
 *   peripheral, no registers are changed. The initialization mode is
 *   required to change the baud rate.
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int at32can_enterinitmode(struct at32_can_s *priv)
{
  uint32_t regval;
  volatile uint32_t timeout;

  caninfo("CAN%" PRIu8 "\n", priv->port);

  /* Enter initialization mode */

  regval  = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
  regval |= CAN_MCTRL_FZEN;
  at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);

  /* Wait until initialization mode is acknowledged */

  for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
    {
      regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
      if ((regval & CAN_MSTS_FZC) != 0)
        {
          /* We are in initialization mode */

          break;
        }
    }

  /* Check for a timeout */

  if (timeout < 1)
    {
      canerr("ERROR: Timed out waiting to enter initialization mode\n");
      return -ETIMEDOUT;
    }

  return OK;
}

/****************************************************************************
 * Name: at32can_exitinitmode
 *
 * Description:
 *   Put the CAN cell out of the Initialization mode (to Normal mode)
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int at32can_exitinitmode(struct at32_can_s *priv)
{
  uint32_t regval;
  volatile uint32_t timeout;

  /* Exit Initialization mode, enter Normal mode */

  regval  = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
  regval &= ~CAN_MCTRL_FZEN;
  at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);

  /* Wait until the initialization mode exit is acknowledged */

  for (timeout = INAK_TIMEOUT; timeout > 0; timeout--)
    {
      regval = at32can_getreg(priv, AT32_CAN_MSTS_OFFSET);
      if ((regval & CAN_MSTS_FZC) == 0)
        {
          /* We are out of initialization mode */

          break;
        }
    }

  /* Check for a timeout */

  if (timeout < 1)
    {
      canerr("ERROR: Timed out waiting to exit initialization mode: %08"
                  PRIx32 "\n", regval);
      return -ETIMEDOUT;
    }

  return OK;
}

/****************************************************************************
 * Name: at32can_cellinit
 *
 * Description:
 *   CAN cell initialization
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int at32can_cellinit(struct at32_can_s *priv)
{
  uint32_t regval;
  int ret;

  caninfo("CAN%" PRIu8 "\n", priv->port);

  /* Exit from sleep mode */

  regval  = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
  regval &= ~CAN_MCTRL_DZEN;
  at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);

  ret = at32can_enterinitmode(priv);
  if (ret != 0)
    {
      return ret;
    }

  /* Disable the following modes:
   *
   *  - Time triggered communication mode
   *  - Automatic bus-off management
   *  - Automatic wake-up mode
   *  - No automatic retransmission
   *  - Receive FIFO locked mode
   *
   * Enable:
   *
   *  - Transmit FIFO priority
   */

  regval  = at32can_getreg(priv, AT32_CAN_MCTRL_OFFSET);
  regval &= ~(CAN_MCTRL_MDRSEL | CAN_MCTRL_PRSFEN | CAN_MCTRL_AEDEN |
              CAN_MCTRL_AEBOEN | CAN_MCTRL_TTCEN);
  regval |=  CAN_MCTRL_MMSSR;
  at32can_putreg(priv, AT32_CAN_MCTRL_OFFSET, regval);

  /* Configure bit timing. */

  ret = at32can_bittiming(priv);
  if (ret < 0)
    {
      canerr("ERROR: Failed to set bit timing: %d\n", ret);
      return ret;
    }

  return at32can_exitinitmode(priv);
}

/****************************************************************************
 * Name: at32can_filterinit
 *
 * Description:
 *   CAN filter initialization.  CAN filters are not currently used by this
 *   driver.  The CAN filters can be configured in a different way:
 *
 *   1. As a match of specific IDs in a list (IdList mode), or as
 *   2. And ID and a mask (IdMask mode).
 *
 *   Filters can also be configured as:
 *
 *   3. 16- or 32-bit.  The advantage of 16-bit filters is that you get
 *      more filters;  The advantage of 32-bit filters is that you get
 *      finer control of the filtering.
 *
 *   One filter is set up for each CAN.  The filter resources are shared
 *   between the two CAN modules:  CAN1 uses only filter 0 (but reserves
 *   0 through CAN_NFILTERS/2-1); CAN2 uses only filter CAN_NFILTERS/2
 *   (but reserves CAN_NFILTERS/2 through CAN_NFILTERS-1).
 *
 *   32-bit IdMask mode is configured.  However, both the ID and the MASK
 *   are set to zero thus suppressing all filtering because anything masked
 *   with zero matches zero.
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *
 * Returned Value:
 *   Zero on success; a negated errno value on failure.
 *
 ****************************************************************************/

static int at32can_filterinit(struct at32_can_s *priv)
{
  uint32_t regval;
  uint32_t bitmask;

  caninfo("CAN%" PRIu8 " filter: %" PRIu8 "\n", priv->port, priv->filter);

  /* Get the bitmask associated with the filter used by this CAN block */

  bitmask = (uint32_t)1 << priv->filter;

  /* Enter filter initialization mode */

  regval  = at32can_getfreg(priv, AT32_CAN_FCTRL_OFFSET);
  regval |= CAN_FCTRL_FCS;
  at32can_putfreg(priv, AT32_CAN_FCTRL_OFFSET, regval);

  /* Disable the filter */

  regval  = at32can_getfreg(priv, AT32_CAN_FACFG_OFFSET);
  regval &= ~bitmask;
  at32can_putfreg(priv, AT32_CAN_FACFG_OFFSET, regval);

  /* Select the 32-bit scale for the filter */

  regval  = at32can_getfreg(priv, AT32_CAN_FSCFG_OFFSET);
  regval |= bitmask;
  at32can_putfreg(priv, AT32_CAN_FSCFG_OFFSET, regval);

  /* There are 14 or 28 filter banks (depending) on the device.
   * Each filter bank is composed of two 32-bit registers, CAN_FiR:
   */

  at32can_putfreg(priv, AT32_CAN_FBF_OFFSET(priv->filter, 1), 0);
  at32can_putfreg(priv, AT32_CAN_FBF_OFFSET(priv->filter, 2), 0);

  /* Set Id/Mask mode for the filter */

  regval  = at32can_getfreg(priv, AT32_CAN_FMCFG_OFFSET);
  regval &= ~bitmask;
  at32can_putfreg(priv, AT32_CAN_FMCFG_OFFSET, regval);

  /* Assign FIFO 0 for the filter */

  regval  = at32can_getfreg(priv, AT32_CAN_FRF_OFFSET);
  regval &= ~bitmask;
  at32can_putfreg(priv, AT32_CAN_FRF_OFFSET, regval);

  /* Enable the filter */

  regval  = at32can_getfreg(priv, AT32_CAN_FACFG_OFFSET);
  regval |= bitmask;
  at32can_putfreg(priv, AT32_CAN_FACFG_OFFSET, regval);

  /* Exit filter initialization mode */

  regval  = at32can_getfreg(priv, AT32_CAN_FCTRL_OFFSET);
  regval &= ~CAN_FCTRL_FCS;
  at32can_putfreg(priv, AT32_CAN_FCTRL_OFFSET, regval);
  return OK;
}

/****************************************************************************
 * Name: at32can_addextfilter
 *
 * Description:
 *   Add a filter for extended CAN IDs
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *   arg  - A pointer to a structure describing the filter
 *
 * Returned Value:
 *   A non-negative filter ID is returned on success.
 *   Otherwise -1 (ERROR) is returned with the errno
 *   set to indicate the nature of the error.
 *
 ****************************************************************************/

#ifdef CONFIG_CAN_EXTID
static int at32can_addextfilter(struct at32_can_s *priv,
                                 struct canioc_extfilter_s *arg)
{
  return -ENOTTY;
}
#endif

/****************************************************************************
 * Name: at32can_delextfilter
 *
 * Description:
 *   Remove a filter for extended CAN IDs
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *   arg  - The filter index previously returned by the
 *            CANIOC_ADD_EXTFILTER command
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise -1 (ERROR)
 *   returned with the errno variable set to indicate the
 *   of the error.
 *
 ****************************************************************************/

#ifdef CONFIG_CAN_EXTID
static int at32can_delextfilter(struct at32_can_s *priv, int arg)
{
  return -ENOTTY;
}
#endif

/****************************************************************************
 * Name: at32can_addstdfilter
 *
 * Description:
 *   Add a filter for standard CAN IDs
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *   arg  - A pointer to a structure describing the filter
 *
 * Returned Value:
 *   A non-negative filter ID is returned on success.
 *   Otherwise -1 (ERROR) is returned with the errno
 *   set to indicate the nature of the error.
 *
 ****************************************************************************/

static int at32can_addstdfilter(struct at32_can_s *priv,
                                 struct canioc_stdfilter_s *arg)
{
  return -ENOTTY;
}

/****************************************************************************
 * Name: at32can_delstdfilter
 *
 * Description:
 *   Remove a filter for standard CAN IDs
 *
 * Input Parameters:
 *   priv - A pointer to the private data structure for this CAN block
 *   arg  - The filter index previously returned by the
 *            CANIOC_ADD_STDFILTER command
 *
 * Returned Value:
 *   Zero (OK) is returned on success.  Otherwise -1 (ERROR)
 *   returned with the errno variable set to indicate the
 *   of the error.
 *
 ****************************************************************************/

static int at32can_delstdfilter(struct at32_can_s *priv, int arg)
{
  return -ENOTTY;
}

/****************************************************************************
 * Name: at32can_txmb0empty
 *
 * Input Parameters:
 *   tsr_regval - value of CAN transmit status register
 *
 * Returned Value:
 *   Returns true if mailbox 0 is empty and can be used for sending.
 *
 ****************************************************************************/

static bool at32can_txmb0empty(uint32_t tsr_regval)
{
  return (tsr_regval & CAN_TSTS_TM0EF) != 0 &&
         (tsr_regval & CAN_TSTS_TM0TCF) == 0;
}

/****************************************************************************
 * Name: at32can_txmb1empty
 *
 * Input Parameters:
 *   tsr_regval - value of CAN transmit status register
 *
 * Returned Value:
 *   Returns true if mailbox 1 is empty and can be used for sending.
 *
 ****************************************************************************/

static bool at32can_txmb1empty(uint32_t tsr_regval)
{
  return (tsr_regval & CAN_TSTS_TM1EF) != 0 &&
         (tsr_regval & CAN_TSTS_TM1TCF) == 0;
}

/****************************************************************************
 * Name: at32can_txmb2empty
 *
 * Input Parameters:
 *   tsr_regval - value of CAN transmit status register
 *
 * Returned Value:
 *   Returns true if mailbox 2 is empty and can be used for sending.
 *
 ****************************************************************************/

static bool at32can_txmb2empty(uint32_t tsr_regval)
{
  return (tsr_regval & CAN_TSTS_TM2EF) != 0 &&
         (tsr_regval & CAN_TSTS_TM2TCF) == 0;
}

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

/****************************************************************************
 * Name: at32_caninitialize
 *
 * Description:
 *   Initialize the selected CAN port
 *
 * Input Parameters:
 *   Port number (for hardware that has multiple CAN interfaces)
 *
 * Returned Value:
 *   Valid CAN device structure reference on success; a NULL on failure
 *
 ****************************************************************************/

struct can_dev_s *at32_caninitialize(int port)
{
  struct can_dev_s *dev = NULL;

  caninfo("CAN%" PRIu8 "\n", port);

  /* NOTE:  Peripherical clocking for CAN1 and/or CAN2 was already provided
   * by at32_clockconfig() early in the reset sequence.
   */

#ifdef CONFIG_AT32_CAN1
  if (port == 1)
    {
      /* Select the CAN1 device structure */

      dev = &g_can1dev;

      /* Configure CAN1 pins.  The ambiguous settings in the at32*_pinmap.h
       * file must have been disambiguated in the board.h file.
       */

      at32_configgpio(GPIO_CAN1_RX);
      at32_configgpio(GPIO_CAN1_TX);
    }
  else
#endif
#ifdef CONFIG_AT32_CAN2
  if (port == 2)
    {
      /* Select the CAN2 device structure */

      dev = &g_can2dev;

      /* Configure CAN2 pins.  The ambiguous settings in the at32*_pinmap.h
       * file must have been disambiguated in the board.h file.
       */

      at32_configgpio(GPIO_CAN2_RX);
      at32_configgpio(GPIO_CAN2_TX);
    }
  else
#endif
    {
      canerr("ERROR: Unsupported port %d\n", port);
      return NULL;
    }

  return dev;
}

#endif /* CONFIG_CAN && (CONFIG_AT32_CAN1 || CONFIG_AT32_CAN2) */