summaryrefslogblamecommitdiff
path: root/nuttx/arch/arm/src/lpc17xx/lpc17_can.c
blob: 43c7399eb0e1a58698a1f903c55489a4ee69f74e (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12



                                                                                     







                                                                   
   

                                





















































                                                                                      
                                                            

                                                                             

                                                                              



























































                                                                              























                                                                              
                                                                              
                                                                        
 
                                                                     
 
                                                                             

                                                                              
 

               



                                                      
  



                                                                              















                                                                          











                                                                             
 

                    

                                                     
 



                                                





                                                                              








                                        




                                   
               
                                 

                              



                                   

                         
  
      
 

                                   
 
               
                                 

                              



                                   

                         

      



                                                                              











































































































































































































                                                                              

                                                

                                                                  
          



                                 

                    

                                                     



                                                                               










                                                           






                                                                                              

 

















                                                                              
 








                                                                  
     
                                   
     
             

 














                                                                              

                                                   







                                                                  

 













                                                                              

                                                             

                                                                  
                   


                                                    




                                                                             









                                                  
                    

 













                                                                              

                                                             

                                                                  
                   









                                                                             







                                                                               


                                                              
                        
     
    

 














                                                                              
 

                                  

 














                                                                              
 

                                  

 























                                                                              
 




































































                                                                             





                                                                              
        

                                                                           


                                                       
                                                             
























                                                                             

 

















                                                                              

                                                  


                                                                  

 














                                                                              
 







































                                                                                


                                                                           
 
                                                               
     







                                                                         






















                                                                              
 




                               
                        
                            
      

            


                                                                             






























































                                                                              

                                                            

















                                                                                    
                                                           
























































                                                                              

                                                                              
                                                                             
                            

               
                                     

                   
                                                               

                  
                                                                        


                                                                              
                                                   
 

















                                                                         
                                                 



                                                     
                                                                  




















                                                                         
                                                 



                                                     
                                                                  





















                                                   


      
/************************************************************************************
 * arch/arm/src/lpc17xx/lpc17_can.c
 *
 *   Copyright (C) 2011 Li Zhuoyi. All rights reserved.
 *   Copyright (C) 2012 Gregory Nutt. All rights reserved.
 *   Authors:
 *     Li Zhuoyi <lzyy.cn@gmail.com>
 *     Gregory Nutt <gnutt@nuttx.org>
 *   History: 
 *     2011-07-12: Initial version (Li Zhuoyi)
 *     2011-08-03: Support CAN1/CAN2 (Li Zhuoyi)
 *     2012-01-02: Add support for CAN loopback mode (Gregory Nutt)
 * 
 * This file is a part of NuttX:
 *
 *   Copyright (C) 2010 Gregory Nutt. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************************************/

#include <nuttx/config.h>

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

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

#include "up_internal.h"
#include "up_arch.h"

#include "chip.h"
#include "lpc17_internal.h"
#include "lpc17_syscon.h"
#include "lpc17_pinconn.h"
#include "lpc17_can.h"

#if defined(CONFIG_LPC17_CAN1) || defined(CONFIG_LPC17_CAN2)

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

#ifdef CONFIG_LPC17_CAN1

   /* A CAN bit rate must be provided */

#  ifndef CONFIG_CAN1_BAUD
#    error "CONFIG_CAN1_BAUD is not defined"
#  endif

   /* If no divsor is provided, use a divisor of 4 */

#  ifndef CONFIG_CAN1_DIVISOR
#    define CONFIG_CAN1_DIVISOR 4
#  endif

   /* Get the SYSCON_PCLKSEL value for CAN1 the implements this divisor */

#  if CONFIG_CAN1_DIVISOR == 1
#    define CAN1_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK
#  elif CONFIG_CAN1_DIVISOR == 2
#    define CAN1_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK2
#  elif CONFIG_CAN1_DIVISOR == 4
#    define CAN1_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK4
#  elif CONFIG_CAN1_DIVISOR == 6
#    define CAN1_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK6
#  else
#    error "Unsupported value of CONFIG_CAN1_DIVISOR"
#  endif
#endif

#ifdef CONFIG_LPC17_CAN2

   /* A CAN bit rate must be provided */

#  ifndef CONFIG_CAN2_BAUD
#    error "CONFIG_CAN2_BAUD is not defined"
#  endif

   /* If no divsor is provided, use a divisor of 4 */

#  ifndef CONFIG_CAN2_DIVISOR
#    define CONFIG_CAN2_DIVISOR 4
#  endif

   /* Get the SYSCON_PCLKSEL value for CAN2 the implements this divisor */

#  if CONFIG_CAN2_DIVISOR == 1
#    define CAN2_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK
#  elif CONFIG_CAN2_DIVISOR == 2
#    define CAN2_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK2
#  elif CONFIG_CAN2_DIVISOR == 4
#    define CAN2_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK4
#  elif CONFIG_CAN2_DIVISOR == 6
#    define CAN2_CCLK_DIVISOR SYSCON_PCLKSEL_CCLK6
#  else
#    error "Unsupported value of CONFIG_CAN2_DIVISOR"
#  endif
#endif

/* Debug ********************************************************************/
/* Non-standard debug that may be enabled just for testing CAN */

#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_CAN)
#  undef CONFIG_CAN_REGDEBUG
#endif

#ifdef CONFIG_DEBUG_CAN
#  ifdef CONFIG_CAN_REGDEBUG
#    define candbg  lldbg
#    define canvdbg llvdbg
#  else
#    define candbg  dbg
#    define canvdbg vdbg
#  endif
#  define canlldbg  lldbg
#  define canllvdbg llvdbg
#else
#  define candbg(x...)
#  define canvdbg(x...)
#  define canlldbg(x...)
#  define canllvdbg(x...)
#endif

/* Timing *******************************************************************/
/* CAN clocking is provided at CCLK divided by the configured divisor */

#define CAN_CLOCK_FREQUENCY(d) ((uint32_t)LPC17_CCLK / (uint32_t)(d))

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

struct up_dev_s
{
  uint8_t  port;    /* CAN port number */
  uint8_t  divisor; /* CCLK divisor (numeric value) */
  uint32_t baud;    /* Configured baud */
  uint32_t base;    /* CAN register base address */
};

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/
/* CAN Register access */

#ifdef CONFIG_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_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(FAR struct can_dev_s *dev);
static int  can_setup(FAR struct can_dev_s *dev);
static void can_shutdown(FAR struct can_dev_s *dev);
static void can_rxint(FAR struct can_dev_s *dev, bool enable);
static void can_txint(FAR struct can_dev_s *dev, bool enable);
static int  can_ioctl(FAR struct can_dev_s *dev, int cmd, unsigned long arg);
static int  can_remoterequest(FAR struct can_dev_s *dev, uint16_t id);
static int  can_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg);
static bool can_txempty(FAR struct can_dev_s *dev);

