/****************************************************************************
 * drivers/can/mcp2515.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 <string.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <strings.h>

#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/signal.h>
#include <nuttx/mutex.h>
#include <nuttx/semaphore.h>
#include <nuttx/kmalloc.h>
#include <nuttx/spi/spi.h>
#include <nuttx/can/can.h>
#include <nuttx/can/mcp2515.h>

#include "mcp2515.h"

#if defined(CONFIG_CAN) && defined(CONFIG_CAN_MCP2515)

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

/* MCP2515 Configuration ****************************************************/

#define CAN_FRAME_MAX_DATA_LEN 8
#define SPI_TRANSFER_BUF_LEN   (6 + CAN_FRAME_MAX_DATA_LEN)
#define MCP2515_NUM_TX_BUFFERS 3

/* Bit timing */

#define MCP2515_PROPSEG  CONFIG_MCP2515_PROPSEG
#define MCP2515_PHSEG1   CONFIG_MCP2515_PHASESEG1
#define MCP2515_PHSEG2   CONFIG_MCP2515_PHASESEG2
#define MCP2515_TSEG1    (MCP2515_PROPSEG + MCP2515_PHSEG1)
#define MCP2515_TSEG2    MCP2515_PHSEG2
#define MCP2515_BRP      ((uint8_t)(((float)(MCP2515_CANCLK_FREQUENCY) / \
                         ((float)(MCP2515_TSEG1 + MCP2515_TSEG2 + 1) * \
                         (float)(2 * CONFIG_MCP2515_BITRATE))) - 1))
#define MCP2515_SJW      CONFIG_MCP2515_SJW

#if MCP2515_PROPSEG < 1
#  error Invalid PROPSEG. It cannot be lower than 1
#endif
#if MCP2515_PROPSEG > 8
#  error Invalid PROPSEG. It cannot be greater than 8
#endif
#if MCP2515_PHSEG1 < 1
#  error Invalid PHSEG1. It cannot be lower than 1
#endif
#if MCP2515_PHSEG1 > 8
#  error Invalid PHSEG1. It cannot be greater than 1
#endif
#if MCP2515_TSEG2 < 2
#  error Invalid TSEG2. It cannot be lower than 2
#endif
#if MCP2515_TSEG2 > 8
#  error Invalid TSEG2. It cannot be greater than 8
#endif
#if MCP2515_SJW > 4
#  error Invalid SJW. It cannot be greater than 4
#endif

/* MCP2515 RXB0 element size */

/* MCP2515 RXB1 element size */

/* MCP2515 Filters */

#ifndef CONFIG_MCP2515_NSTDFILTERS
#  define CONFIG_MCP2515_NSTDFILTERS 0
#endif

#if (CONFIG_MCP2515_NSTDFILTERS > 128)
#  error Invalid MCP25150 number of Standard Filters
#endif

#ifndef CONFIG_MCP2515_NEXTFILTERS
#  define CONFIG_MCP2515_NEXTFILTERS 0
#endif

#if (CONFIG_MCP2515_NEXTFILTERS > 64)
#  error Invalid MCP25150 number of Extended Filters
#endif

#define MCP2515_STDFILTER_BYTES \
   MCP2515_ALIGN_UP(CONFIG_MCP2515_NSTDFILTERS << 2)
#define MCP2515_STDFILTER_WORDS (MCP2515_STDFILTER_BYTES >> 2)

#define MCP2515_EXTFILTER_BYTES \
   MCP2515_ALIGN_UP(CONFIG_MCP2515_NEXTFILTERS << 3)
#define MCP2515_EXTFILTER_WORDS (MCP2515_EXTFILTER_BYTES >> 2)

/* MCP25150 TX buffer element size */

/* MCP25150 TX FIFOs */

/* Loopback mode */

#undef MCP2515_LOOPBACK
#if defined(CONFIG_MCP2515_LOOPBACK)
#  define MCP2515_LOOPBACK 1
#endif

/* Interrupts ***************************************************************/

/* Interrupts Errors
 *
 *   MCP2515_INT_MERR  - Message Error Interrupt Flag bit
 *   MCP2515_INT_ERR   - Error Interrupt Flag bit (mult src in EFLG register)
 */

#define MCP2515_ERROR_INTS    (MCP2515_INT_MERR | MCP2515_INT_ERR)

/* RXn buffer interrupts
 *
 *   MCP2515_INT_RX0  - Receive Buffer 0 New Message
 *   MCP2515_INT_RX1  - Receive Buffer 1 New Message
 */

#define MCP2515_RXBUFFER_INTS   (MCP2515_INT_RX0 | MCP2515_INT_RX1)

/* TXn buffer interrupts
 *
 *   MCP2515_INT_TX0  - Transmit Buffer 0 Empty Interrupt
 *   MCP2515_INT_TX1  - Transmit Buffer 1 Empty Interrupt
 *   MCP2515_INT_TX2  - Transmit Buffer 2 Empty Interrupt
 */

#define MCP2515_TXBUFFER_INTS  (MCP2515_INT_TX0 | MCP2515_INT_TX1 | MCP2515_INT_TX2)

/* Helpers ******************************************************************/

#define TXREGVAL(reg) priv->spi_txbuf[reg - MCP2515_TXB0CTRL]

/* Debug ********************************************************************/

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

#ifndef CONFIG_DEBUG_CAN_INFO
#  undef CONFIG_MCP2515_REGDEBUG
#endif

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

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

/* CAN driver state */

enum can_state_s
{
  MCP2515_STATE_UNINIT = 0,    /* Not yet initialized */
  MCP2515_STATE_RESET,         /* Initialized, reset state */
  MCP2515_STATE_SETUP,         /* can_setup() has been called */
};

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

struct mcp2515_can_s
{
  FAR struct mcp2515_config_s *config; /* The constant configuration */

  uint8_t state;               /* See enum can_state_s */
  uint8_t nalloc;              /* Number of allocated filters */
  mutex_t lock;                /* Enforces mutually exclusive access */
  sem_t txfsem;                /* Used to wait for TX FIFO availability */
  uint32_t btp;                /* Current bit timing */
  uint8_t rxints;              /* Configured RX interrupts */
  uint8_t txints;              /* Configured TX interrupts */
#ifdef CONFIG_CAN_ERRORS
  uint32_t olderrors;          /* Used to detect the changes in error states */
#endif
  uint8_t filters;             /* Standard/Extende filter bit allocator. */
  uint8_t txbuffers;           /* TX Buffers bit allocator. */

  FAR uint8_t *spi_txbuf;
  FAR uint8_t *spi_rxbuf;
#ifdef CONFIG_MCP2515_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
 ****************************************************************************/

/* MCP2515 Register access */

static void mcp2515_config_spi(FAR struct mcp2515_can_s *priv);

static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr,
              FAR uint8_t *buffer, uint8_t len);
