1e4fe73e6b
git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4459 42af7a65-404d-4744-a932-0658087f49c3
1307 lines
37 KiB
C
1307 lines
37 KiB
C
/************************************************************************************
|
|
* 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
|
|
|
|
/* User-defined TSEG1 and TSEG2 settings may be used.
|
|
*
|
|
* CONFIG_CAN_TSEG1 = the number of CAN time quanta in segment 1
|
|
* CONFIG_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_CAN_TSEG1
|
|
# define CONFIG_CAN_TSEG1 6
|
|
#endif
|
|
|
|
#if CONFIG_CAN_TSEG1 < 1 || CONFIG_CAN_TSEG1 > CAN_BTR_TSEG1_MAX
|
|
# errror "CONFIG_CAN_TSEG1 is out of range"
|
|
#endif
|
|
|
|
#ifndef CONFIG_CAN_TSEG2
|
|
# define CONFIG_CAN_TSEG2 7
|
|
#endif
|
|
|
|
#if CONFIG_CAN_TSEG2 < 1 || CONFIG_CAN_TSEG2 > CAN_BTR_TSEG2_MAX
|
|
# errror "CONFIG_CAN_TSEG2 is out of range"
|
|
#endif
|
|
|
|
#define CAN_BIT_QUANTA (CONFIG_CAN_TSEG1 + CONFIG_CAN_TSEG2 + 1)
|
|
|
|
/* 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_txready(FAR struct can_dev_s *dev);
|
|
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_txready = can_txready,
|
|
.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;
|
|
irqstate_t flags;
|
|
int ret;
|
|
|
|
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 = (uint32_t)msg->cm_hdr.ch_id;
|
|
uint32_t tfi = (uint32_t)msg->cm_hdr.ch_dlc << 16;
|
|
uint32_t regval;
|
|
irqstate_t flags;
|
|
int ret = OK;
|
|
|
|
canvdbg("CAN%d ID: %d DLC: %d\n", priv->port, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc);
|
|
|
|
if (msg->cm_hdr.ch_rtr)
|
|
{
|
|
tfi |= CAN_TFI_RTR;
|
|
}
|
|
|
|
/* Set the FF bit in the TFI register if this message should be sent with
|
|
* the extended frame format (and 29-bit extened ID).
|
|
*/
|
|
|
|
#ifdef CONFIG_CAN_EXTID
|
|
if (msg->cm_hdr.ch_extid)
|
|
{
|
|
/* The provided ID should be 29 bits */
|
|
|
|
DEBUGASSERT((tid & ~CAN_TID_ID29_MASK) == 0);
|
|
tfi |= CAN_TFI_FF;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* The provided ID should be 11 bits */
|
|
|
|
DEBUGASSERT((tid & ~CAN_TID_ID11_MASK) == 0);
|
|
}
|
|
|
|
flags = irqsave();
|
|
|
|
/* Pick a transmit buffer */
|
|
|
|
regval = can_getreg(priv, LPC17_CAN_SR_OFFSET);
|
|
if ((regval & CAN_SR_TBS1) != 0)
|
|
{
|
|
/* Make sure that buffer 1 TX interrupts are enabled BEFORE sending the
|
|
* message. The TX interrupt is generated when the 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 may miss the TIE1 interrupt.
|
|
*
|
|
* 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_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
|
|
|
|
/* 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
|
|
}
|
|
else if ((regval & CAN_SR_TBS2) != 0)
|
|
{
|
|
/* Make sure that buffer 2 TX interrupts are enabled BEFORE sending the
|
|
* message. The TX interrupt is generated when the 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 may miss the TIE2 interrupt.
|
|
*
|
|
* 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_TIE2;
|
|
can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
|
|
|
|
/* 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
|
|
}
|
|
else if ((regval & CAN_SR_TBS3) != 0)
|
|
{
|
|
/* Make sure that buffer 3 TX interrupts are enabled BEFORE sending the
|
|
* message. The TX interrupt is generated when the 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 may miss the TIE3 interrupt.
|
|
*
|
|
* 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_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_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 can_txready(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_SR_OFFSET);
|
|
return ((regval & (CAN_SR_TBS1 | CAN_SR_TBS2 | CAN_SR_TBS3)) != 0);
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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:
|
|
* True if there are no pending TX transfers in the CAN hardware.
|
|
*
|
|
****************************************************************************/
|
|
|
|
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;
|
|
struct can_hdr_s hdr;
|
|
uint32_t data[2];
|
|
uint32_t rfs;
|
|
uint32_t rid;
|
|
uint32_t regval;
|
|
|
|
/* 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 */
|
|
|
|
hdr.ch_id = rid;
|
|
hdr.ch_rtr = ((rfs & CAN_RFS_RTR) != 0);
|
|
hdr.ch_dlc = (rfs & CAN_RFS_DLC_MASK) >> CAN_RFS_DLC_SHIFT;
|
|
#ifdef CONFIG_CAN_EXTID
|
|
hdr.ch_extid = ((rfs & CAN_RFS_FF) != 0);
|
|
#else
|
|
if ((rfs & CAN_RFS_FF) != 0)
|
|
{
|
|
canlldbg("ERROR: Received message with extended identifier. Dropped\n");
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Process the received CAN packet */
|
|
|
|
can_receive(dev, &hdr, (uint8_t *)data);
|
|
}
|
|
}
|
|
|
|
/* Check for TX buffer 1 complete */
|
|
|
|
if ((regval & CAN_ICR_TI1) != 0)
|
|
{
|
|
/* Disable all further TX buffer 1 interrupts */
|
|
|
|
regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
|
|
regval &= ~CAN_IER_TIE1;
|
|
can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
|
|
|
|
/* Indicate that the TX is done and a new TX buffer is available */
|
|
|
|
can_txdone(dev);
|
|
}
|
|
|
|
/* Check for TX buffer 2 complete */
|
|
|
|
if ((regval & CAN_ICR_TI2) != 0)
|
|
{
|
|
/* Disable all further TX buffer 2 interrupts */
|
|
|
|
regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
|
|
regval &= ~CAN_IER_TIE2;
|
|
can_putreg(priv, LPC17_CAN_IER_OFFSET, regval);
|
|
|
|
/* Indicate that the TX is done and a new TX buffer is available */
|
|
|
|
can_txdone(dev);
|
|
}
|
|
|
|
/* Check for TX buffer 3 complete */
|
|
|
|
if ((regval & CAN_ICR_TI3) != 0)
|
|
{
|
|
/* Disable all further TX buffer 3 interrupts */
|
|
|
|
regval = can_getreg(priv, LPC17_CAN_IER_OFFSET);
|
|
regval &= ~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(dev);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* 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 * Tcan
|
|
*
|
|
* Where:
|
|
* Tcan is the period of the APB clock (PCLK = CCLK / CONFIG_CAN1_DIVISOR).
|
|
*
|
|
* 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 btr;
|
|
uint32_t nclks;
|
|
uint32_t brp;
|
|
uint32_t ts1;
|
|
uint32_t ts2;
|
|
uint32_t sjw;
|
|
|
|
canllvdbg("CAN%d PCLK: %d baud: %d\n", priv->port,
|
|
CAN_CLOCK_FREQUENCY(priv->divisor), priv->baud);
|
|
|
|
/* Try to get CAN_BIT_QUANTA quanta in one bit_time.
|
|
*
|
|
* bit_time = Tq*(ts1 + ts2 + 1)
|
|
* nquanta = bit_time/Tq
|
|
* Tq = brp * Tcan
|
|
* nquanta = (ts1 + ts2 + 1)
|
|
*
|
|
* bit_time = brp * Tcan * (ts1 + ts2 + 1)
|
|
* nquanta = bit_time / brp / Tcan
|
|
* brp = Fcan / baud / nquanta;
|
|
*
|
|
* First, calculate the number of CAN clocks in one bit time: Fcan / baud
|
|
*/
|
|
|
|
nclks = CAN_CLOCK_FREQUENCY(priv->divisor) / priv->baud;
|
|
if (nclks < CAN_BIT_QUANTA)
|
|
{
|
|
/* At the smallest brp value (1), there are already too few bit times
|
|
* (CAN_CLOCK / baud) to meet our goal. brp must be one and we need
|
|
* make some reasonable guesses about ts1 and ts2.
|
|
*/
|
|
|
|
brp = 1;
|
|
|
|
/* In this case, we have to guess a good value for ts1 and ts2 */
|
|
|
|
ts1 = (nclks - 1) >> 1;
|
|
ts2 = nclks - ts1 - 1;
|
|
if (ts1 == ts2 && ts1 > 1 && ts2 < CAN_BTR_TSEG2_MAX)
|
|
{
|
|
ts1--;
|
|
ts2++;
|
|
}
|
|
}
|
|
|
|
/* Otherwise, nquanta is CAN_BIT_QUANTA, ts1 is CONFIG_CAN_TSEG1, ts2 is
|
|
* CONFIG_CAN_TSEG2 and we calculate brp to achieve CAN_BIT_QUANTA quanta
|
|
* in the bit time
|
|
*/
|
|
|
|
else
|
|
{
|
|
ts1 = CONFIG_CAN_TSEG1;
|
|
ts2 = CONFIG_CAN_TSEG2;
|
|
brp = (nclks + (CAN_BIT_QUANTA/2)) / CAN_BIT_QUANTA;
|
|
DEBUGASSERT(brp >=1 && brp <= CAN_BTR_BRP_MAX);
|
|
}
|
|
|
|
sjw = 1;
|
|
|
|
canllvdbg("TS1: %d TS2: %d BRP: %d SJW= %d\n", ts1, ts2, brp, sjw);
|
|
|
|
/* Configure bit timing */
|
|
|
|
btr = (((brp - 1) << CAN_BTR_BRP_SHIFT) |
|
|
((ts1 - 1) << CAN_BTR_TSEG1_SHIFT) |
|
|
((ts2 - 1) << CAN_BTR_TSEG2_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).
|
|
*/
|
|
|
|
btr |= CAN_BTR_SAM;
|
|
#endif
|
|
|
|
canllvdbg("Setting CANxBTR= 0x%08x\n", btr);
|
|
can_putreg(priv, LPC17_CAN_BTR_OFFSET, btr); /* 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
|
|
|