/* CAN interrupts */

static void can_interrupt(FAR struct can_dev_s *dev);
static int  can12_interrupt(int irq, void *context);

/* 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_txempty       = can_txempty,
};

#ifdef CONFIG_LPC17_CAN1
static struct up_dev_s g_can1priv =
{
  .port    = 1,
  .divisor = CONFIG_CAN1_DIVISOR,
  .baud    = CONFIG_CAN1_BAUD,
  .base    = LPC17_CAN1_BASE,
};

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

#ifdef CONFIG_LPC17_CAN2
static struct up_dev_s g_can2priv =
{
  .port    = 2,
  .divisor = CONFIG_CAN2_DIVISOR,
  .baud    = CONFIG_CAN2_BAUD,
  .base    = LPC17_CAN2_BASE,
};

static struct can_dev_s g_can2dev =
{
  .cd_ops  = &g_canops,
  .cd_priv = &g_can2priv,
};
#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_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)
             {
               lldbg("...\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 */

           lldbg("[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 */

  lldbg("%08x->%08x\n", addr, value);
}
#endif

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

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

  lldbg("%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

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

/****************************************************************************
 * 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_CAN_REGDEBUG
static void can_putcommon(uint32_t addr, uint32_t value)
{
  /* Show the register value being written */

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

  /* Write the value */

  putreg32(value, addr);
}
#endif

