/****************************************************************************
 * arch/arm/src/lpc43xx/lpc43_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 <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 "lpc43_gpio.h"
#include "hardware/lpc43_can.h"
#include "hardware/lpc43_rgu.h"
#include "lpc43_ccu.h"
#include "lpc43_cgu.h"

#if defined(CONFIG_LPC43_CAN0) || defined(CONFIG_LPC43_CAN1)

#define CAN_MSG_OBJECTS_NUM    0x20
#define CAN_RX_OBJ_NUM         0x10
#define CAN_TX_OBJ_NUM         0x10

#define CAN_RX_OBJ_FIRST       0x01
#define CAN_RX_OBJ_LAST         (CAN_RX_OBJ_FIRST + CAN_RX_OBJ_NUM - 0x01)

#define CAN_TX_OBJ_FIRST       (CAN_RX_OBJ_LAST + 0x01)
#define CAN_TX_OBJ_LAST         (CAN_TX_OBJ_FIRST + CAN_TX_OBJ_NUM - 0x01)

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

/* Configuration ************************************************************/

#ifdef CONFIG_LPC43_CAN0

/* A CAN bit rate must be provided */

#  ifndef CONFIG_LPC43_CAN0_BAUD
#    define CONFIG_LPC43_CAN0_BAUD 1000000
#   endif
#endif

#ifdef CONFIG_LPC43_CAN1

/* A CAN bit rate must be provided */

#  ifndef CONFIG_LPC43_CAN1_BAUD
#    define CONFIG_LPC43_CAN1_BAUD 1000000
#  endif
#endif

/* User-defined TSEG1 and TSEG2 settings may be used.
 *
 * CONFIG_LPC43_CAN_TSEG1 = the number of CAN time quanta in segment 1
 * CONFIG_LPC43_CAN_TSEG2 = the number of CAN time quanta in segment 2
 * CAN_BIT_QUANTA   = The number of CAN time quanta in on bit time
 */

#ifndef CONFIG_LPC43_CAN_TSEG1
#  define CONFIG_LPC43_CAN_TSEG1 12
#endif

#ifndef CONFIG_LPC43_CAN_TSEG2
#  define CONFIG_LPC43_CAN_TSEG2 4
#endif

#define CAN_BIT_QUANTA (CONFIG_LPC43_CAN_TSEG1 + CONFIG_LPC43_CAN_TSEG2 + 1)

/* Timing *******************************************************************/

/* CAN clock source is defined in board.h */

#define CAN_CLOCK_FREQUENCY(c) ((uint32_t)LPC43_CCLK / ((uint32_t)(c)))

/* CAN module clock must be less then 50 MHz */

#define CAN_CLKDIVVAL  0x05

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

struct up_dev_s
{
  uint8_t port;   /* CAN port number */
  uint8_t clkdiv; /* CLKDIV register */
  uint32_t baud;  /* Configured baud */
  uint32_t base;  /* CAN register base address */
  uint8_t irq;    /* IRQ associated with this CAN */
};

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

/* CAN Register access */

#ifdef CONFIG_LPC43_CAN_REGDEBUG
static void can_printreg(uint32_t addr, uint32_t value);
#endif

static uint32_t can_getreg(struct up_dev_s *priv, int offset);
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value);

#ifdef CONFIG_LPC43_CAN_REGDEBUG
static uint32_t can_getcommon(uint32_t addr);
static void can_putcommon(uint32_t addr, uint32_t value);
#else
#  define can_getcommon(addr)        getreg32(addr)
#  define can_putcommon(addr, value) putreg32(value, addr)
#endif

/* CAN methods */

static void can_reset(struct can_dev_s *dev);
static int can_setup(struct can_dev_s *dev);
static void can_shutdown(struct can_dev_s *dev);
static void can_rxint(struct can_dev_s *dev, bool enable);
static void can_txint(struct can_dev_s *dev, bool enable);
static int can_ioctl(struct can_dev_s *dev, int cmd, unsigned long arg);
static int can_remoterequest(struct can_dev_s *dev, uint16_t id);
static int can_send(struct can_dev_s *dev, struct can_msg_s *msg);
static bool candev_txready(struct can_dev_s *dev);
static bool candev_txempty(struct can_dev_s *dev);