static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv,
              uint8_t regaddr,
              FAR const uint8_t *buffer, uint8_t len);
static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv,
              uint8_t regaddr, uint8_t mask, uint8_t value);
#ifdef CONFIG_MCP2515_REGDEBUG
static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv,
              FAR const char *msg);
#else
#  define mcp2515_dumpregs(priv,msg)
#endif

/* MCP2515 helpers */

#ifdef CONFIG_CAN_EXTID
static int mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv,
              FAR struct canioc_extfilter_s *extconfig);
static int mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx);
#endif
static int mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv,
              FAR struct canioc_stdfilter_s *stdconfig);
static int mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx);

/* CAN driver methods */

static void mcp2515_reset(FAR struct can_dev_s *dev);
static int  mcp2515_setup(FAR struct can_dev_s *dev);
static void mcp2515_shutdown(FAR struct can_dev_s *dev);
static void mcp2515_rxint(FAR struct can_dev_s *dev, bool enable);
static void mcp2515_txint(FAR struct can_dev_s *dev, bool enable);
static int  mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd,
              unsigned long arg);
static int  mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id);
static int  mcp2515_send(FAR struct can_dev_s *dev,
              FAR struct can_msg_s *msg);
static bool mcp2515_txready(FAR struct can_dev_s *dev);
static bool mcp2515_txempty(FAR struct can_dev_s *dev);

/* MCP2515 interrupt handling */

#ifdef CONFIG_CAN_ERRORS
static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status,
              uint8_t oldstatus);
#endif
static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset);
static int  mcp2515_interrupt(FAR struct mcp2515_config_s *config,
             FAR void *arg);

/* Hardware initialization */

static int  mcp2515_hw_initialize(FAR struct mcp2515_can_s *priv);

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

static const struct can_ops_s g_mcp2515ops =
{
  mcp2515_reset,         /* co_reset */
  mcp2515_setup,         /* co_setup */
  mcp2515_shutdown,      /* co_shutdown */
  mcp2515_rxint,         /* co_rxint */
  mcp2515_txint,         /* co_txint */
  mcp2515_ioctl,         /* co_ioctl */
  mcp2515_remoterequest, /* co_remoterequest */
  mcp2515_send,          /* co_send */
  mcp2515_txready,       /* co_txready */
  mcp2515_txempty        /* co_txempty */
};

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

static void mcp2515_config_spi(FAR struct mcp2515_can_s *priv)
{
  /* Setup SPI frequency and mode */

  SPI_SETFREQUENCY(priv->config->spi, CONFIG_MCP2515_SPI_SCK_FREQUENCY);
  SPI_SETMODE(priv->config->spi, MCP2515_SPI_MODE);
  SPI_SETBITS(priv->config->spi, 8);
  SPI_HWFEATURES(priv->config->spi, 0);
}

static void mcp2515_read_2regs(FAR struct mcp2515_can_s *priv, uint8_t reg,
                               FAR uint8_t *v1, FAR uint8_t *v2)
{
  priv->spi_txbuf[0] = MCP2515_READ;
  priv->spi_txbuf[1] = reg;

  SPI_LOCK(priv->config->spi, true);
  mcp2515_config_spi(priv);
  SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), true);
  SPI_EXCHANGE(priv->config->spi, priv->spi_txbuf, priv->spi_rxbuf, 4);
  SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), false);
  SPI_LOCK(priv->config->spi, false);

  *v1 = priv->spi_rxbuf[2];
  *v2 = priv->spi_rxbuf[3];
}

/****************************************************************************
 * Name: mcp2515_readregs
 *
 * Description:
 *   Read value(s) of MCP2515 register(s).
 *
 * Input Parameters:
 *   priv - A reference to the MCP2515 peripheral state
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr,
                             FAR uint8_t *buffer, uint8_t len)
{
  FAR struct mcp2515_config_s *config = priv->config;
#ifdef CONFIG_CANBUS_REGDEBUG
  int i;
#endif

  SPI_LOCK(config->spi, true);

  mcp2515_config_spi(priv);

  /* Select the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);

  /* Send the READ command */

  SPI_SEND(config->spi, MCP2515_READ);

  /* Send register to read and get the next bytes read back */

  SPI_SEND(config->spi, regaddr);
  SPI_RECVBLOCK(config->spi, buffer, len);

  /* Deselect the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);

  /* Unlock bus */

  SPI_LOCK(config->spi, false);

#ifdef CONFIG_CANBUS_REGDEBUG
  for (i = 0; i < len; i++)
    {
      caninfo("%02x->%02x\n", regaddr, buffer[i]);
    }
#endif
}

static void mcp2515_transfer(FAR struct mcp2515_can_s *priv, uint8_t len)
{
  FAR struct mcp2515_config_s *config = priv->config;

  SPI_LOCK(config->spi, true);

  mcp2515_config_spi(priv);

  /* Select the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);

  /* Send the READ command */

  SPI_EXCHANGE(config->spi, priv->spi_txbuf, priv->spi_rxbuf, len);

  /* Deselect the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);

  /* Unlock bus */

  SPI_LOCK(config->spi, false);
}

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

static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv,
                              uint8_t regaddr,
                              FAR const uint8_t *buffer, uint8_t len)
{
  FAR struct mcp2515_config_s *config = priv->config;
#ifdef CONFIG_CANBUS_REGDEBUG
  int i;

  for (i = 0; i < len; i++)
    {
      caninfo("%02x<-%02x\n", regaddr + i, buffer[i]);
    }
#endif

  SPI_LOCK(config->spi, true);

  mcp2515_config_spi(priv);

  /* Select the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);

  /* Send the READ command */

  SPI_SEND(config->spi, MCP2515_WRITE);

  /* Send initial register to be written */

  SPI_SEND(config->spi, regaddr);
  SPI_SNDBLOCK(config->spi, buffer, len);

  /* Deselect the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);

  /* Unlock bus */

  SPI_LOCK(config->spi, false);
}

/****************************************************************************
 * Name: mcp2515_modifyreg
 *
 * Description:
 *   Modify individuals bits of MCP2515 register
 *
 * Input Parameters:
 *   priv - A reference to the MCP2515 peripheral state
 *   offset - The offset to the register to write
 *   regval - The value to write to the register
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv,
                              uint8_t regaddr, uint8_t mask, uint8_t value)
{
  FAR struct mcp2515_config_s *config = priv->config;
  uint8_t wr[4] =
  {
    MCP2515_BITMOD, regaddr, mask, value
  };

  SPI_LOCK(config->spi, true);

  mcp2515_config_spi(priv);

  /* Select the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);

  SPI_SNDBLOCK(config->spi, wr, 4);

  /* Deselect the MCP2515 */

  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);

  /* Unlock bus */

  SPI_LOCK(config->spi, false);
}

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