/****************************************************************************
 * 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(FAR struct can_dev_s *dev)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t baud;
  int ret;
  irqstate_t flags;

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

  flags = irqsave();

  /* Disable the CAN and stop ongong transmissions */

  can_putreg(priv, LPC17_CAN_MOD_OFFSET, CAN_MOD_RM);  /* Enter Reset Mode */
  can_putreg(priv, LPC17_CAN_IER_OFFSET, 0);           /* Disable interrupts */
  can_putreg(priv, LPC17_CAN_GSR_OFFSET, 0);           /* Clear status bits */
  can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_AT);  /* Abort transmission */

  /* Set bit timing */

  ret = can_bittiming(priv);
  if (ret != OK)
    {
      candbg("ERROR: Failed to set bit timing: %d\n", ret);
    }

  /* Restart the CAN */

#ifdef CONFIG_CAN_LOOPBACK
  can_putreg(priv, LPC17_CAN_MOD_OFFSET, CAN_MOD_STM); /* Leave Reset Mode, enter Test Mode */
#else
  can_putreg(priv, LPC17_CAN_MOD_OFFSET, 0);           /* Leave Reset Mode */
#endif
  can_putcommon(LPC17_CANAF_AFMR, CANAF_AFMR_ACCBP);   /* All RX messages accepted */
  irqrestore(flags);
}

/****************************************************************************
 * 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(FAR struct can_dev_s *dev)
{
#ifdef CONFIG_DEBUG_CAN
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
#endif
  int ret;

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

  ret = irq_attach(LPC17_IRQ_CAN, can12_interrupt);
  if (ret == OK)
    {
      up_enable_irq(LPC17_IRQ_CAN);
    }
  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(FAR struct can_dev_s *dev)
{
#ifdef CONFIG_DEBUG_CAN
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;

  canvdbg("CAN%d\n", priv->port);
#endif

  up_disable_irq(LPC17_IRQ_CAN);
  irq_detach(LPC17_IRQ_CAN);
}

/****************************************************************************
 * Name: can_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 can_rxint(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t regval;
  irqstate_t flags;

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

   /* The EIR register is also modifed from the interrupt handler, so we have
    * to protect this code section.
    */

  flags = irqsave();
  regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
  if (enable)
    {
      regval |= CAN_IER_RIE;
    }
  else
    {
      regval &= ~CAN_IER_RIE;
    }
  can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
  irqrestore(flags);
}

/****************************************************************************
 * 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(FAR struct can_dev_s *dev, bool enable)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t regval;
  irqstate_t flags;

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

  /* Only disabling of the TX interrupt is supported here.  The TX interrupt
   * is automatically enabled just before a message is sent in order to avoid
   * lost TX interrupts.
   */

  if (!enable)
    {
      /* TX interrupts are also disabled from the interrupt handler, so we have
       * to protect this code section.
       */

      flags = irqsave();

      /* Disable all TX interrupts */

      regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
      regval &= ~(CAN_IER_TIE1 | CAN_IER_TIE2 | CAN_IER_TIE3);
      can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
      irqrestore(flags);
    }
    
}