/* CAN interrupts */

#ifdef CONFIG_LPC43_CAN0
static int can0_interrupt(int irq, void *context, void *arg);
#endif
#ifdef CONFIG_LPC43_CAN1
static int can1_interrupt(int irq, void *context, void *arg);
#endif
static void can_interrupt(struct can_dev_s *dev);

/* Message Processing */

static void can_savemsg(struct up_dev_s *priv, struct can_hdr_s *hdr,
                        uint32_t *data);
static void can_readobj(struct up_dev_s *priv, uint32_t index);
static void can_invalobj(struct up_dev_s *priv, uint32_t index);
static void can_setuprxobj(struct up_dev_s *priv);

/* Initialization */

static int can_bittiming(struct up_dev_s *priv);

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

static const struct can_ops_s g_canops =
{
  .co_reset         = can_reset,
  .co_setup         = can_setup,
  .co_shutdown      = can_shutdown,
  .co_rxint         = can_rxint,
  .co_txint         = can_txint,
  .co_ioctl         = can_ioctl,
  .co_remoterequest = can_remoterequest,
  .co_send          = can_send,
  .co_txready       = candev_txready,
  .co_txempty       = candev_txempty,
};

#ifdef CONFIG_LPC43_CAN0
static struct up_dev_s g_can0priv =
{
  .port   = 0,
  .clkdiv = CAN_CLKDIVVAL + 1,
  .baud   = CONFIG_LPC43_CAN0_BAUD,
  .base   = LPC43_CAN0_BASE,
  .irq    = LPC43M4_IRQ_CAN0,
};

static struct can_dev_s g_can0dev =
{
  .cd_ops  = &g_canops,
  .cd_priv = &g_can0priv,
};
#endif

#ifdef CONFIG_LPC43_CAN1
static struct up_dev_s g_can1priv =
{
  .port   = 1,
  .clkdiv = CAN_CLKDIVVAL + 1,
  .baud   = CONFIG_LPC43_CAN1_BAUD,
  .base   = LPC43_CAN1_BASE,
  .irq    = LPC43M4_IRQ_CAN1,
};

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

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

/****************************************************************************
 * Name: can_printreg
 *
 * Description:
 *   Print the value read from a register.
 *
 * Input Parameters:
 *   addr - The register address
 *   value - The register value
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_LPC43_CAN_REGDEBUG
static void can_printreg(uint32_t addr, uint32_t value)
{
  static uint32_t prevaddr = 0;
  static uint32_t preval = 0;
  static uint32_t count = 0;

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

          return;
        }
    }

  /* 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 %d more times]\n", count - 3);
        }

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

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

  /* Show the register value read */

  caninfo("%08x->%08x\n", addr, value);
}
#endif /* CONFIG_LPC43_CAN_REGDEBUG */

/****************************************************************************
 * Name: can_getreg
 *
 * Description:
 *   Read the value of an CAN1/2 register.
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *   offset - The offset to the register to read
 *
 * Returned Value:
 *
 ****************************************************************************/

#ifdef CONFIG_LPC43_CAN_REGDEBUG
static uint32_t can_getreg(struct up_dev_s *priv, int offset)
{
  uint32_t addr;
  uint32_t value;

  /* Read the value from the register */

  addr = priv->base + offset;
  value = getreg32(addr);
  can_printreg(addr, value);
  return value;
}
#else
static uint32_t can_getreg(struct up_dev_s *priv, int offset)
{
  return getreg32(priv->base + offset);
}
#endif /* CONFIG_LPC43_CAN_REGDEBUG */

/****************************************************************************
 * Name: can_putreg
 *
 * Description:
 *   Set the value of an CAN1/2 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_LPC43_CAN_REGDEBUG
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value)
{
  uint32_t addr = priv->base + offset;

  /* Show the register value being written */

  caninfo("%08x<-%08x\n", addr, value);

  /* Write the value */

  putreg32(value, addr);
}
#else
static void can_putreg(struct up_dev_s *priv, int offset, uint32_t value)
{
  putreg32(value, priv->base + offset);
}
#endif /* CONFIG_LPC43_CAN_REGDEBUG */