#ifdef CONFIG_MCP2515_REGDEBUG
static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv,
                             FAR const char *msg)
{
  FAR struct mcp2515_config_s *config = priv->config;
}
#endif

/****************************************************************************
 * Name: mcp2515_add_extfilter
 *
 * Description:
 *   Add an address filter for a extended 29 bit address.
 *
 * Input Parameters:
 *   priv      - An instance of the MCP2515 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 mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv,
                                 FAR struct canioc_extfilter_s *extconfig)
{
  FAR struct mcp2515_config_s *config;
  uint8_t regval;
  uint8_t offset;
  uint8_t mode = CAN_FILTER_MASK;
  int ndx;
  int ret;

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

  /* Get exclusive excess to the MCP2515 hardware */

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

  /* Find an unused standard filter */

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

      if ((priv->filters & (1 << ndx)) == 0)
        {
          /* No, assign the filter */

          DEBUGASSERT(priv->nalloc < priv->config->nfilters);
          priv->filters |= (1 << ndx);
          priv->nalloc++;

          /* Format and write filter */

          DEBUGASSERT(extconfig->xf_id1 <= CAN_MAX_STDMSGID);

          DEBUGASSERT(extconfig->xf_id2 <= CAN_MAX_STDMSGID);

          /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL,
           * RXFnEID8 and RXFnEID0) using this formula:
           *
           * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
           * maskN   = RXM0reg + offset
           */

          if (priv->nalloc <= 3)
            {
              offset = 0;
            }
          else
            {
              offset = 4;
            }

#if 0
          /* N.B. Buffer 0 is higher priority than Buffer 1
           * but to separate these messages we will make this
           * driver more complex. So let to consider that the
           * first 2 IDs inserted in the filter will have more
           * priority than the latest 4 IDs.
           */

          if (extconfig->sf_prio == CAN_MSGPRIO_LOW)
            {
              /* Use RXB1 filters */
            }
          else
            {
              /* Use RXB0 filters */
            }
#endif

          switch (extconfig->xf_type)
            {
              default:
              case CAN_FILTER_DUAL:
                mode = CAN_FILTER_DUAL;
                break;

              case CAN_FILTER_MASK:
                mode = CAN_FILTER_MASK;
                break;

              case CAN_FILTER_RANGE:

                /* Not supported */

                break;
            }

          /* Setup the CONFIG Mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

          if (mode == CAN_FILTER_DUAL)
            {
              /* The MSD IDs will be filtered by separated Mask and Filter */

              /* Setup the Filter */

              /* EID0 - EID7 */

              regval = (uint8_t)(extconfig->xf_id1 & 0xff);
              mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              /* EID8 - EID15 */

              regval = (uint8_t)((extconfig->xf_id1 & 0xff00) >> 8);
              mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              /* EID16 - EID17 */

              regval = (uint8_t)((extconfig->xf_id1 & 0x30000) >> 16);

              /* STD0 - STD2 */

              regval = (regval) |
                       (uint8_t)(((extconfig->xf_id1 &
                                   0x1c0000) >> 16) << 3);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              /* STD3 - STD10 */

              regval = (uint8_t)((extconfig->xf_id1 & 0x1fe00000) >> 21);
              regval |= RXFSIDL_EXIDE;
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              /* Setup the Mask */

              /* EID0 - EID7 */

              regval = (uint8_t)(extconfig->xf_id2 & 0xff);
              mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, &regval, 1);

              /* EID8 - EID15 */

              regval = (uint8_t)((extconfig->xf_id2 & 0xff00) >> 8);
              mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, &regval, 1);

              /* EID16 - EID17 */

              regval = (uint8_t)((extconfig->xf_id2 & 0x30000) >> 16);

              /* STD0 - STD2 */

              regval = (regval) |
                       (uint8_t)(((extconfig->xf_id2 &
                                   0x1c0000) >> 16) << 3);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, &regval, 1);

              /* STD3 - STD10 */

              regval = (uint8_t)((extconfig->xf_id2 & 0x1fe00000) >> 21);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, &regval, 1);
            }
          else
            {
              /* The IDs will be filtered only by the Filter register
               * (Mask == Filter)
               */

              /* Setup the Filter */

              /* EID0 - EID7 */

              regval = (uint8_t)(extconfig->xf_id1 & 0xff);
              mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, &regval, 1);

              /* EID8 - EID15 */

              regval = (uint8_t)((extconfig->xf_id1 & 0xff00) >> 8);
              mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, &regval, 1);

              /* EID16 - EID17 */

              regval = (uint8_t)((extconfig->xf_id1 & 0x30000) >> 16);

              /* STD0 - STD2 */

              regval = (regval) | (uint8_t)(((extconfig->xf_id1 &
                                0x1c0000) >> 16) << 3) | RXFSIDL_EXIDE;
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, &regval, 1);

              /* STD3 - STD10 */

              regval = (uint8_t)((extconfig->xf_id1 & 0x1fe00000) >> 21);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset, &regval, 1);
            }

          /* Leave the Configuration mode, Move to Normal mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

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

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

/****************************************************************************
 * Name: mcp2515_del_extfilter
 *
 * Description:
 *   Remove an address filter for a standard 29 bit address.
 *
 * Input Parameters:
 *   priv - An instance of the MCP2515 driver state structure.
 *   ndx  - The filter index previously returned by the
 *          mcp2515_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 mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx)
{
  FAR struct mcp2515_config_s *config;
  uint8_t regval;
  uint8_t offset;
  int ret;

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

  /* Check Userspace Parameters */

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

  caninfo("ndx = %d\n", ndx);

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

  /* Get exclusive excess to the MCP2515 hardware */

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

  /* Check if this filter is really assigned */

  if ((priv->filters & (1 << ndx)) == 0)
    {
      /* No, error out */

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

  /* Release the filter */

  priv->filters &= ~(1 << ndx);

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

  /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL,
   * RXFnEID8 and RXFnEID0) using this formula:
   *
   * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
   * maskN   = RXM0reg + offset
   */

  if (ndx < 3)
    {
      offset = 0;
    }
  else
    {
      offset = 4;
    }

  /* Setup the CONFIG Mode */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
  regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
  mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

  /* Invalidate this filter, set its ID to 0 */

  regval = 0;
  mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + (ndx * 4), &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + (ndx * 4), &regval, 1);

  /* Leave the Configuration mode, Move to Normal mode */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
  regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
  mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

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