/****************************************************************************
 * 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(FAR struct can_dev_s *dev, int cmd, unsigned long arg)
{
  dbg("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(FAR struct can_dev_s *dev, uint16_t id)
{
  dbg("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 Tranmission 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(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t tid = CAN_ID(msg->cm_hdr);
  uint32_t tfi = CAN_DLC(msg->cm_hdr) << 16;
  uint32_t regval;
  irqstate_t flags;
  int ret = OK;

  canvdbg("CAN%d ID: %d DLC: %d\n",
          priv->port, CAN_ID(msg->cm_hdr), CAN_DLC(msg->cm_hdr));

  if (CAN_RTR(msg->cm_hdr))
    {
      tfi |= CAN_TFI_RTR;
    }

  flags = irqsave();

  /* Pick a transmit buffer */

  regval = can_getreg(priv, LPC17_CAN_SR_OFFSET);
  if ((regval & CAN_SR_TBS1) != 0)
    {
      /* Set up the transfer */

      can_putreg(priv, LPC17_CAN_TFI1_OFFSET, tfi);
      can_putreg(priv, LPC17_CAN_TID1_OFFSET, tid);
      can_putreg(priv, LPC17_CAN_TDA1_OFFSET, *(uint32_t *)&msg->cm_data[0]);
      can_putreg(priv, LPC17_CAN_TDB1_OFFSET, *(uint32_t *)&msg->cm_data[4]);

      /* Send the message */

#ifdef CONFIG_CAN_LOOPBACK
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB1 | CAN_CMR_SRR);
#else
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB1 | CAN_CMR_TR);
#endif

      /* Tell the caller that the transfer is done.  It isn't, but we have
       * more transmit buffers and this can speed things up.
       */

      can_txdone(dev);
    }
  else if ((regval & CAN_SR_TBS2) != 0)
    {
      /* Set up the transfer */

      can_putreg(priv, LPC17_CAN_TFI2_OFFSET, tfi);
      can_putreg(priv, LPC17_CAN_TID2_OFFSET, tid);
      can_putreg(priv, LPC17_CAN_TDA2_OFFSET, *(uint32_t *)&msg->cm_data[0]);
      can_putreg(priv, LPC17_CAN_TDB2_OFFSET, *(uint32_t *)&msg->cm_data[4]);

      /* Send the message */

#ifdef CONFIG_CAN_LOOPBACK
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB2 | CAN_CMR_SRR);
#else
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB2 | CAN_CMR_TR);
#endif

      /* Tell the caller that the transfer is done.  It isn't, but we have
       * more transmit buffers and this can speed things up.
       */

      can_txdone(dev);
    }
  else if ((regval & CAN_SR_TBS3) != 0)
    {
      /* We have no more buffers.  We will make the caller wait.  First, make
       * sure that all buffer 3 interrupts are enabled BEFORE sending the
       * message. The TX interrupt is generated TBSn bit in CANxSR goes from 0
       * to 1 when the TIEn bit in CANxIER is 1.  If we don't enable it now,
       * we will miss the TIE3 interrupt.  We enable ALL TIE interrupts here
       * because we don't care which one finishes:  When first one finishes it
       * means that a transmit buffer is again available.
       *
       * NOTE: The IER is also modified from the interrupt handler, but the
       * following is safe because interrupts are disabled here.
       */

      regval  = can_getreg(priv, LPC17_CAN_IER_OFFSET);
      regval |= (CAN_IER_TIE1 | CAN_IER_TIE2 | CAN_IER_TIE3);
      can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);

      /* Set up the transfer */

      can_putreg(priv, LPC17_CAN_TFI3_OFFSET, tfi);
      can_putreg(priv, LPC17_CAN_TID3_OFFSET, tid);
      can_putreg(priv, LPC17_CAN_TDA3_OFFSET, *(uint32_t *)&msg->cm_data[0]);
      can_putreg(priv, LPC17_CAN_TDB3_OFFSET, *(uint32_t *)&msg->cm_data[4]);

      /* Send the message */

#ifdef CONFIG_CAN_LOOPBACK
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB3 | CAN_CMR_SRR);
#else
      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_STB3 | CAN_CMR_TR);
#endif
    }
  else
    {
      candbg("No available transmission buffer, SR: %08x\n", regval);
      ret = -EBUSY;
    }

  irqrestore(flags);
  return ret;
}