/****************************************************************************
 * Name: can_getcommon
 *
 * Description:
 *   Get the value of common register.
 *
 * Input Parameters:
 *   addr - The address of the register to read
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

#ifdef CONFIG_LPC43_CAN_REGDEBUG
static uint32_t can_getcommon(uint32_t addr)
{
  uint32_t value;

  /* Read the value from the register */

  value = getreg32(addr);
  can_printreg(addr, value);
  return value;
}
#endif /* CONFIG_LPC43_CAN_REGDEBUG */

/****************************************************************************
 * Name: can_putcommon
 *
 * Description:
 *   Set the value of common register.
 *
 * Input Parameters:
 *   addr - The address of the register to write
 *   value - The value to write to the register
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

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

  caninfo("%08x<-%08x\n", addr, value);

  /* Write the value */

  putreg32(value, addr);
}
#endif /* CONFIG_LPC43_CAN_REGDEBUG */

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

static void can_reset(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  uint32_t regval;

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

#ifdef CONFIG_LPC43_CAN0
  if (priv->port == 0)
    {
      regval = ~(getreg32(LPC43_RGU_ACTIVE1));
      regval |= RGU_CTRL1_CAN0_RST;
      putreg32(regval, LPC43_RGU_CTRL1);
    }
#endif /* CONFIG_LPC43_CAN0 */

#ifdef CONFIG_LPC43_CAN1
  if (priv->port == 1)
    {
      regval = ~(getreg32(LPC43_RGU_ACTIVE1));
      regval |= RGU_CTRL1_CAN1_RST;
      putreg32(regval, LPC43_RGU_CTRL1);
    }
#endif /* CONFIG_LPC43_CAN1 */
}

/****************************************************************************
 * Name: can_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 can_setup(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  uint32_t regval;
  uint8_t i;
  int ret = ERROR;

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

  can_bittiming(priv);

  /* Clock must be divided by the CAN clock divider CLKDIV to be less than
   * 50 MHz.
   */

  can_putreg(priv, LPC43_CAN_CLKDIV_OFFSET, CAN_CLKDIVVAL);

  /* Invalidate all message objects */

  for (i = 1; i <= CAN_MSG_OBJECTS_NUM; ++i)
    {
      can_invalobj(priv, i);
    }

  /* Initialization finished, normal operation now. */

  regval  = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
  regval &= ~CAN_CNTL_INIT;
  can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);

  while (can_getreg(priv, LPC43_CAN_CNTL_OFFSET) & CAN_CNTL_INIT);

#ifdef CONFIG_LPC43_CAN0
  if (priv->irq == LPC43M4_IRQ_CAN0)
    {
      ret = irq_attach(priv->irq, can0_interrupt, 0);
    }
#endif

#ifdef CONFIG_LPC43_CAN1
  if (priv->irq == LPC43M4_IRQ_CAN1)
    {
      ret = irq_attach(priv->irq, can1_interrupt, 0);
    }
#endif

  if (ret == OK)
    {
      up_enable_irq(priv->irq);

      /* Enable CAN interrupts within CAN module */

      regval = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
      regval |= (CAN_CNTL_IE | CAN_CNTL_SIE | CAN_CNTL_EIE);
      can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);
    }

  return ret;
}

/****************************************************************************
 * Name: can_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 can_shutdown(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  uint32_t regval;

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

  /* Stop operation mode */

  regval = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
  regval |= CAN_CNTL_INIT;
  can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);

  /* Disable CAN interrupts */

  regval = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
  regval &= ~(CAN_CNTL_IE | CAN_CNTL_SIE | CAN_CNTL_EIE);
  can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);

  up_disable_irq(priv->irq);

  /* Then detach the CAN interrupt handler. */

  irq_detach(priv->irq);
}

/****************************************************************************
 * Name: can_rxint
 *
 * Description:
 *   Call to enable or disable RX interrupts.
 *   This function is called two times: from can_open and can_close.
 *   Therefore this function enables and disables not only RX interrupts
 *   but all message objects.
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void can_rxint(struct can_dev_s *dev, bool enable)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;

  if (enable == true)
    {
      can_setuprxobj(priv);
    }
  else
    {
      uint8_t i = 0;
      for (i = CAN_RX_OBJ_FIRST; i <= CAN_RX_OBJ_LAST; ++i)
        {
          can_invalobj(priv, i);
        }
    }
}

/****************************************************************************
 * Name: can_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 can_txint(struct can_dev_s *dev, bool enable)
{
  /* The TX interrupt is automatically enabled in can_send within a
   * message object.
   */
}