/****************************************************************************
 * Name: mcp2515_add_stdfilter
 *
 * Description:
 *   Add an address filter for a standard 11 bit address.
 *
 * Input Parameters:
 *   priv      - An instance of the MCP2515 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 mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv,
                                 FAR struct canioc_stdfilter_s *stdconfig)
{
  FAR struct mcp2515_config_s *config;
  uint8_t regval;
  uint8_t offset;
  uint8_t mode = CAN_FILTER_MASK;
  int ndx;
  int ret;

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

  /* Get exclusive excess to the MCP2515 hardware */

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

  /* Find an unused standard filter */

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

      if ((priv->filters & (1 << ndx)) == 0)
        {
          /* No, assign the filter */

          DEBUGASSERT(priv->nalloc < priv->config->nfilters);
          priv->filters |= (1 << ndx);
          priv->nalloc++;

          /* Format and write filter */

          DEBUGASSERT(stdconfig->sf_id1 <= CAN_MAX_STDMSGID);

          DEBUGASSERT(stdconfig->sf_id2 <= CAN_MAX_STDMSGID);

          /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL,
           * RXFnEID8 and RXFnEID0) using this formula:
           *
           * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
           * maskN   = RXM0reg + offset
           */

          if (priv->nalloc <= 3)
            {
              offset = 0;
            }
          else
            {
              offset = 4;
            }

#if 0
          /* N.B. Buffer 0 is higher priority than Buffer 1
           * but to separate these messages we will make this
           * driver more complex. So let to consider that the
           * first 2 IDs inserted in the filter will have more
           * priority than the latest 4 IDs.
           */

          if (stdconfig->sf_prio == CAN_MSGPRIO_LOW)
            {
              /* Use RXB1 filters */
            }
          else
            {
              /* Use RXB0 filters */
            }
#endif

          switch (stdconfig->sf_type)
            {
              default:
              case CAN_FILTER_DUAL:
                mode = CAN_FILTER_DUAL;
                break;

              case CAN_FILTER_MASK:
                mode = CAN_FILTER_MASK;
                break;

              case CAN_FILTER_RANGE:

                /* Not supported */

                break;
            }

          /* Setup the CONFIG Mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

          if (mode == CAN_FILTER_DUAL)
            {
              /* The MSD IDs will be filtered by separated Mask and Filter */

              /* Setup the Filter */

              regval = (uint8_t)(((stdconfig->sf_id1) & 0x7f8) >> 3);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              regval = (uint8_t)((stdconfig->sf_id1 & 0x07) << 5);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);

              /* Setup the Mask */

              regval = (uint8_t)(((stdconfig->sf_id2) & 0x7f8) >> 3);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, &regval, 1);

              regval = (uint8_t)((stdconfig->sf_id2 & 0x07) << 5);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, &regval, 1);
            }
          else
            {
              /* The IDs will be filtered only by the Filter register
               * (Mask == Filter)
               */

              /* Setup the Filter */

              regval = (uint8_t) (((stdconfig->sf_id1) & 0x7f8) >> 3);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, &regval, 1);

              regval = (uint8_t)((stdconfig->sf_id1 & 0x07) << 5);
              mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset +
                                ((priv->nalloc - 1) * 4), &regval, 1);
              mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, &regval, 1);
            }

          /* We need to clear the extended ID bits */

          regval = 0;
          mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset +
                            ((priv->nalloc - 1) * 4), &regval, 1);
          mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset +
                            ((priv->nalloc - 1) * 4), &regval, 1);
          mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, &regval, 1);
          mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, &regval, 1);

          /* Leave the Configuration mode, Move to Normal mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

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

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

/****************************************************************************
 * Name: mcp2515_del_stdfilter
 *
 * Description:
 *   Remove an address filter for a standard 29 bit address.
 *
 * Input Parameters:
 *   priv - An instance of the MCP2515 driver state structure.
 *   ndx  - The filter index previously returned by the
 *          mcp2515_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 mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx)
{
  FAR struct mcp2515_config_s *config;
  uint8_t regval;
  uint8_t offset;
  int ret;

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

  /* Check Userspace Parameters */

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

  caninfo("ndx = %d\n", ndx);

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

  /* Get exclusive excess to the MCP2515 hardware */

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

  /* Check if this filter is really assigned */

  if ((priv->filters & (1 << ndx)) == 0)
    {
      /* No, error out */

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

  /* Release the filter */

  priv->filters &= ~(1 << ndx);

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

  /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL,
   * RXFnEID8 and RXFnEID0) using this formula:
   *
   * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ;
   * maskN   = RXM0reg + offset
   */

  if (ndx < 3)
    {
      offset = 0;
    }
  else
    {
      offset = 4;
    }

  /* Setup the CONFIG Mode */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
  regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
  mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

  /* Invalidade this filter, set its ID to 0 */

  regval = 0;
  mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), &regval, 1);

  /* Leave the Configuration mode, Move to Normal mode */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
  regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
  mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

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

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

static void mcp2515_reset_lowlevel(FAR struct mcp2515_can_s *priv)
{
  FAR struct mcp2515_config_s *config;
  int ret;

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

  UNUSED(config);

  /* Get exclusive access to the MCP2515 peripheral */

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

  /* Send SPI reset command to MCP2515 */

  SPI_LOCK(config->spi, true);
  mcp2515_config_spi(priv);
  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true);
  SPI_SEND(config->spi, MCP2515_RESET);
  SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false);
  SPI_LOCK(config->spi, false);

  /* Wait 1ms to let MCP2515 restart */

  nxsig_usleep(1000);

  /* Make sure that all buffers are released. */

  nxsem_reset(&priv->txfsem, MCP2515_NUM_TX_BUFFERS);
  priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1;

  /* Define the current state and unlock */

  priv->state = MCP2515_STATE_RESET;
  nxmutex_unlock(&priv->lock);
}

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

static void mcp2515_reset(FAR struct can_dev_s *dev)
{
  FAR struct mcp2515_can_s *priv;

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

  /* Execute the reset */

  mcp2515_reset_lowlevel(priv);
}

/****************************************************************************
 * Name: mcp2515_setup
 *
 * Description:
 *   Configure the MCP2515. This method is called the first time that the
 *   MCP2515 device is opened.  This will occur when the device file is
 *   first opened.  This setup includes configuring and attaching MCP2515
 *   interrupts.  All MCP2515 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 mcp2515_setup(FAR struct can_dev_s *dev)
{
  FAR struct mcp2515_can_s *priv;
  FAR struct mcp2515_config_s *config;
  int ret;

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

  /* Get exclusive access to the MCP2515 peripheral */

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

  /* MCP2515 hardware initialization */

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

  /* Attach the MCP2515 interrupt handler. */

  ret = config->attach(config, (mcp2515_handler_t)mcp2515_interrupt,
                       (FAR void *)dev);
  if (ret < 0)
    {
      canerr("ERROR: Failed to attach to IRQ Handler!\n");
      return ret;
    }

  /* Enable receive interrupts */

  priv->state = MCP2515_STATE_SETUP;
  mcp2515_rxint(dev, true);

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