/****************************************************************************
 * Name: can_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:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static bool can_txempty(FAR struct can_dev_s *dev)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t regval = can_getreg(priv, LPC17_CAN_GSR_OFFSET);
  return ((regval & CAN_GSR_TBS) != 0);
}

/****************************************************************************
 * Name: can_interrupt
 *
 * Description:
 *   CAN1/2 RX/TX interrupt handler
 *
 * Input Parameters:
 *   dev - An instance of the "upper half" can driver state structure.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static void can_interrupt(FAR struct can_dev_s *dev)
{
  FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv;
  uint32_t data[2];
  uint32_t rfs;
  uint32_t rid;
  uint32_t regval;
  uint16_t hdr;
  uint16_t id;
  uint16_t dlc;
  uint16_t rtr;

  /* Read the interrupt and capture register (also clearing most status bits) */

  regval = can_getreg(priv, LPC17_CAN_ICR_OFFSET);
  canllvdbg("CAN%d ICR: %08x\n",  priv->port, regval);

  /* Check for a receive interrupt */

  if ((regval & CAN_ICR_RI) != 0)
    {
      rfs     = can_getreg(priv, LPC17_CAN_RFS_OFFSET);
      rid     = can_getreg(priv, LPC17_CAN_RID_OFFSET);
      data[0] = can_getreg(priv, LPC17_CAN_RDA_OFFSET);
      data[1] = can_getreg(priv, LPC17_CAN_RDB_OFFSET);

      /* Release the receive buffer */

      can_putreg(priv, LPC17_CAN_CMR_OFFSET, CAN_CMR_RRB);

      /* Construct the CAN header */

      id      = rid & CAN_RID_ID11_MASK;
      dlc     = (rfs & CAN_RFS_DLC_MASK) >> CAN_RFS_DLC_SHIFT;
      rtr     = ((rfs & CAN_RFS_RTR) != 0);
      hdr     = CAN_HDR(id, rtr, dlc);

      /* Process the received CAN packet */

      can_receive(dev, hdr, (uint8_t *)data);
    }

  /* Check for a transmit interrupt from buffer 1, 2, or 3 meaning that at
   * least one TX is complete and that at least one TX buffer is available.
   */

  if ((regval & (CAN_ICR_TI1 | CAN_ICR_TI2 |CAN_ICR_TI3)) != 0)
    {
      /* Disable all further TX interrupts */

      regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
      regval &= ~(CAN_IER_TIE1 | CAN_IER_TIE2 | CAN_IER_TIE3);
      can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);

      /* Indicate that the TX is done and a new TX buffer is available */

      can_txdone(&g_can1dev);
    }
}

/****************************************************************************
 * Name: can12_interrupt
 *
 * Description:
 *   CAN interrupt handler.  There is a single interrupt for both CAN1 and
 *   CAN2.
 *
 * Input Parameters:
 *   irq - The IRQ number of the interrupt.
 *   context - The register state save array at the time of the interrupt.
 *
 * Returned Value:
 *   Zero on success; a negated errno on failure
 *
 ****************************************************************************/

static int can12_interrupt(int irq, void *context)
{
  /* Handle CAN1/2 interrupts */

  canllvdbg("irq: %d\n",  irq);

#ifdef CONFIG_LPC17_CAN1
  can_interrupt(&g_can1dev);
#endif
#ifdef CONFIG_LPC17_CAN2
  can_interrupt(&g_can2dev);
#endif

  return OK;
}