/****************************************************************************
 * Name: can_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 can_ioctl(struct can_dev_s *dev, int cmd, unsigned long arg)
{
  caninfo("Fix me:Not Implemented\n");
  return 0;
}

/****************************************************************************
 * Name: can_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 can_remoterequest(struct can_dev_s *dev, uint16_t id)
{
  caninfo("Fix me:Not Implemented\n");
  return 0;
}

/****************************************************************************
 * Name: can_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 can_send(struct can_dev_s *dev, struct can_msg_s *msg)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  uint32_t regval;
  uint32_t clz;
  uint32_t id;
  uint32_t dlc;
  uint8_t txobj;

  if (msg == NULL)
    {
      return ERROR;
    }

  regval = (can_getreg(priv, LPC43_CAN_MSGV2_OFFSET) & 0xffff);
  clz    = arm_clz(regval);

  if (clz == 0x10)
    {
      return ERROR;
    }

  txobj = CAN_TX_OBJ_LAST - clz + CAN_TX_OBJ_FIRST;
  DEBUGASSERT((txobj >= CAN_TX_OBJ_FIRST) && (txobj <= CAN_TX_OBJ_LAST));

  id = (uint32_t) msg->cm_hdr.ch_id;
  dlc = (uint32_t) msg->cm_hdr.ch_dlc;

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

  can_putreg(priv, LPC43_CAN_IF1_MSK1_OFFSET, 0xffff);
  can_putreg(priv, LPC43_CAN_IF1_MSK2_OFFSET, 0xffff);

  regval = ((dlc & CAN_MCTRL_DLC_MASK) | CAN_MCTRL_EOB | CAN_MCTRL_TXRQST
                      | CAN_MCTRL_TXIE);
  can_putreg(priv, LPC43_CAN_IF1_MCTRL_OFFSET, regval);

  /* Write data to IF1 data registers */

  regval = msg->cm_data[0] + (msg->cm_data[1] << 8);
  can_putreg(priv, LPC43_CAN_IF1_DA1_OFFSET, regval);

  regval = msg->cm_data[2] + (msg->cm_data[3] << 8);
  can_putreg(priv, LPC43_CAN_IF1_DA2_OFFSET, regval);

  regval = msg->cm_data[4] + (msg->cm_data[5] << 8);
  can_putreg(priv, LPC43_CAN_IF1_DB1_OFFSET, regval);

  regval = msg->cm_data[6] + (msg->cm_data[7] << 8);
  can_putreg(priv, LPC43_CAN_IF1_DB2_OFFSET, regval);

#ifdef CONFIG_CAN_EXTID
  can_putreg(priv, LPC43_CAN_IF1_ARB1_OFFSET, id & 0x0000ffff);
  can_putreg(priv, LPC43_CAN_IF1_ARB2_OFFSET, CAN_MSK2_DIR | CAN_MSK2_XTD
                   | CAN_MSK2_MSGVAL | id >> 16);
#else
  can_putreg(priv, LPC43_CAN_IF1_ARB1_OFFSET, 0x0000);
  can_putreg(priv, LPC43_CAN_IF1_ARB2_OFFSET, CAN_MSK2_DIR | CAN_MSK2_MSGVAL
                   | id << 2);
#endif

  regval = (CAN_CMDMSKW_WRRD | CAN_CMDMSKW_MASK |
            CAN_CMDMSKW_ARB | CAN_CMDMSKW_CTRL |
            CAN_CMDMSKW_CLRINTPND | CAN_CMDMSKW_TXRQST |
            CAN_CMDMSKW_DATAA | CAN_CMDMSKW_DATAB);
  can_putreg(priv, LPC43_CAN_IF1_CMDMSKW_OFFSET, regval);

  /* Write to Message RAM */

  can_putreg(priv, LPC43_CAN_IF1_CMDREQ_OFFSET, txobj);

#ifdef CONFIG_CAN_TXREADY
  can_txdone(dev);
#endif

  return OK;
}