/****************************************************************************
 * Name: mcp2515_shutdown
 *
 * Description:
 *   Disable the MCP2515.  This method is called when the MCP2515 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 mcp2515_shutdown(FAR struct can_dev_s *dev)
{
  /* Nothing to do here! */
}

/****************************************************************************
 * Name: mcp2515_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 mcp2515_rxint(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct mcp2515_can_s *priv;
  FAR struct mcp2515_config_s *config;
  irqstate_t flags;

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

  caninfo("CAN%d enable: %d\n", config->devid, enable);
  UNUSED(config);

  /* Enable/disable the receive interrupts */

  flags = enter_critical_section();

  if (enable)
    {
      mcp2515_modifyreg(priv, MCP2515_CANINTE,
                        priv->rxints | MCP2515_ERROR_INTS,
                        priv->rxints | MCP2515_ERROR_INTS);
    }
  else
    {
      mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->rxints, ~priv->rxints);
    }

  leave_critical_section(flags);
}

/****************************************************************************
 * Name: mcp2515_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 mcp2515_txint(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct mcp2515_can_s *priv = dev->cd_priv;
  irqstate_t flags;

  DEBUGASSERT(priv && priv->config);

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

  /* Enable/disable the receive interrupts */

  flags = enter_critical_section();

  if (enable)
    {
      mcp2515_modifyreg(priv, MCP2515_CANINTE,
                        priv->txints | MCP2515_ERROR_INTS,
                        priv->txints | MCP2515_ERROR_INTS);
    }
  else
    {
      mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->txints, ~priv->txints);
    }

  leave_critical_section(flags);
}

/****************************************************************************
 * Name: mcp2515_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 mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd,
                         unsigned long arg)
{
  FAR struct mcp2515_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:
        {
          FAR struct canioc_bittiming_s *bt =
            (FAR struct canioc_bittiming_s *)arg;
          uint8_t regval;
          uint8_t brp;

          DEBUGASSERT(bt != NULL);

          mcp2515_readregs(priv, MCP2515_CNF1, &regval, 1);
          bt->bt_sjw    = ((regval & CNF1_SJW_MASK) >> CNF1_SJW_SHIFT) + 1;
          brp           = (((regval & CNF1_BRP_MASK) >>
                                      CNF1_BRP_SHIFT) + 1) * 2;

          mcp2515_readregs(priv, MCP2515_CNF2, &regval, 1);
          bt->bt_tseg1  = ((regval & CNF2_PRSEG_MASK) >>
                                     CNF2_PRSEG_SHIFT) + 1;
          bt->bt_tseg1 += ((regval & CNF2_PHSEG1_MASK) >>
                                     CNF2_PHSEG1_SHIFT) + 1;

          mcp2515_readregs(priv, MCP2515_CNF3, &regval, 1);
          bt->bt_tseg2  = ((regval & CNF3_PHSEG2_MASK) >>
                                     CNF3_PHSEG2_SHIFT) + 1;

          bt->bt_baud   = MCP2515_CANCLK_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 MCP2515 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:
        {
          FAR const struct canioc_bittiming_s *bt =
            (FAR const struct canioc_bittiming_s *)arg;
          irqstate_t flags;
          uint8_t brp;
          uint8_t sjw;
          uint8_t tseg1;
          uint8_t tseg2;
          uint8_t prseg;
          uint8_t phseg1;
          uint8_t regval;

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

          /* Extract bit timing data */

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

          /* PRSEG = TSEG1 - PHSEG1
           * Because we don't have PHSEG1 then let us to assume:
           * PHSEG1 == PHSEG2 (PHSEG2 = TSEG2)
           *
           * See more at:
           *  http://www.analog.com/en/analog-dialogue/articles/
           *          configure-can-bit-timing.html
           *
           */

          phseg1 = tseg2;
          prseg  = tseg1 - phseg1;

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

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

          flags = enter_critical_section();

          /* Setup the CONFIG Mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

          /* Setup CNF1 register */

          mcp2515_readregs(priv, MCP2515_CNF1, &regval, 1);
          regval = (regval & ~CNF1_BRP_MASK) |
                   (brp << CNF1_BRP_SHIFT);
          regval = (regval & ~CNF1_SJW_MASK) |
                   ((sjw) << CNF1_SJW_SHIFT);
          mcp2515_writeregs(priv, MCP2515_CNF1, &regval, 1);

          /* Setup CNF2 register */

          mcp2515_readregs(priv, MCP2515_CNF2, &regval, 1);
          regval = (regval & ~CNF2_PRSEG_MASK) |
                   ((prseg - 1) << CNF2_PRSEG_SHIFT);
          regval = (regval & ~CNF2_PHSEG1_MASK) |
                   (phseg1 << CNF2_PHSEG1_SHIFT);
          regval = (regval | CNF2_SAM | CNF2_BTLMODE);
          mcp2515_writeregs(priv, MCP2515_CNF2, &regval, 1);

          /* Setup CNF3 register */

          mcp2515_readregs(priv, MCP2515_CNF3, &regval, 1);
          regval = (regval & ~CNF3_PHSEG2_MASK) |
                   (tseg2 << CNF3_PHSEG2_SHIFT);
          regval = (regval | CNF3_SOF);
          mcp2515_writeregs(priv, MCP2515_CNF3, &regval, 1);

          /* Leave the Configuration mode, Move to Normal mode */

          mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
          regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
          mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

          leave_critical_section(flags);

          ret = OK;
        }
        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 = mcp2515_add_extfilter(priv,
                                      (FAR 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:
        {
          FAR int *ndx = (FAR int *)((uintptr_t)arg);

          DEBUGASSERT(*ndx <= priv->config->nfilters);
          ret = mcp2515_del_extfilter(priv, (int)*ndx);
        }
        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 = mcp2515_add_stdfilter(priv,
                                      (FAR 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:
        {
          FAR int *ndx = (FAR int *)((uintptr_t)arg);

          DEBUGASSERT(*ndx <= priv->config->nfilters);
          ret = mcp2515_del_stdfilter(priv, (int)*ndx);
        }
        break;

      /* Unsupported/unrecognized command */

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

  return ret;
}

/****************************************************************************
 * Name: mcp2515_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 mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id)
{
  /* REVISIT:  Remote request not implemented */

  return -ENOSYS;
}

/****************************************************************************
 * Name: mcp2515_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 mcp2515_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg)
{
  FAR struct mcp2515_can_s *priv;
  FAR struct mcp2515_config_s *config;
  uint8_t regval;
  uint8_t txbuf;
  unsigned int nbytes;
  int ret;

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

  caninfo("CAN%d\n", config->devid);
  caninfo("CAN%d ID: %" PRId32 " DLC: %d\n",
          config->devid, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
  UNUSED(config);

  /* Get exclusive access to the MCP2515 peripheral */

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

  /* Acquire buffer */

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

  DEBUGASSERT(priv->txbuffers != 0);

  txbuf = ffs(priv->txbuffers) - 1;
  priv->txbuffers &= ~(1 << txbuf);

  /* Select one empty transmit buffer */

  /* Setup the MCP2515 TX Buffer with the message to send */

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

      /* EID7 - EID0 */

      regval = (msg->cm_hdr.ch_id & 0xff);
      TXREGVAL(MCP2515_TXB0EID0) = regval;

      /* EID15 - EID8 */

      regval = (msg->cm_hdr.ch_id & 0xff00) >> 8;
      TXREGVAL(MCP2515_TXB0EID8) = regval;

      /* EID17 and EID16 */

      regval  = (msg->cm_hdr.ch_id & 0x30000) >> 16;
      regval |= TXBSIDL_EXIDE;

      /* STD2 - STD0 */

      regval |= (msg->cm_hdr.ch_id & 0x1c0000) >> 13;
      TXREGVAL(MCP2515_TXB0SIDL) = regval;

      /* STD10 - STD3 */

      regval  = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21;
      TXREGVAL(MCP2515_TXB0SIDH) = regval;
    }
  else
#endif
    {
      DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID);

      /* Setup the Standard ID of the message to send */

      /* STD10 - STD3 */

      regval  = (msg->cm_hdr.ch_id & 0x7f8) >> 3;
      TXREGVAL(MCP2515_TXB0SIDH) = regval;

      /* STD2 - STD0 */

      regval  = (msg->cm_hdr.ch_id & 0x007) << 5;
      TXREGVAL(MCP2515_TXB0SIDL) = regval;
    }

  /* Setup the DLC */

  regval = (msg->cm_hdr.ch_dlc & 0xf);

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

  TXREGVAL(MCP2515_TXB0DLC) = regval;

  /* Fill the data buffer */

  nbytes = msg->cm_hdr.ch_dlc;

  memcpy(&TXREGVAL(MCP2515_TXB0D0), msg->cm_data, nbytes);

  TXREGVAL(MCP2515_TXB0CTRL) = MCP2515_LOAD_TXB(txbuf);

  mcp2515_transfer(priv, SPI_TRANSFER_BUF_LEN);

  /* Request to send */

  priv->spi_txbuf[0] = MCP2515_RTS(txbuf);
  mcp2515_transfer(priv, 1);

  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: mcp2515_txready
 *
 * Description:
 *   Return true if the MCP2515 hardware can accept another TX message.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   True if the MCP2515 hardware is ready to accept another TX message.
 *
 ****************************************************************************/