/****************************************************************************
 * 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 * Tpclk1
 *
 * Where:
 *   Tpclk1 is the period of the APB clock (PCLK = CCLK / 4).
 *
 * Input Parameter:
 *   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 canbtr;
  uint32_t brp;
  uint32_t ts1;
  uint32_t ts2;
  uint32_t sjw;

  canllvdbg("CAN%d PCLK1: %d baud: %d\n", priv->port,
            CAN_CLOCK_FREQUENCY(priv->divisor), priv->baud);

  /* Try to get 14 quanta in one bit_time.  That is based on the idea that the ideal
   * would be ts1=6 nd ts2=7 and (1 + ts1 + ts2) = 14.
   *
   *   bit_time = Tq*(1 +ts1 + ts2)
   *   nquanta = bit_time/Tq
   *   nquanta  = (1 +ts1 + ts2)
   *
   *   bit_time = brp * Tpclk1 * (1 + ts1 + ts2)
   *   nquanta  = bit_time / brp / Tpclk1
   *            = PCLK1 / baud / brp
   *   brp      = PCLK1 / baud / nquanta;
   *
   * Example:
   *   PCLK1 = 42,000,000 baud = 1,000,000 nquanta = 14 : brp = 3
   *   PCLK1 = 42,000,000 baud =   700,000 nquanta = 14 : brp = 4
   */

  canbtr = CAN_CLOCK_FREQUENCY(priv->divisor) / priv->baud;
  if (canbtr < 14)
    {
      /* At the smallest brp value (1), there are already fewer bit times
       * (CAN_CLOCK / baud) is already smaller than our goal.  brp must be one
       * and we need make some reasonalble guesses about ts1 and ts2.
       */

      brp = 1;

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

      ts1 = (canbtr - 1) >> 1;
      ts2 = canbtr - ts1 - 1;
      if (ts1 == ts2 && ts1 > 1 && ts2 < 16)
        {
          ts1--;
          ts2++;          
        }
    }

  /* Otherwise, nquanta is 14, ts1 is 6, ts2 is 7 and we calculate brp to
   * achieve 14 quanta in the bit time 
   */

  else
    {
      ts1 = 6;
      ts2 = 7;
      brp = (canbtr + 7) / 14;
      DEBUGASSERT(brp >=1 && brp < 1024);
    }
    
  sjw = 1;

  canllvdbg("TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw);

 /* Configure bit timing */

  canbtr = ((brp - 1) << CAN_BTR_BRP_SHIFT)   |
           ((ts1 - 1) << CAN_BTR_TESG1_SHIFT) |
           ((ts2 - 1) << CAN_BTR_TESG2_SHIFT) |
           ((sjw - 1) << CAN_BTR_SJW_SHIFT);

#ifdef CONFIG_CAN_SAM
  /* The bus is sampled 3 times (recommended for low to medium speed buses
   * to spikes on the bus-line).
   */

  canbtr |= CAN_BTR_SAM;
#endif

  canllvdbg("Setting CANxBTR= 0x%08x\n", canbtr);
  can_putreg(priv, LPC17_CAN_BTR_OFFSET, canbtr);        /* Set bit timing */
  return OK;
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/
/****************************************************************************
 * Name: lpc17_caninitialize
 *
 * Description:
 *   Initialize the selected can port
 *
 * Input Parameter:
 *   Port number (for hardware that has mutiple can interfaces)
 *
 * Returned Value:
 *   Valid can device structure reference on succcess; a NULL on failure
 *
 ****************************************************************************/

FAR struct can_dev_s *lpc17_caninitialize(int port)
{
  FAR struct can_dev_s *candev;
  irqstate_t flags;
  uint32_t regval;

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

  flags = irqsave();

#ifdef CONFIG_LPC17_CAN1  
  if (port == 1)
    {
      /* Enable power to the CAN module */

      regval  = can_getcommon(LPC17_SYSCON_PCONP);
      regval |= SYSCON_PCONP_PCCAN1;
      can_putcommon(LPC17_SYSCON_PCONP, regval);

      /* Enable clocking to the CAN module (not necessary... already done
       * in low level clock configuration logic).
       */

      regval  = can_getcommon(LPC17_SYSCON_PCLKSEL0);
      regval &= ~SYSCON_PCLKSEL0_CAN1_MASK;
      regval |= (CAN1_CCLK_DIVISOR << SYSCON_PCLKSEL0_CAN1_SHIFT);
      can_putcommon(LPC17_SYSCON_PCLKSEL0, regval);

      /* Configure CAN GPIO pins */

      lpc17_configgpio(GPIO_CAN1_RD);
      lpc17_configgpio(GPIO_CAN1_TD);

      candev = &g_can1dev;
    }
  else
#endif
#ifdef CONFIG_LPC17_CAN2  
  if (port == 2)
    {
      /* Enable power to the CAN module */

      regval  = can_getcommon(LPC17_SYSCON_PCONP);
      regval |= SYSCON_PCONP_PCCAN2;
      can_putcommon(LPC17_SYSCON_PCONP, regval);

      /* Enable clocking to the CAN module (not necessary... already done
       * in low level clock configuration logic).
       */

      regval  = can_getcommon(LPC17_SYSCON_PCLKSEL0);
      regval &= ~SYSCON_PCLKSEL0_CAN2_MASK;
      regval |= (CAN2_CCLK_DIVISOR << SYSCON_PCLKSEL0_CAN2_SHIFT);
      can_putcommon(LPC17_SYSCON_PCLKSEL0, regval);

      /* Configure CAN GPIO pins */

      lpc17_configgpio(GPIO_CAN2_RD);
      lpc17_configgpio(GPIO_CAN2_TD);

      candev = &g_can2dev;
   }
 else
#endif
  {
    candbg("Unsupported port: %d\n", port);
    irqrestore(flags);
    return NULL;
  }

  /* Then just perform a CAN reset operation */

  can_reset(candev);
  irqrestore(flags);
  return candev;
}
#endif