/****************************************************************************
 * Name: candev_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 candev_txready(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;

  if (can_getreg(priv, LPC43_CAN_MSGV2_OFFSET) & 0x8000)
    {
      return false;
    }

  if (can_getreg(priv, LPC43_CAN_IF1_CMDREQ_OFFSET) & CAN_CMDREQ_BUSY)
    {
      return false;
    }

  return true;
}

/****************************************************************************
 * Name: candev_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 candev_txempty(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  return (!((can_getreg(priv, LPC43_CAN_MSGV2_OFFSET)) & 0xffff));
}

/****************************************************************************
 * Name: can0/1_interrupt
 *
 * Description:
 *   CAN interrupt handler for CAN0 and CAN1.
 *
 * 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
 *
 ****************************************************************************/

#ifdef CONFIG_LPC43_CAN0
static int can0_interrupt(int irq, void *context, void *arg)
{
  can_interrupt(&g_can0dev);
  return OK;
}
#endif

#ifdef CONFIG_LPC43_CAN1
static int can1_interrupt(int irq, void *context, void *arg)
{
  can_interrupt(&g_can1dev);
  return OK;
}
#endif

/****************************************************************************
 * Name: can_interrupt
 *
 * Description:
 *   CAN interrupt handler.  There is a single interrupt for both CAN0 and
 *   CAN1.
 *
 ****************************************************************************/

static void can_interrupt(struct can_dev_s *dev)
{
  struct up_dev_s *priv = (struct up_dev_s *)dev->cd_priv;
  uint32_t regval = 0;

  uint32_t msgindex = 0;

  /* Read CAN interrupt register */

  uint32_t interrupt = can_getreg(priv, LPC43_CAN_INT_OFFSET);

  /* Read CAN status register */

  uint32_t stat = can_getreg(priv, LPC43_CAN_STAT_OFFSET);

  if (interrupt & CAN_INT_STAT)
    {
      /* Clear all warning/error states except RXOK/TXOK */

      regval = can_getreg(priv, LPC43_CAN_STAT_OFFSET);
      regval &= CAN_STAT_RXOK | CAN_STAT_TXOK;
      can_putreg(priv, LPC43_CAN_STAT_OFFSET, regval);
    }
  else
    {
      msgindex = interrupt & 0x7fff;

      /* if no error detected */

      if (((stat & CAN_STAT_LEC_MASK) == 0) ||
          ((stat & CAN_STAT_LEC_MASK) == CAN_STAT_LEC_MASK))
        {
          if (msgindex <= CAN_RX_OBJ_LAST)
            {
              struct can_hdr_s hdr;
              uint32_t data[2];

              regval = can_getreg(priv, LPC43_CAN_STAT_OFFSET);
              regval &= ~CAN_STAT_RXOK;
              can_putreg(priv, LPC43_CAN_STAT_OFFSET, regval);

              can_readobj(priv, msgindex);
              can_savemsg(priv, &hdr, data);
              can_invalobj(priv, msgindex);
              can_receive(dev, &hdr, (uint8_t *)data);
            }
          else
            {
              regval = can_getreg(priv, LPC43_CAN_STAT_OFFSET);
              regval &= ~CAN_STAT_TXOK;
              can_putreg(priv, LPC43_CAN_STAT_OFFSET, regval);

              can_invalobj(priv, msgindex);
#ifdef CONFIG_CAN_TXREADY
              can_txready(dev);
#else
              can_txdone(dev);
#endif
            }
        }
      else
        {
          can_invalobj(priv, msgindex);
        }

      can_putreg(priv, LPC43_CAN_STAT_OFFSET, 0);

      if (msgindex == CAN_RX_OBJ_LAST)
        {
          can_setuprxobj(priv);
        }
    }
}