static bool mcp2515_txready(FAR struct can_dev_s *dev)
{
  FAR struct mcp2515_can_s *priv;

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

  return priv->txbuffers != 0;
}

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

static bool mcp2515_txempty(FAR struct can_dev_s *dev)
{
  FAR struct mcp2515_can_s *priv;

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

  return priv->txbuffers == (1 << MCP2515_NUM_TX_BUFFERS) - 1;
}

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

#ifdef CONFIG_CAN_ERRORS
static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status,
                       uint8_t oldstatus)
{
  FAR struct mcp2515_can_s *priv = dev->cd_priv;
  struct can_hdr_s hdr;
  uint8_t eflg;
  uint8_t txerr;
  uint8_t txb0err;
  uint8_t txb1err;
  uint8_t txb2err;
  uint16_t errbits;
  uint8_t data[CAN_ERROR_DLC];
  int ret;

  /* Encode error bits */

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

  /* Please note that MCP2515_CANINTF only reports if an error
   * happened. It doesn't report what error it is.
   * We need to check EFLG and TXBnCTRL to discover.
   */

  mcp2515_readregs(priv, MCP2515_EFLG, &eflg, 1);
  if (eflg & EFLG_TXBO)
    {
      errbits |= CAN_ERROR_BUSOFF;
    }

  if (eflg & EFLG_RXEP)
    {
      data[1] |= CAN_ERROR1_RXPASSIVE;
    }

  if (eflg & EFLG_TXEP)
    {
      data[1] |= CAN_ERROR1_TXPASSIVE;
    }

  if (eflg & EFLG_RXWAR)
    {
      data[1] |= CAN_ERROR1_RXWARNING;
    }

  if (eflg & EFLG_TXWAR)
    {
      data[1] |= CAN_ERROR1_TXWARNING;
    }

  if (eflg & (EFLG_RX0OVR | EFLG_RX1OVR))
    {
      data[1] |= CAN_ERROR1_RXOVERFLOW;
    }

  /* Verify Message Error */

  mcp2515_readregs(priv, MCP2515_TXB0CTRL, &txb0err, 1);
  mcp2515_readregs(priv, MCP2515_TXB1CTRL, &txb1err, 1);
  mcp2515_readregs(priv, MCP2515_TXB2CTRL, &txb2err, 1);

  txerr = txb0err | txb1err | txb2err;

  if (txerr & (TXBCTRL_MLOA))
    {
      errbits |= CAN_ERROR_LOSTARB;
    }

  if (txerr & (TXBCTRL_ABTF))
    {
      errbits |= CAN_ERROR_LOSTARB;
    }

  if (txerr & (TXBCTRL_MLOA))
    {
      data[0] |= CAN_ERROR0_UNSPEC;
    }

  if ((status & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0)
    {
      /* If Message Error or Other error */

      errbits |= CAN_ERROR_CONTROLLER;
    }
  else if ((oldstatus & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0)
    {
      errbits |= CAN_ERROR_CONTROLLER;
    }

  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: mcp2515_receive
 *
 * Description:
 *   Receive an MCP2515 messages
 *
 * Input Parameters:
 *   dev      - CAN-common state data
 *   rxbuffer - The RX buffer containing the received messages
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#define RXREGVAL(reg) priv->spi_rxbuf[reg-MCP2515_RXB0CTRL]

static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset)
{
  FAR struct mcp2515_can_s *priv;
  struct can_hdr_s hdr;
  int ret;
  uint8_t regval;

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

  mcp2515_readregs(priv, MCP2515_RXB0CTRL + offset, priv->spi_rxbuf,
                   SPI_TRANSFER_BUF_LEN);

  regval = RXREGVAL(MCP2515_RXB0SIDL);

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

      /* EID7 - EID0 */

      regval     = RXREGVAL(MCP2515_RXB0EID0);
      hdr.ch_id  = regval ;

      /* EID15 - EID8 */

      regval     = RXREGVAL(MCP2515_RXB0EID8);
      hdr.ch_id  = hdr.ch_id | (regval << 8);

      /* EID17 and EID16 */

      regval     = RXREGVAL(MCP2515_RXB0SIDL);
      hdr.ch_id  = hdr.ch_id | ((regval & RXBSIDL_EID_MASK) << 16);

      /* STD2 - STD0 */

      hdr.ch_id  = hdr.ch_id | ((regval >> 5) << 18);

      /* STD10 - STD3 */

      regval     = RXREGVAL(MCP2515_RXB0SIDH);
      hdr.ch_id  = hdr.ch_id | (regval << 21);

      hdr.ch_extid = true;
    }
  else
    {
      /* Save the standard ID of the newly received message */

      regval = RXREGVAL(MCP2515_RXB0SIDH);
      hdr.ch_id = regval;

      regval = RXREGVAL(MCP2515_RXB0SIDL);
      hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5);
      hdr.ch_extid = false;
    }

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

      canerr("ERROR: Extended MSG in Standard Mode\n");

      return;
    }

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

  regval    = RXREGVAL(MCP2515_RXB0SIDH);
  hdr.ch_id = regval;
  regval    = RXREGVAL(MCP2515_RXB0SIDL);
  hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5);
