diff options
Diffstat (limited to 'nuttx/arch/arm/src/lpc17xx/lpc17_can.c')
-rwxr-xr-x | nuttx/arch/arm/src/lpc17xx/lpc17_can.c | 1017 |
1 files changed, 761 insertions, 256 deletions
diff --git a/nuttx/arch/arm/src/lpc17xx/lpc17_can.c b/nuttx/arch/arm/src/lpc17xx/lpc17_can.c index dfcd1f0e7..41f823459 100755 --- a/nuttx/arch/arm/src/lpc17xx/lpc17_can.c +++ b/nuttx/arch/arm/src/lpc17xx/lpc17_can.c @@ -2,9 +2,14 @@ * arch/arm/src/lpc17xx/lpc17_can.c * * Copyright (C) 2011 Li Zhuoyi. All rights reserved. - * Author: Li Zhuoyi <lzyy.cn@gmail.com> - * History: 0.1 2011-07-12 initial version - * 0.2 2011-08-03 support CAN1/CAN2 + * 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: * @@ -65,17 +70,62 @@ #if defined(CONFIG_LPC17_CAN1) || defined(CONFIG_LPC17_CAN2) /**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* 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 + +/**************************************************************************** * Private Types ****************************************************************************/ + struct up_dev_s { - int port; - int baud; /* Configured baud */ + uint8_t port; /* CAN port number */ + 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 */ @@ -88,7 +138,9 @@ 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); -static int can_interrupt(int irq, void *context); + +static void can_interrupt(FAR struct can_dev_s *dev); +static int can12_interrupt(int irq, void *context); /**************************************************************************** * Private Data @@ -96,263 +148,704 @@ static int can_interrupt(int irq, void *context); 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, + .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, - .baud = CONFIG_CAN1_BAUD, + .port = 1, + .baud = CONFIG_CAN1_BAUD, + .base = LPC17_CAN1_BASE, }; static struct can_dev_s g_can1dev = { - .cd_ops = &g_canops, - .cd_priv= &g_can1priv, + .cd_ops = &g_canops, + .cd_priv = &g_can1priv, }; #endif #ifdef CONFIG_LPC17_CAN2 static struct up_dev_s g_can2priv = { - .port = 2, - .baud = CONFIG_CAN2_BAUD, + .port = 2, + .baud = CONFIG_CAN2_BAUD, + .base = LPC17_CAN2_BASE, }; static struct can_dev_s g_can2dev = { - .cd_ops = &g_canops, - .cd_priv= &g_can2priv, + .cd_ops = &g_canops, + .cd_priv = &g_can2priv, }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ -/* Reset the CAN device. Called early to initialize the hardware. This -* is called, before co_setup() and on error conditions. -*/ +/**************************************************************************** + * 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) { - irqstate_t flags; - uint32_t regval; - struct up_dev_s *priv=dev->cd_priv; - int baud = priv->baud; - int port = priv->port; - - baud = 0x25c003; - flags = irqsave(); - - if(port==1) - { - putreg32(0x01,LPC17_CAN1_MOD); - putreg32(0x00,LPC17_CAN1_IER); - putreg32(0x00,LPC17_CAN1_GSR); - putreg32(0x02,LPC17_CAN1_CMR); - putreg32(baud,LPC17_CAN1_BTR); - putreg32(0x00,LPC17_CAN1_MOD); - putreg32(0x01,LPC17_CAN1_IER); - putreg32(0x02,LPC17_CANAF_AFMR); - } - else if(port==2) - { - putreg32(0x01,LPC17_CAN2_MOD); - putreg32(0x00,LPC17_CAN2_IER); - putreg32(0x00,LPC17_CAN2_GSR); - putreg32(0x02,LPC17_CAN2_CMR); - putreg32(baud,LPC17_CAN2_BTR); - putreg32(0x00,LPC17_CAN2_MOD); - putreg32(0x01,LPC17_CAN2_IER); - putreg32(0x02,LPC17_CANAF_AFMR); - } - else - { - dbg("Unsupport port %d\n",port); - } - irqrestore(flags); + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv; + uint32_t baud; + irqstate_t flags; + + canvdbg("CAN%d\n", priv->port); + +#warning "BTR setting must be calculated from priv->baud" + baud = 0x25c003; + flags = irqsave(); + + 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 */ + can_putreg(priv, LPC17_CAN_BTR_OFFSET, baud); /* Set bit timing */ +#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); } -/* 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. Interrupts -* are all disabled upon return. -*/ -static int can_setup(FAR struct can_dev_s *dev) +/**************************************************************************** + * 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) { - int ret = irq_attach(LPC17_IRQ_CAN, can_interrupt); - if (ret == OK) +#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); + up_enable_irq(LPC17_IRQ_CAN); } - return ret; + return ret; } -/* Disable the CAN. This method is called when the CAN device is closed. -* This method reverses the operation the setup method. -*/ +/**************************************************************************** + * 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) { - up_disable_irq(LPC17_IRQ_CAN); - irq_detach(LPC17_IRQ_CAN); +#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); } -/* Call to enable or disable RX interrupts */ +/**************************************************************************** + * 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) { - uint32_t regval; - int port = ((struct up_dev_s *)(dev->cd_priv))->port; - - if(port == 1) - regval = getreg32(LPC17_CAN1_IER); - else - regval = getreg32(LPC17_CAN2_IER); - - if (enable) - regval |= CAN_IER_RIE; - else - regval &= ~CAN_IER_RIE; - - if(port == 1) - putreg32(regval, LPC17_CAN1_IER); - else - putreg32(regval, LPC17_CAN2_IER); + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv; + uint32_t regval; + + canvdbg("CAN%d enable: %d\n", priv->port, enable); + + 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); } -/* Call to enable or disable TX interrupts */ +/**************************************************************************** + * 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) { - uint32_t regval; - int port = ((struct up_dev_s *)(dev->cd_priv))->port; - - if(port == 1) - regval = getreg32(LPC17_CAN1_IER); - else - regval = getreg32(LPC17_CAN2_IER); - - if (enable) - regval |= CAN_IER_TIE1; - else - regval &= ~CAN_IER_TIE1; - - if(port == 1) - putreg32(regval, LPC17_CAN1_IER); - else - putreg32(regval, LPC17_CAN2_IER); + FAR struct up_dev_s *priv = (FAR struct up_dev_s *)dev->cd_priv; + uint32_t regval; + + 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) + { + 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); + } } -/* All ioctl calls will be routed through this method */ -static int can_ioctl(FAR struct can_dev_s *dev, int cmd, unsigned long arg) +/**************************************************************************** + * 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; + dbg("Fix me:Not Implemented\n"); + return 0; } -/* Send a remote request */ -static int can_remoterequest(FAR struct can_dev_s *dev, uint16_t id) +/**************************************************************************** + * 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; + dbg("Fix me:Not Implemented\n"); + return 0; } -static int can_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg) +/**************************************************************************** + * 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) { - int port = ((struct up_dev_s *)(dev->cd_priv))->port; - uint32_t tid=CAN_ID(msg->cm_hdr); - uint32_t tfi=CAN_DLC(msg->cm_hdr)<<16; - if (CAN_RTR(msg->cm_hdr)) - tfi|=CAN_TFI_RTR; - if( port == 1) - { - putreg32(tfi,LPC17_CAN1_TFI1); - putreg32(tid,LPC17_CAN1_TID1); - putreg32(*(uint32_t *)&msg->cm_data[0],LPC17_CAN1_TDA1); - putreg32(*(uint32_t *)&msg->cm_data[4],LPC17_CAN1_TDB1); - putreg32(0x21,LPC17_CAN1_CMR); - } - else - { - putreg32(tfi,LPC17_CAN2_TFI1); - putreg32(tid,LPC17_CAN2_TID1); - putreg32(*(uint32_t *)&msg->cm_data[0],LPC17_CAN2_TDA1); - putreg32(*(uint32_t *)&msg->cm_data[4],LPC17_CAN2_TDB1); - putreg32(0x21,LPC17_CAN2_CMR); - } - return 0; + 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 the buffer 3 TX interrupt is enabled BEFORE sending the + * message. The TX interrupt is generated TBS3 bit in CANxSR goes from 0 + * to 1 when the TIE3 bit in CANxIER is 1. If we don't enable it now, + * we will miss interrupts. + * + * Hmmm... we could probably do better than this. Buffer 1 or 2 is much + * more likely to complete quicker than buffer 3. + */ + + 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_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) { - uint32_t regval; - int port = ((struct up_dev_s *)(dev->cd_priv))->port; - if( port == 1) - regval = getreg32(LPC17_CAN1_GSR); - else - regval = getreg32(LPC17_CAN2_GSR); - if ( regval & CAN_GSR_TBS) - return true; - else - return false; + 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); } -static int can_interrupt(int irq, void *context) +/**************************************************************************** + * 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) { - uint32_t regval; - -#ifdef CONFIG_LPC17_CAN1 - regval=getreg32(LPC17_CAN1_ICR); - if (regval & CAN_ICR_RI ) //Receive interrupt - { - uint16_t hdr; - uint32_t data[2]; - uint32_t rfs=getreg32(LPC17_CAN1_RFS); - uint32_t rid=getreg32(LPC17_CAN1_RID); - data[0]=getreg32(LPC17_CAN1_RDA); - data[1]=getreg32(LPC17_CAN1_RDB); - putreg32(0x04,LPC17_CAN1_CMR); //release recieve buffer - hdr=((rid<<5)&~0x1f)|((rfs>>16)&0x0f); - if (rfs&CAN_RFS_RTR) - hdr|=0x10; - can_receive(&g_can1dev,hdr,(uint8_t *)data); - } - if ( regval & CAN_ICR_TI1) //Transmit interrupt 1 - can_txdone(&g_can1dev); -#endif + 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 3 */ + + if ((regval & CAN_ICR_TI3) != 0) + { + 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 - regval=getreg32(LPC17_CAN2_ICR); - if (regval & CAN_ICR_RI ) //Receive interrupt - { - uint16_t hdr; - uint32_t data[2]; - uint32_t rfs=getreg32(LPC17_CAN2_RFS); - uint32_t rid=getreg32(LPC17_CAN2_RID); - data[0]=getreg32(LPC17_CAN2_RDA); - data[1]=getreg32(LPC17_CAN2_RDB); - putreg32(0x04,LPC17_CAN2_CMR); //release recieve buffer - hdr=((rid<<5)&~0x1f)|((rfs>>16)&0x0f); - if (rfs&CAN_RFS_RTR) - hdr|=0x10; - can_receive(&g_can2dev,hdr,data); - } - if ( regval & CAN_ICR_TI1) //Transmit interrupt 1 - can_txdone(&g_can2dev); + can_interrupt(&g_can2dev); #endif - return OK; + + return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ - /**************************************************************************** * Name: lpc17_caninitialize * @@ -369,67 +862,79 @@ static int can_interrupt(int irq, void *context) FAR struct can_dev_s *lpc17_caninitialize(int port) { - uint32_t regval; - irqstate_t flags; - flags = irqsave(); - struct can_dev_s *candev=NULL; - -#ifdef CONFIG_LPC17_CAN1 - if( port == 1 ) - { - regval = getreg32(LPC17_SYSCON_PCONP); - regval |= SYSCON_PCONP_PCCAN1; - putreg32(regval, LPC17_SYSCON_PCONP); - - regval = getreg32(LPC17_SYSCON_PCLKSEL0); - regval &= ~SYSCON_PCLKSEL0_CAN1_MASK; - regval |= (SYSCON_PCLKSEL_CCLK4 << SYSCON_PCLKSEL0_CAN1_SHIFT); - putreg32(regval, LPC17_SYSCON_PCLKSEL0); - - lpc17_configgpio(GPIO_CAN1_RD); - lpc17_configgpio(GPIO_CAN1_TD); - - putreg32(0x01,LPC17_CAN1_MOD); - putreg32(0x00,LPC17_CAN1_IER); - putreg32(0x00,LPC17_CAN1_GSR); - putreg32(0x02,LPC17_CAN1_CMR); - putreg32(0x49c009,LPC17_CAN1_BTR); - putreg32(0x00,LPC17_CAN1_MOD); - putreg32(0x01,LPC17_CAN1_IER); - putreg32(0x02,LPC17_CANAF_AFMR); - - candev = &g_can1dev; - } -#endif -#ifdef CONFIG_LPC17_CAN2 - if ( port ==2 ) - { - regval = getreg32(LPC17_SYSCON_PCONP); - regval |= SYSCON_PCONP_PCCAN2; - putreg32(regval, LPC17_SYSCON_PCONP); - - regval = getreg32(LPC17_SYSCON_PCLKSEL0); - regval &= ~SYSCON_PCLKSEL0_CAN2_MASK; - regval |= (SYSCON_PCLKSEL_CCLK4 << SYSCON_PCLKSEL0_CAN2_SHIFT); - putreg32(regval, LPC17_SYSCON_PCLKSEL0); - - lpc17_configgpio(GPIO_CAN2_RD); - lpc17_configgpio(GPIO_CAN2_TD); - - putreg32(0x01,LPC17_CAN2_MOD); - putreg32(0x00,LPC17_CAN2_IER); - putreg32(0x00,LPC17_CAN2_GSR); - putreg32(0x02,LPC17_CAN2_CMR); - putreg32(0x49c009,LPC17_CAN2_BTR); - putreg32(0x00,LPC17_CAN2_MOD); - putreg32(0x01,LPC17_CAN2_IER); - putreg32(0x02,LPC17_CANAF_AFMR); - - candev = &g_can2dev; - } -#endif - irqrestore(flags); - return candev; + 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 |= (SYSCON_PCLKSEL_CCLK4 << 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 |= (SYSCON_PCLKSEL_CCLK4 << 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 |