/****************************************************************************
 * Name: can_savemsg
 *
 * Description:
 *   Save received message.
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *   hdr - A reference to the message header
 *   data - A reference to the data block
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void can_savemsg(struct up_dev_s *priv, struct can_hdr_s *hdr,
                        uint32_t *data)
{
#ifdef CONFIG_CAN_EXTID
  hdr->ch_id  = (can_getreg(priv, LPC43_CAN_IF2_ARB1_OFFSET) & 0xffff) |
                (can_getreg(priv, LPC43_CAN_IF2_ARB2_OFFSET) & 0x0fff) << 16;
  hdr->ch_extid = 1;
#else
  hdr->ch_id  = (can_getreg(priv, LPC43_CAN_IF2_ARB2_OFFSET) & 0x1fff) >> 2;
#endif
  hdr->ch_dlc = can_getreg(priv, LPC43_CAN_IF2_MCTRL_OFFSET) & 0x000f;
  hdr->ch_rtr = 0;

  data[0]     = can_getreg(priv, LPC43_CAN_IF2_DA2_OFFSET) << 16 |
                can_getreg(priv, LPC43_CAN_IF2_DA1_OFFSET);
  data[1]     = can_getreg(priv, LPC43_CAN_IF2_DB2_OFFSET) << 16 |
                can_getreg(priv, LPC43_CAN_IF2_DB1_OFFSET);
}

/****************************************************************************
 * Name: can_readobj
 *
 * Description:
 *  Transfer Message Object into IF registers.
 *
 * Input Parameters:
 *  priv - A reference to the CAN block status
 *  index - Message Object number
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void can_readobj(struct up_dev_s *priv, uint32_t index)
{
  uint32_t regval;

  while (can_getreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET) & CAN_CMDREQ_BUSY);

  regval = (CAN_CMDMSKR_MASK | CAN_CMDMSKR_ARB | CAN_CMDMSKR_CTRL |
            CAN_CMDMSKR_CLRINTPND | CAN_CMDMSKR_DATAA | CAN_CMDMSKR_DATAB);
  can_putreg(priv, LPC43_CAN_IF2_CMDMSKR_OFFSET, regval);
  can_putreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET, index);
}

/****************************************************************************
 * Name: can_invalobj
 *
 * Description:
 *  Invalidate Message Object.
 *
 * Input Parameters:
 *  priv - A reference to the CAN block status
 *  index - Message Object number
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void can_invalobj(struct up_dev_s *priv, uint32_t index)
{
  while (can_getreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET) & CAN_CMDREQ_BUSY);

  can_putreg(priv, LPC43_CAN_IF2_ARB1_OFFSET, 0x0000);
  can_putreg(priv, LPC43_CAN_IF2_ARB2_OFFSET, 0x0000);

  /* Disable reception and transmission interrupts, clear transmit request */

  can_putreg(priv, LPC43_CAN_IF2_MCTRL_OFFSET, CAN_MCTRL_EOB);
  can_putreg(priv, LPC43_CAN_IF2_CMDMSKW_OFFSET,
                   CAN_CMDMSKW_WRRD | CAN_CMDMSKW_CLRINTPND |
                   CAN_CMDMSKW_CTRL | CAN_CMDMSKW_ARB);
  can_putreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET, index);
}

/****************************************************************************
 * Name: can_setuprxobj
 *
 * Description:
 *  Setup Message Object as buffer for received messages.
 *
 * Input Parameters:
 *  priv - A reference to the CAN block status
 *
 * Returned Value:
 *   None
 *
 ****************************************************************************/

static void can_setuprxobj(struct up_dev_s *priv)
{
  uint32_t regval;
  uint8_t i;

  while (can_getreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET) & CAN_CMDREQ_BUSY);

  can_putreg(priv, LPC43_CAN_IF2_MSK1_OFFSET, 0x0000);
  regval = (CAN_MSK2_MXTD | CAN_MSK2_MDIR);
  can_putreg(priv, LPC43_CAN_IF2_MSK2_OFFSET, regval);

  regval = (CAN_MCTRL_DLC_MASK | CAN_MCTRL_EOB | CAN_MCTRL_RXIE |
            CAN_MCTRL_UMASK);
  can_putreg(priv, LPC43_CAN_IF2_MCTRL_OFFSET, regval);

  can_putreg(priv, LPC43_CAN_IF2_ARB1_OFFSET, 0x0000);
#ifdef CONFIG_CAN_EXTID
  can_putreg(priv, LPC43_CAN_IF2_ARB2_OFFSET,
             CAN_MSK2_MSGVAL | CAN_MSK2_XTD);
#else
  can_putreg(priv, LPC43_CAN_IF2_ARB2_OFFSET, CAN_MSK2_MSGVAL);