#endif

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

  /* Extract the RTR bit */

  regval = RXREGVAL(MCP2515_RXB0CTRL);
  hdr.ch_rtr = (regval & RXBCTRL_RXRTR) != 0;

  /* Get the DLC */

  regval = RXREGVAL(MCP2515_RXB0DLC);
  hdr.ch_dlc = (regval & RXBDLC_DLC_MASK) >> RXBDLC_DLC_SHIFT;

  /* Save the message data */

  ret = can_receive(dev, &hdr, (FAR uint8_t *)&RXREGVAL(MCP2515_RXB0D0));

  if (ret < 0)
    {
      canerr("ERROR: can_receive failed: %d\n", ret);
    }
}

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

static int mcp2515_interrupt(FAR struct mcp2515_config_s *config,
                             FAR void *arg)
{
  FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg;
  FAR struct mcp2515_can_s *priv;
  uint8_t ir;
  uint8_t ie;
  uint8_t pending;
  uint8_t clrmask;
  bool handled;

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

  /* Loop while there are pending interrupt events */

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

      mcp2515_read_2regs(priv, MCP2515_CANINTE, &ie, &ir);

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

      if (pending == 0)
        {
          return OK;
        }

      /* Check for any errors */

      if ((pending & MCP2515_ERROR_INTS) != 0)
        {
          /* Clear interrupt errors */

          pending &= ~MCP2515_ERROR_INTS;
          clrmask |= MCP2515_ERROR_INTS;

#ifdef CONFIG_CAN_ERRORS
          /* Report errors */

          mcp2515_error(dev, pending & MCP2515_ERROR_INTS, priv->olderrors);

          priv->olderrors = (pending & MCP2515_ERROR_INTS);
#endif
          handled = true;
        }
#ifdef CONFIG_CAN_ERRORS
      else if (priv->olderrors != 0)
        {
          /* All (old) errors cleared  */

          canerr("ERROR: CLEARED\n");

          mcp2515_error(dev, 0, priv->olderrors);

          priv->olderrors = 0;
          handled = true;
        }
#endif

      /* Check for successful completion of a transmission */

      if ((pending & MCP2515_TXBUFFER_INTS) != 0)
        {
          /* Clear the pending TX completion interrupt (and all
           * other TX-related interrupts)
           */

          if (pending & MCP2515_INT_TX0)
            {
              caninfo("TX0 is empty to transmit new message!\n");

              /* Clear TX0 interrupt */

              priv->txbuffers |= (1 << 0);
              nxsem_post(&priv->txfsem);
              pending &= ~MCP2515_INT_TX0;
              clrmask |= MCP2515_INT_TX0;
            }

          if (pending & MCP2515_INT_TX1)
            {
              caninfo("TX1 is empty to transmit new message!\n");

              priv->txbuffers |= (1 << 1);
              nxsem_post(&priv->txfsem);

              /* Clear TX1 interrupt */

              pending &= ~MCP2515_INT_TX1;
              clrmask |= MCP2515_INT_TX1;
            }

          if (pending & MCP2515_INT_TX2)
            {
              caninfo("TX2 is empty to transmit new message!\n");

              priv->txbuffers |= (1 << 2);
              nxsem_post(&priv->txfsem);

              /* Clear TX2 interrupt */

              pending &= ~MCP2515_INT_TX2;
              clrmask |= MCP2515_INT_TX2;
            }

          handled = true;

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

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

          handled = true;
        }

      /* Check if there is a new message to read */

      if ((pending & MCP2515_RXBUFFER_INTS) != 0)
        {
          /* RX Buffer 0 is the "high priority" buffer:  We will process
           * all messages in RXB0 before processing any message from RX
           * RXB1.
           */

          if ((pending & MCP2515_INT_RX0) != 0)
            {
              mcp2515_receive(dev, MCP2515_RX0_OFFSET);

              /* Clear RX0 interrupt */

              pending &= ~MCP2515_INT_RX0;
              clrmask |= MCP2515_INT_RX0;
            }
          else
            {
              if ((pending & MCP2515_INT_RX1) != 0)
                {
                  mcp2515_receive(dev, MCP2515_RX1_OFFSET);

                  /* Clear RX1 interrupt */

                  pending &= ~MCP2515_INT_RX1;
                  clrmask |= MCP2515_INT_RX1;
                }
            }

          /* Acknowledge reading the FIFO entry */

          handled = true;
        }

      mcp2515_modifyreg(priv, MCP2515_CANINTF, clrmask, pending);
    }
  while (handled);

  return OK;
}

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