#endif

  regval = (CAN_CMDMSKR_WRRD | CAN_CMDMSKR_MASK | CAN_CMDMSKR_ARB |
            CAN_CMDMSKR_CTRL | CAN_CMDMSKR_CLRINTPND | CAN_CMDMSKR_DATAA |
            CAN_CMDMSKR_DATAB);
  can_putreg(priv, LPC43_CAN_IF2_CMDMSKR_OFFSET, regval);

  for (i = CAN_RX_OBJ_FIRST; i <= CAN_RX_OBJ_LAST; ++i)
    {
      while (can_getreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET) &
             CAN_CMDREQ_BUSY);
      can_putreg(priv, LPC43_CAN_IF2_CMDREQ_OFFSET, i);
    }
}

/****************************************************************************
 * Name: can_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 * Tcan
 *
 * Where:
 *   Tcan is the period of the APB clock.
 *
 * Input Parameters:
 *   priv - A reference to the CAN block status
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int can_bittiming(struct up_dev_s *priv)
{
  uint32_t ts1 = CONFIG_LPC43_CAN_TSEG1;
  uint32_t ts2 = CONFIG_LPC43_CAN_TSEG2;
  uint32_t sjw = 1;
  uint32_t brp = CAN_CLOCK_FREQUENCY(priv->clkdiv) /
                 (priv->baud * CAN_BIT_QUANTA);

  uint32_t regval;

  canllvdbg("CAN%d PCLK: %d baud: %d\n",
            priv->port, CAN_CLOCK_FREQUENCY(priv->clkdiv), priv->baud);
  canllvdbg("TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw);

  /* Start configuring bit timing */

  regval = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
  regval |= CAN_CNTL_CCE;
  can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);

  regval = (((brp - 1) << CAN_BT_BRP_SHIFT)
                     | ((ts1 - 1) << CAN_BT_TSEG1_SHIFT)
                     | ((ts2 - 1) << CAN_BT_TSEG2_SHIFT)
                     | ((sjw - 1) << CAN_BT_SJW_SHIFT));

  canllvdbg("Setting CANxBTR= 0x%08x\n", regval);

  /* Set bit timing */

  can_putreg(priv, LPC43_CAN_BT_OFFSET, regval);

  /* Stop configuring bit timing */

  regval = can_getreg(priv, LPC43_CAN_CNTL_OFFSET);
  regval &= ~CAN_CNTL_CCE;
  can_putreg(priv, LPC43_CAN_CNTL_OFFSET, regval);

  return OK;
}

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

/****************************************************************************
 * Name: lpc43_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 *lpc43_caninitialize(int port)
{
  struct can_dev_s *candev;
  irqstate_t flags;
  uint32_t regval;

  canllvdbg("CAN%d\n", port);

  flags = enter_critical_section();

#ifdef CONFIG_LPC43_CAN0
  if (port == 0)
    {
      /* Enable clock */

      regval = getreg32(LPC43_CCU1_APB3_CAN0_CFG);
      regval |= CCU_CLK_CFG_RUN;
      putreg32(regval, LPC43_CCU1_APB3_CAN0_CFG);

      /* Configure CAN GPIO pins */

      lpc43_pin_config(PINCONF_CAN0_RD);
      lpc43_pin_config(PINCONF_CAN0_TD);

      candev = &g_can0dev;
    }
  else
#endif /* CONFIG_LPC43_CAN0 */

#ifdef CONFIG_LPC43_CAN1
  if (port == 1)
    {
      /* Enable clock */

      regval = getreg32(LPC43_CCU1_APB1_CAN1_CFG);
      regval |= CCU_CLK_CFG_RUN;
      putreg32(regval, LPC43_CCU1_APB1_CAN1_CFG);

      /* Configure CAN GPIO pins */

      lpc43_pin_config(PINCONF_CAN1_RD);
      lpc43_pin_config(PINCONF_CAN1_TD);

      candev = &g_can1dev;
    }
  else
#endif /* CONFIG_LPC43_CAN1 */
    {
      canerr("Unsupported port: %d\n", port);

      leave_critical_section(flags);
      return NULL;
    }

  leave_critical_section(flags);

  return candev;
}
#endif