static int mcp2515_hw_initialize(struct mcp2515_can_s *priv)
{
  FAR struct mcp2515_config_s *config = priv->config;
  uint8_t regval;

  caninfo("CAN%d\n", config->devid);
  UNUSED(config);

  /* Setup CNF1 register */

  mcp2515_readregs(priv, MCP2515_CNF1, &regval, 1);
  regval = (regval & ~CNF1_BRP_MASK) |
           (MCP2515_BRP << CNF1_BRP_SHIFT);
  regval = (regval & ~CNF1_SJW_MASK) |
           ((MCP2515_SJW - 1) << CNF1_SJW_SHIFT);
  mcp2515_writeregs(priv, MCP2515_CNF1, &regval, 1);

  /* Setup CNF2 register */

  mcp2515_readregs(priv, MCP2515_CNF2, &regval, 1);
  regval = (regval & ~CNF2_PRSEG_MASK) |
           ((MCP2515_PROPSEG - 1) << CNF2_PRSEG_SHIFT);
  regval = (regval & ~CNF2_PHSEG1_MASK) |
           ((MCP2515_PHSEG1 - 1) << CNF2_PHSEG1_SHIFT);
  regval = (regval | CNF2_SAM | CNF2_BTLMODE);
  mcp2515_writeregs(priv, MCP2515_CNF2, &regval, 1);

  /* Setup CNF3 register */

  mcp2515_readregs(priv, MCP2515_CNF3, &regval, 1);
  regval = (regval & ~CNF3_PHSEG2_MASK) |
           ((MCP2515_PHSEG2 - 1) << CNF3_PHSEG2_SHIFT);
  regval = (regval | CNF3_SOF);
  mcp2515_writeregs(priv, MCP2515_CNF3, &regval, 1);

  /* Mask all messages to be received */

  mcp2515_readregs(priv, MCP2515_RXB0CTRL, &regval, 1);
  regval = (regval & ~RXBCTRL_RXM_MASK) |
           (RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT);
  regval = (regval | RXB0CTRL_BUKT); /* Enable Rollover from RXB0 to RXB1 */
  mcp2515_writeregs(priv, MCP2515_RXB0CTRL, &regval, 1);

  mcp2515_readregs(priv, MCP2515_RXB1CTRL, &regval, 1);
  regval = (regval & ~RXBCTRL_RXM_MASK) |
           (RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT);
  mcp2515_writeregs(priv, MCP2515_RXB1CTRL, &regval, 1);

  regval = 0x00;
  mcp2515_writeregs(priv, MCP2515_RXM0SIDH, &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXM0SIDL, &regval, 1);
#ifdef CONFIG_CAN_EXTID
  mcp2515_writeregs(priv, MCP2515_RXM0EID8, &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXM0EID0, &regval, 1);
#endif

  mcp2515_writeregs(priv, MCP2515_RXM1SIDH, &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXM1SIDL, &regval, 1);
#ifdef CONFIG_CAN_EXTID
  mcp2515_writeregs(priv, MCP2515_RXM1EID8, &regval, 1);
  mcp2515_writeregs(priv, MCP2515_RXM1EID0, &regval, 1);
#endif

#ifdef CONFIG_CAN_EXTID
  mcp2515_modifyreg(priv, MCP2515_RXM0SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE);
  mcp2515_modifyreg(priv, MCP2515_RXM1SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE);
#endif

  /* Leave the Configuration mode, Move to Normal mode */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &regval, 1);
  regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL);
  mcp2515_writeregs(priv, MCP2515_CANCTRL, &regval, 1);

  nxsig_usleep(100);

  /* Read the CANINTF */

  mcp2515_readregs(priv, MCP2515_CANINTF, &regval, 1);
  caninfo("CANINFT = 0x%02X\n", regval);

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

  if (config->loopback)
    {
      /* To Be Implemented */
    }
#endif

  /* Configure interrupt lines */

  /* Select RX-related interrupts */

  priv->rxints = MCP2515_RXBUFFER_INTS;

  /* Select TX-related interrupts */

  priv->txints = MCP2515_TXBUFFER_INTS;

  /* In this option we set a special receive mode in the
   * RXM[1:0] bits (RXBnCTRL[6:5]). In both registers:
   * RXB0CTRL and RXB1CTRL.
   * 11 = Turns mask/filters off; receives any message.
   *
   * In this mode it is tested that it receives both
   * extended and standard id messages.
   */

#ifdef CONFIG_CAN_EXTID
  mcp2515_readregs(priv, MCP2515_RXB0CTRL, &regval, 1);
  regval &= ~RXBCTRL_RXM_ALLMSG;
  regval |= RXBCTRL_RXM_ALLMSG;
  mcp2515_writeregs(priv, MCP2515_RXB0CTRL, &regval, 1);

  mcp2515_readregs(priv, MCP2515_RXB1CTRL, &regval, 1);
  regval &= ~RXBCTRL_RXM_ALLMSG;
  regval |= RXBCTRL_RXM_ALLMSG;
  mcp2515_writeregs(priv, MCP2515_RXB1CTRL, &regval, 1);
#endif

  return OK;
}

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

/****************************************************************************
 * Name: mcp2515_instantiate
 *
 * Description:
 *   Initialize the selected MCP2515 CAN Bus Controller over SPI
 *
 * Input Parameters:
 *   config - The configuration structure passed by the board.
 *
 * Returned Value:
 *   Valid CAN device structure reference on success; a NULL on failure
 *
 ****************************************************************************/

FAR struct mcp2515_can_s *
  mcp2515_instantiate(FAR struct mcp2515_config_s *config)
{
  FAR struct mcp2515_can_s *priv;
  uint8_t  canctrl;

  caninfo("Starting mcp2515_instantiate()!\n");

  priv = (FAR struct mcp2515_can_s *)
    kmm_malloc(sizeof(struct mcp2515_can_s));
  if (priv == NULL)
    {
      canerr("ERROR: Failed to allocate instance of mcp2515_can_s!\n");
      return NULL;
    }

  /* Perform one time data initialization */

  memset(priv, 0, sizeof(struct mcp2515_can_s));
  priv->config = config;

  priv->spi_txbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN);
  priv->spi_rxbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN);

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

  /* Initialize mutex & semaphores */

  nxmutex_init(&priv->lock);
  nxsem_init(&priv->txfsem, 0, MCP2515_NUM_TX_BUFFERS);

  /* Initialize bitmask */

  priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1;

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

  mcp2515_reset_lowlevel(priv);

  /* Probe the MCP2515 to confirm it was detected */

  mcp2515_readregs(priv, MCP2515_CANCTRL, &canctrl, 1);

  if (canctrl != DEFAULT_CANCTRL_CONFMODE)
    {
      canerr("ERROR: CANCTRL = 0x%02X ! It should be 0x87\n", canctrl);
      nxmutex_destroy(&priv->lock);
      kmm_free(priv);
      return NULL;
    }

  /* Return our private data structure as an opaque handle */

  return priv;
}

/****************************************************************************
 * Name: mcp2515_initialize
 *
 * Description:
 *   Initialize the selected MCP2515 CAN Bus Controller over SPI
 *
 * Input Parameters:
 *   config - The configuration structure passed by the board.
 *
 * Returned Value:
 *   Valid CAN device structure reference on success; a NULL on failure
 *
 ****************************************************************************/

FAR struct can_dev_s *mcp2515_initialize(
    FAR struct mcp2515_can_s *mcp2515can)
{
  FAR struct can_dev_s *dev;

  caninfo("Starting mcp2515_initialize()!\n");

  /* Allocate a CAN Device structure */

  dev = kmm_zalloc(sizeof(struct can_dev_s));
  if (dev == NULL)
    {
      canerr("ERROR: Failed to allocate instance of can_dev_s!\n");
      return NULL;
    }

  dev->cd_ops  = &g_mcp2515ops;
  dev->cd_priv = (FAR void *)mcp2515can;

  return dev;
}

#endif /* CONFIG_CAN && CONFIG_MCP2515 */