From eb7373cedfaa10a8cccc608ef51f4b570aa46260 Mon Sep 17 00:00:00 2001 From: Alan Carvalho de Assis Date: Tue, 23 May 2017 14:28:52 -0300 Subject: [PATCH] Add Microchip MCP2515 CAN Bus controller driver --- drivers/can/Kconfig | 48 + drivers/can/mcp2515.c | 2581 +++++++++++++++++++++++++++++++++++ drivers/can/mcp2515.h | 397 ++++++ include/nuttx/can/mcp2515.h | 164 +++ 4 files changed, 3190 insertions(+) create mode 100644 drivers/can/mcp2515.c create mode 100644 drivers/can/mcp2515.h create mode 100644 include/nuttx/can/mcp2515.h diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 577dbea8d15..69b044d2743 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -124,4 +124,52 @@ config CAN_NPOLLWAITERS The maximum number of threads that may be waiting on the poll method. +comment "CAN Bus Controllers:" + +config CAN_MCP2515 + bool "Microchip MCP2515 CAN Bus Controller over SPI" + default n + depends on SPI + select ARCH_HAVE_CAN_ERRORS + ---help--- + Enable driver support for Microchip MCP2515. + +if CAN_MCP2515 + +config MCP2515_BITRATE + int "MCP2515 bitrate" + default 500000 + ---help--- + MCP2515 bitrate in bits per second. + +config MCP2515_PROPSEG + int "MCP2515 Propagation Segment TQ" + default 2 + range 1 8 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config MCP2515_PHASESEG1 + int "MCP2515 Phase Segment 1" + default 2 + range 1 8 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config MCP2515_PHASESEG2 + int "MCP2515 Phase Segment 2" + default 3 + range 2 8 + ---help--- + The length of the bit time is Tquanta * (SyncSeg + PropSeg + PhaseSeg1 + PhaseSeg2). + +config MCP2515_SJW + int "MCP2515 Synchronization Jump Width" + default 1 + range 1 4 + ---help--- + The duration of a synchronization jump is SJW. + +endif # CAN_MCP2515 + endif # CAN diff --git a/drivers/can/mcp2515.c b/drivers/can/mcp2515.c new file mode 100644 index 00000000000..5a514e48e85 --- /dev/null +++ b/drivers/can/mcp2515.c @@ -0,0 +1,2581 @@ +/**************************************************************************** + * drivers/can/mcp2515.c + * + * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved. + * Author: Alan Carvalho de Assis + * + * 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, Atmel, 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "mcp2515.h" + +#if defined(CONFIG_CAN) && defined(CONFIG_CAN_MCP2515) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* Common definitions *******************************************************/ + +/* General Configuration ****************************************************/ + +#ifndef CONFIG_CAN_TXREADY +# warning WARNING!!! CONFIG_CAN_TXREADY is required by this driver +#endif + +/* MCP2515 Configuration ****************************************************/ + +/* Bit timing */ + +#define MCP2515_PROPSEG CONFIG_MCP2515_PROPSEG +#define MCP2515_PHSEG1 CONFIG_MCP2515_PHASESEG1 +#define MCP2515_PHSEG2 CONFIG_MCP2515_PHASESEG2 +#define MCP2515_TSEG1 (MCP2515_PROPSEG + MCP2515_PHSEG1) +#define MCP2515_TSEG2 MCP2515_PHSEG2 +#define MCP2515_BRP ((uint8_t)(((float) MCP2515_CANCLK_FREQUENCY / \ + ((float)(MCP2515_TSEG1 + MCP2515_TSEG2 + 1) * \ + (float)CONFIG_MCP2515_BITRATE)) - 1)) +#define MCP2515_SJW CONFIG_MCP2515_SJW + +#if MCP2515_TSEG1 > 16 +# error Invalid MCP2515 TSEG1 +#endif +#if MCP2515_TSEG2 < 2 +# error Invalid TSEG2. It cannot be lower than 2 +#endif +#if MCP2515_TSEG2 > 8 +# error Invalid TSEG2. It cannot be greater than 8 +#endif +#if MCP2515_SJW > 4 +# error Invalid SJW. It cannot be greater than 4 +#endif + +/* MCP2515 RXB0 element size */ + +/* MCP2515 RXB1 element size */ + +/* MCP2515 Filters */ + +# ifndef CONFIG_MCP2515_NSTDFILTERS +# define CONFIG_MCP2515_NSTDFILTERS 0 +# endif + +# if (CONFIG_MCP2515_NSTDFILTERS > 128) +# error Invalid MCP25150 number of Standard Filters +# endif + +# ifndef CONFIG_MCP2515_NEXTFILTERS +# define CONFIG_MCP2515_NEXTFILTERS 0 +# endif + +# if (CONFIG_MCP2515_NEXTFILTERS > 64) +# error Invalid MCP25150 number of Extended Filters +# endif + +# define MCP2515_STDFILTER_BYTES \ + MCP2515_ALIGN_UP(CONFIG_MCP2515_NSTDFILTERS << 2) +# define MCP2515_STDFILTER_WORDS (MCP2515_STDFILTER_BYTES >> 2) + +# define MCP2515_EXTFILTER_BYTES \ + MCP2515_ALIGN_UP(CONFIG_MCP2515_NEXTFILTERS << 3) +# define MCP2515_EXTFILTER_WORDS (MCP2515_EXTFILTER_BYTES >> 2) + +/* MCP25150 TX buffer element size */ + +/* MCP25150 TX FIFOs */ + +/* Loopback mode */ + +#undef MCP2515_LOOPBACK +#if defined(CONFIG_MCP2515_LOOPBACK) +# define MCP2515_LOOPBACK 1 +#endif + +/* Interrupts ***************************************************************/ +/* Interrupts Errors + * + * MCP2515_INT_MERR - Message Error Interrupt Flag bit + * MCP2515_INT_ERR - Error Interrupt Flag bit (mult src in EFLG register) + */ + +#define MCP2515_ERROR_INTS (MCP2515_INT_MERR | MCP2515_INT_ERR) + +/* RXn buffer interrupts + * + * MCP2515_INT_RX0 - Receive Buffer 0 New Message + * MCP2515_INT_RX1 - Receive Buffer 1 New Message + */ + +#define MCP2515_RXBUFFER_INTS (MCP2515_INT_RX0 | MCP2515_INT_RX1) + +/* TXn buffer interrupts + * + * MCP2515_INT_TX0 - Transmmit Buffer 0 Empty Interrupt + * MCP2515_INT_TX1 - Transmmit Buffer 1 Empty Interrupt + * MCP2515_INT_TX2 - Transmmit Buffer 2 Empty Interrupt + */ + +#define MCP2515_TXBUFFER_INTS (MCP2515_INT_TX0 | MCP2515_INT_TX1 | MCP2515_INT_TX2) + +/* Debug ********************************************************************/ +/* Debug configurations that may be enabled just for testing MCP2515 */ + +#ifndef CONFIG_DEBUG_CAN_INFO +# undef CONFIG_MCP2515_REGDEBUG +#endif + +#ifdef CONFIG_MCP2515_REGDEBUG +# define reginfo caninfo +#else +# define reginfo(x...) +#endif + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* CAN driver state */ + +enum can_state_s +{ + MCP2515_STATE_UNINIT = 0, /* Not yet initialized */ + MCP2515_STATE_RESET, /* Initialized, reset state */ + MCP2515_STATE_SETUP, /* can_setup() has been called */ +}; + +/* This structure provides the current state of a CAN peripheral */ + +struct mcp2515_can_s +{ + struct mcp2515_config_s *config; /* The constant configuration */ + uint8_t state; /* See enum can_state_s */ + uint8_t nalloc; /* Number of allocated filters */ + sem_t locksem; /* Enforces mutually exclusive access */ + sem_t txfsem; /* Used to wait for TX FIFO availability */ + uint32_t btp; /* Current bit timing */ + uint8_t rxints; /* Configured RX interrupts */ + uint8_t txints; /* Configured TX interrupts */ +#ifdef CONFIG_CAN_ERRORS + uint32_t olderrors; /* Used to detect the changes in error states */ +#endif + uint8_t filters; /* Standard/Extende filter bit allocator. */ + uint8_t ntxbufs; /* Number of allocated TX Buffers */ + uint8_t txbuffers; /* TX Buffers bit allocator. */ + +#ifdef CONFIG_MCP2515_REGDEBUG + uintptr_t regaddr; /* Last register address read */ + uint32_t regval; /* Last value read from the register */ + unsigned int count; /* Number of times that the value was read */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* MCP2515 Register access */ + +static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t *buffer, uint8_t len); +static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t *buffer, uint8_t len); +static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t mask, uint8_t value); +#ifdef CONFIG_MCP2515_REGDEBUG +static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv, FAR const char *msg); +#else +# define mcp2515_dumpregs(priv,msg) +#endif + +/* Semaphore helpers */ + +static void mcp2515_dev_lock(FAR struct mcp2515_can_s *priv); +#define mcp2515_dev_unlock(priv) sem_post(&priv->locksem) + +/* MCP2515 helpers */ + +#ifdef CONFIG_CAN_EXTID +static int mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv, + FAR struct canioc_extfilter_s *extconfig); +static int mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx); +#endif +static int mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv, + FAR struct canioc_stdfilter_s *stdconfig); +static int mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx); + +/* CAN driver methods */ + +static void mcp2515_reset(FAR struct can_dev_s *dev); +static int mcp2515_setup(FAR struct can_dev_s *dev); +static void mcp2515_shutdown(FAR struct can_dev_s *dev); +static void mcp2515_rxint(FAR struct can_dev_s *dev, bool enable); +static void mcp2515_txint(FAR struct can_dev_s *dev, bool enable); +static int mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd, + unsigned long arg); +static int mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id); +static int mcp2515_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg); +static bool mcp2515_txready(FAR struct can_dev_s *dev); +static bool mcp2515_txempty(FAR struct can_dev_s *dev); + +/* MCP2515 interrupt handling */ + +#ifdef CONFIG_CAN_ERRORS +static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status, + uint8_t oldstatus); +#endif +static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset); +static int mcp2515_interrupt(FAR struct mcp2515_config_s *config, FAR void *arg); + +/* Hardware initialization */ + +static int mcp2515_hw_initialize(FAR struct mcp2515_can_s *priv); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct can_ops_s g_mcp2515ops = +{ + .co_reset = mcp2515_reset, + .co_setup = mcp2515_setup, + .co_shutdown = mcp2515_shutdown, + .co_rxint = mcp2515_rxint, + .co_txint = mcp2515_txint, + .co_ioctl = mcp2515_ioctl, + .co_remoterequest = mcp2515_remoterequest, + .co_send = mcp2515_send, + .co_txready = mcp2515_txready, + .co_txempty = mcp2515_txempty, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcp2515_readregs + * + * Description: + * Read value(s) of MCP2515 register(s). + * + * Input Parameters: + * priv - A reference to the MCP2515 peripheral state + * offset - The offset to the register to read + * + * Returned Value: + * + ****************************************************************************/ + +static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t *buffer, uint8_t len) +{ + FAR struct mcp2515_config_s *config = priv->config; +#ifdef CONFIG_CANBUS_REGDEBUG + int i; +#endif + + (void)SPI_LOCK(config->spi, true); + + /* Select the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), true); + + /* Send the READ command */ + + (void)SPI_SEND(config->spi, MCP2515_READ); + + /* Send register to read and get the next bytes read back */ + + (void)SPI_SEND(config->spi, regaddr); + SPI_RECVBLOCK(config->spi, buffer, len); + + /* Deselect the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), false); + + /* Unlock bus */ + + (void)SPI_LOCK(config->spi, false); + +#ifdef CONFIG_CANBUS_REGDEBUG + for (i = 0; i < len; i++) + { + caninfo("%02x->%02x\n", regaddr, buffer[i]); + } +#endif +} + +/**************************************************************************** + * Name: mcp2515_writeregs + * + * Description: + * Set the value of a MCP2515 register. + * + * Input Parameters: + * priv - A reference to the MCP2515 peripheral state + * offset - The offset to the register to write + * regval - The value to write to the register + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t *buffer, uint8_t len) +{ + FAR struct mcp2515_config_s *config = priv->config; +#ifdef CONFIG_CANBUS_REGDEBUG + int i; + + for (i = 0; i < len; i++) + { + caninfo("%02x<-%02x\n", regaddr+i, buffer[i]); + } +#endif + + (void)SPI_LOCK(config->spi, true); + + /* Select the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), true); + + /* Send the READ command */ + + (void)SPI_SEND(config->spi, MCP2515_WRITE); + + /* Send initial register to be written */ + + (void)SPI_SEND(config->spi, regaddr); + SPI_SNDBLOCK(config->spi, buffer, len); + + /* Deselect the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), false); + + /* Unlock bus */ + + (void)SPI_LOCK(config->spi, false); + +} + +/**************************************************************************** + * Name: mcp2515_modifyreg + * + * Description: + * Modify individuals bits of MCP2515 register + * + * Input Parameters: + * priv - A reference to the MCP2515 peripheral state + * offset - The offset to the register to write + * regval - The value to write to the register + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_modifyreg(FAR struct mcp2515_can_s *priv, uint8_t regaddr, + uint8_t mask, uint8_t value) +{ + FAR struct mcp2515_config_s *config = priv->config; + + (void)SPI_LOCK(config->spi, true); + + /* Select the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), true); + + /* Send the Modify command */ + + (void)SPI_SEND(config->spi, MCP2515_BITMOD); + + /* Send the register address */ + + (void)SPI_SEND(config->spi, regaddr); + + /* Send the mask */ + + (void)SPI_SEND(config->spi, mask); + + /* Send the value */ + + (void)SPI_SEND(config->spi, value); + + /* Deselect the MCP2515 */ + + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), false); + + /* Unlock bus */ + + (void)SPI_LOCK(config->spi, false); + +} + +/**************************************************************************** + * Name: mcp2515_dumpregs + * + * Description: + * Dump the contents of all MCP2515 control registers + * + * Input Parameters: + * priv - A reference to the MCP2515 peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_MCP2515_REGDEBUG +static void mcp2515_dumpregs(FAR struct mcp2515_can_s *priv, FAR const char *msg) +{ + FAR struct mcp2515_config_s *config = priv->config; +} +#endif + +/**************************************************************************** + * Name: mcp2515_dev_lock + * + * Description: + * Take the semaphore that enforces mutually exclusive access to device + * structures, handling any exceptional conditions + * + * Input Parameters: + * priv - A reference to the MCP2515 peripheral state + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_dev_lock(FAR struct mcp2515_can_s *priv) +{ + int ret; + + /* Wait until we successfully get the semaphore. EINTR is the only + * expected 'failure' (meaning that the wait for the semaphore was + * interrupted by a signal. + */ + + do + { + ret = sem_wait(&priv->locksem); + DEBUGASSERT(ret == 0 || errno == EINTR); + } + while (ret < 0); +} + +/**************************************************************************** + * Name: mcp2515_add_extfilter + * + * Description: + * Add an address filter for a extended 29 bit address. + * + * Input Parameters: + * priv - An instance of the MCP2515 driver state structure. + * extconfig - The configuration of the extended filter + * + * Returned Value: + * A non-negative filter ID is returned on success. Otherwise a negated + * errno value is returned to indicate the nature of the error. + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_EXTID +static int mcp2515_add_extfilter(FAR struct mcp2515_can_s *priv, + FAR struct canioc_extfilter_s *extconfig) +{ + FAR struct mcp2515_config_s *config; + uint8_t regval; + uint8_t offset; + uint8_t mode = CAN_FILTER_MASK; + int ndx; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Get exclusive excess to the MCP2515 hardware */ + + mcp2515_dev_lock(priv); + + /* Find an unused standard filter */ + + for (ndx = 0; ndx < config->nfilters; ndx++) + { + /* Is this filter assigned? */ + + if ((priv->filters & (1 << ndx)) == 0) + { + /* No, assign the filter */ + + DEBUGASSERT(priv->nalloc < priv->config->nfilters); + priv->filters |= (1 << ndx); + priv->nalloc++; + + /* Format and write filter */ + + DEBUGASSERT(extconfig->sf_id1 <= CAN_MAX_STDMSGID); + + DEBUGASSERT(extconfig->sf_id2 <= CAN_MAX_STDMSGID); + + /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL, + * RXFnEID8 and RXFnEID0) using this formula: + * + * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ; + * maskN = RXM0reg + offset + */ + + if (priv->nalloc <= 3) + { + offset = 0; + } + else + { + offset = 4; + } + +#if 0 + /* N.B. Buffer 0 is higher priority than Buffer 1 + * but to separate these messages we will make this + * driver more complex. So let to consider that the + * first 2 IDs inserted in the filter will have more + * priority than the latest 4 IDs*/ + + if (extconfig->sf_prio == CAN_MSGPRIO_LOW) + { + /* Use RXB1 filters */ + } + else + { + /* Use RXB0 filters */ + } +#endif + + switch (extconfig->xf_type) + { + default: + case CAN_FILTER_DUAL: + mode = CAN_FILTER_DUAL; + break; + + case CAN_FILTER_MASK: + mode = CAN_FILTER_MASK; + break; + + case CAN_FILTER_RANGE: + /* not supported */ + break; + } + + /* Setup the CONFIG Mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + if (mode == CAN_FILTER_DUAL) + { + /* The MSD IDs will be filtered by separated Mask and Filter */ + + /* Setup the Filter */ + + /* EID0 - EID7 */ + + regval = (uint8_t) (extconfig->xf_id1 & 0xff); + mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + /* EID8 - EID15 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0xff00) >> 8); + mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + /* EID16 - EID17 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0x30000) >> 16); + + /* STD0 - STD2*/ + + regval = (regval) | (uint8_t) (((extconfig->xf_id1 & 0x1C0000) >> 16) << 3); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + /* STD3 - STD10 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0x1fe00000 ) >> 21); + regval |= RXFSIDL_EXIDE; + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + /* Setup the Mask */ + + /* EID0 - EID7 */ + + regval = (uint8_t) (extconfig->xf_id2 & 0xff); + mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1); + + /* EID8 - EID15 */ + + regval = (uint8_t) ((extconfig->xf_id2 & 0xff00) >> 8); + mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1); + + /* EID16 - EID17 */ + + regval = (uint8_t) ((extconfig->xf_id2 & 0x30000) >> 16); + + /* STD0 - STD2*/ + + regval = (regval) | (uint8_t) (((extconfig->xf_id2 & 0x1c0000) >> 16) << 3); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1); + + /* STD3 - STD10 */ + + regval = (uint8_t) ((extconfig->xf_id2 & 0x1fe00000 ) >> 21); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1); + } + else + { + /* The IDs will be filtered only by the Filter register (Mask == Filter) */ + + /* Setup the Filter */ + + /* EID0 - EID7 */ + + regval = (uint8_t) (extconfig->xf_id1 & 0xff); + mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1); + + /* EID8 - EID15 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0xff00) >> 8); + mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1); + + /* EID16 - EID17 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0x30000) >> 16); + + /* STD0 - STD2 */ + + regval = (regval) | (uint8_t) (((extconfig->xf_id1 & + 0x1c0000) >> 16) << 3) | RXFSIDL_EXIDE; + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1); + + /* STD3 - STD10 */ + + regval = (uint8_t) ((extconfig->xf_id1 & 0x1fe00000 ) >> 21); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset, ®val, 1); + } + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + mcp2515_dev_unlock(priv); + return ndx; + } + } + + DEBUGASSERT(priv->nstdalloc == priv->config->nstdfilters); + mcp2515_dev_unlock(priv); + return -EAGAIN; +} +#endif + +/**************************************************************************** + * Name: mcp2515_del_extfilter + * + * Description: + * Remove an address filter for a standard 29 bit address. + * + * Input Parameters: + * priv - An instance of the MCP2515 driver state structure. + * ndx - The filter index previously returned by the mcp2515_add_extfilter(). + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the error. + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_EXTID +static int mcp2515_del_extfilter(FAR struct mcp2515_can_s *priv, int ndx) +{ + FAR struct mcp2515_config_s *config; + uint8_t regval; + uint8_t offset; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Check Userspace Parameters */ + + DEBUGASSERT(ndx >= 0 || ndx < config->nfilters); + + caninfo("ndx = %d\n", ndx); + + if (ndx < 0 || ndx >= config->nfilters) + { + return -EINVAL; + } + + /* Get exclusive excess to the MCP2515 hardware */ + + mcp2515_dev_lock(priv); + + /* Check if this filter is really assigned */ + + if ((priv->filters & (1 << ndx)) == 0) + { + /* No, error out */ + + mcp2515_dev_unlock(priv); + return -ENOENT; + } + + /* Release the filter */ + + priv->filters &= ~(1 << ndx); + + DEBUGASSERT(priv->nalloc > 0); + priv->nalloc--; + + /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL, + * RXFnEID8 and RXFnEID0) using this formula: + * + * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ; + * maskN = RXM0reg + offset + */ + + if (ndx < 3) + { + offset = 0; + } + else + { + offset = 4; + } + + /* Setup the CONFIG Mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + /* Invalidate this filter, set its ID to 0 */ + + regval = 0; + mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + (ndx * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + (ndx * 4), ®val, 1); + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + mcp2515_dev_unlock(priv); + return OK; +} +#endif + +/**************************************************************************** + * Name: mcp2515_add_stdfilter + * + * Description: + * Add an address filter for a standard 11 bit address. + * + * Input Parameters: + * priv - An instance of the MCP2515 driver state structure. + * stdconfig - The configuration of the standard filter + * + * Returned Value: + * A non-negative filter ID is returned on success. Otherwise a negated + * errno value is returned to indicate the nature of the error. + * + ****************************************************************************/ + +static int mcp2515_add_stdfilter(FAR struct mcp2515_can_s *priv, + FAR struct canioc_stdfilter_s *stdconfig) +{ + FAR struct mcp2515_config_s *config; + uint8_t regval; + uint8_t offset; + uint8_t mode = CAN_FILTER_MASK; + int ndx; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Get exclusive excess to the MCP2515 hardware */ + + mcp2515_dev_lock(priv); + + /* Find an unused standard filter */ + + for (ndx = 0; ndx < config->nfilters; ndx++) + { + /* Is this filter assigned? */ + + if ((priv->filters & (1 << ndx)) == 0) + { + /* No, assign the filter */ + + DEBUGASSERT(priv->nalloc < priv->config->nfilters); + priv->filters |= (1 << ndx); + priv->nalloc++; + + /* Format and write filter */ + + DEBUGASSERT(stdconfig->sf_id1 <= CAN_MAX_STDMSGID); + + DEBUGASSERT(stdconfig->sf_id2 <= CAN_MAX_STDMSGID); + + /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL, + * RXFnEID8 and RXFnEID0) using this formula: + * + * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ; + * maskN = RXM0reg + offset + */ + + if (priv->nalloc <= 3) + { + offset = 0; + } + else + { + offset = 4; + } + +#if 0 + /* N.B. Buffer 0 is higher priority than Buffer 1 + * but to separate these messages we will make this + * driver more complex. So let to consider that the + * first 2 IDs inserted in the filter will have more + * priority than the latest 4 IDs*/ + + if (stdconfig->sf_prio == CAN_MSGPRIO_LOW) + { + /* Use RXB1 filters */ + } + else + { + /* Use RXB0 filters */ + } +#endif + + switch (stdconfig->sf_type) + { + default: + case CAN_FILTER_DUAL: + mode = CAN_FILTER_DUAL; + break; + + case CAN_FILTER_MASK: + mode = CAN_FILTER_MASK; + break; + + case CAN_FILTER_RANGE: + /* not supported */ + break; + } + + /* Setup the CONFIG Mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + if (mode == CAN_FILTER_DUAL) + { + /* The MSD IDs will be filtered by separated Mask and Filter */ + + /* Setup the Filter */ + + regval = (uint8_t) (((stdconfig->sf_id1) & 0x7f8) >> 3); + mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + regval = (uint8_t) ((stdconfig->sf_id1 & 0x07 ) << 5); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + + /* Setup the Mask */ + + regval = (uint8_t) (((stdconfig->sf_id2) & 0x7f8) >> 3); + mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, ®val, 1); + + regval = (uint8_t) ((stdconfig->sf_id2 & 0x07 ) << 5); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1); + } + else + { + /* The IDs will be filtered only by the Filter register (Mask == Filter) */ + + /* Setup the Filter */ + + regval = (uint8_t) (((stdconfig->sf_id1) & 0x7f8) >> 3); + mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0SIDH + offset, ®val, 1); + + regval = (uint8_t) ((stdconfig->sf_id1 & 0x07 ) << 5); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL + offset, ®val, 1); + } + + /* We need to clear the extended ID bits */ + + regval = 0; + mcp2515_writeregs(priv, MCP2515_RXF0EID0 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0EID8 + offset + + ((priv->nalloc - 1) * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0EID0 + offset, ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0EID8 + offset, ®val, 1); + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + mcp2515_dev_unlock(priv); + return ndx; + } + } + + DEBUGASSERT(priv->nstdalloc == priv->config->nstdfilters); + mcp2515_dev_unlock(priv); + return -EAGAIN; +} + +/**************************************************************************** + * Name: mcp2515_del_stdfilter + * + * Description: + * Remove an address filter for a standard 29 bit address. + * + * Input Parameters: + * priv - An instance of the MCP2515 driver state structure. + * ndx - The filter index previously returned by the mcp2515_add_stdfilter(). + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise a negated errno value is + * returned to indicate the nature of the error. + * + ****************************************************************************/ + +static int mcp2515_del_stdfilter(FAR struct mcp2515_can_s *priv, int ndx) +{ + FAR struct mcp2515_config_s *config; + uint8_t regval; + uint8_t offset; + + DEBUGASSERT(priv != NULL && priv->config != NULL); + config = priv->config; + + /* Check Userspace Parameters */ + + DEBUGASSERT(ndx >= 0 || ndx < config->nfilters); + + caninfo("ndx = %d\n", ndx); + + if (ndx < 0 || ndx >= config->nfilters) + { + return -EINVAL; + } + + /* Get exclusive excess to the MCP2515 hardware */ + + mcp2515_dev_lock(priv); + + /* Check if this filter is really assigned */ + + if ((priv->filters & (1 << ndx)) == 0) + { + /* No, error out */ + + mcp2515_dev_unlock(priv); + return -ENOENT; + } + + /* Release the filter */ + + priv->filters &= ~(1 << ndx); + + DEBUGASSERT(priv->nalloc > 0); + priv->nalloc--; + + /* We can reach all RXFn registers (RXFnSIDH, RXFnSIDL, + * RXFnEID8 and RXFnEID0) using this formula: + * + * filterN = RXF0reg + offset + ((priv->nalloc - 1) * 4) ; + * maskN = RXM0reg + offset + */ + + if (ndx < 3) + { + offset = 0; + } + else + { + offset = 4; + } + + /* Setup the CONFIG Mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + /* Invalidade this filter, set its ID to 0 */ + + regval = 0; + mcp2515_writeregs(priv, MCP2515_RXF0SIDH + offset + (ndx * 4), ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXF0SIDL + offset + (ndx * 4), ®val, 1); + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + mcp2515_dev_unlock(priv); + return OK; +} + +/**************************************************************************** + * Name: mcp2515_reset_lowlevel + * + * Description: + * Reset the MCP2515 device. Called early to initialize the hardware. This + * function is called, before mcp2515_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_reset_lowlevel(FAR struct mcp2515_can_s *priv) +{ + FAR struct mcp2515_config_s *config; + + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + UNUSED(config); + + /* Get exclusive access to the MCP2515 peripheral */ + + mcp2515_dev_lock(priv); + + /* Send SPI reset command to MCP2515 */ + + SPI_LOCK(config->spi, true); + SPI_SELECT(config->spi, SPIDEV_CANBUS(0), true); + SPI_SEND(config->spi, MCP2515_RESET); + SPI_LOCK(config->spi, false); + + /* Wait 1ms to let MCP2515 restart */ + + usleep(1000); + + /* Make sure that all buffers are released. + * + * REVISIT: What if a thread is waiting for a buffer? The following + * will not wake up any waiting threads. + */ + + sem_destroy(&priv->txfsem); + sem_init(&priv->txfsem, 0, config->ntxbuffers); + + /* Define the current state and unlock */ + + priv->state = MCP2515_STATE_RESET; + mcp2515_dev_unlock(priv); +} + +/**************************************************************************** + * Name: mcp2515_reset + * + * Description: + * Reset the MCP2515 device. Called early to initialize the hardware. This + * function is called, before mcp2515_setup() and on error conditions. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_reset(FAR struct can_dev_s *dev) +{ + FAR struct mcp2515_can_s *priv; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + + /* Execute the reset */ + + mcp2515_reset_lowlevel(priv); + +} + +/**************************************************************************** + * Name: mcp2515_setup + * + * Description: + * Configure the MCP2515. This method is called the first time that the MCP2515 + * device is opened. This will occur when the device file is first opened. + * This setup includes configuring and attaching MCP2515 interrupts. + * All MCP2515 interrupts are disabled upon return. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int mcp2515_setup(FAR struct can_dev_s *dev) +{ + FAR struct mcp2515_can_s *priv; + FAR struct mcp2515_config_s *config; + int ret; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + //caninfo("MCP2515%d pid: %d\n", config->port, config->pid); + + /* Get exclusive access to the MCP2515 peripheral */ + + mcp2515_dev_lock(priv); + + /* MCP2515 hardware initialization */ + + ret = mcp2515_hw_initialize(priv); + if (ret < 0) + { + canerr("ERROR: MCP2515%d H/W initialization failed: %d\n", config->devid, ret); + return ret; + } + + /* Attach the MCP2515 interrupt handler. */ + + ret = config->attach(config, (mcp2515_handler_t)mcp2515_interrupt, (FAR void *)dev); + if (ret < 0) + { + canerr("ERROR: Failed to attach to IRQ Handler!\n"); + return ret; + } + + /* Enable receive interrupts */ + + priv->state = MCP2515_STATE_SETUP; + mcp2515_rxint(dev, true); + + mcp2515_dev_unlock(priv); + return OK; +} + +/**************************************************************************** + * Name: mcp2515_shutdown + * + * Description: + * Disable the MCP2515. This method is called when the MCP2515 device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_shutdown(FAR struct can_dev_s *dev) +{ + /* Nothing to do here! */ +} + +/**************************************************************************** + * Name: mcp2515_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_rxint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct mcp2515_can_s *priv; + FAR struct mcp2515_config_s *config; + irqstate_t flags; + uint8_t regval; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + config = priv->config; + DEBUGASSERT(config); + + caninfo("CAN%d enable: %d\n", config->devid, enable); + + /* Enable/disable the receive interrupts */ + + flags = enter_critical_section(); + + mcp2515_readregs(priv, MCP2515_CANINTE, ®val, 1); + + if (enable) + { + regval |= priv->rxints | MCP2515_ERROR_INTS; + } + else + { + regval &= ~priv->rxints; + } + + mcp2515_writeregs(priv, MCP2515_CANINTE, ®val, 1); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: mcp2515_txint + * + * Description: + * Call to enable or disable TX interrupts. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_txint(FAR struct can_dev_s *dev, bool enable) +{ + FAR struct mcp2515_can_s *priv = dev->cd_priv; + irqstate_t flags; + uint8_t regval; + + DEBUGASSERT(priv && priv->config); + + caninfo("CAN%d enable: %d\n", priv->config->devid, enable); + + /* Enable/disable the receive interrupts */ + + flags = enter_critical_section(); + mcp2515_readregs(priv, MCP2515_CANINTE, ®val, 1); + + if (enable) + { + regval |= priv->txints | MCP2515_ERROR_INTS; + } + else + { + regval &= ~priv->txints; + } + + mcp2515_writeregs(priv, MCP2515_CANINTE, ®val, 1); + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: mcp2515_ioctl + * + * Description: + * All ioctl calls will be routed through this method + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int mcp2515_ioctl(FAR struct can_dev_s *dev, int cmd, unsigned long arg) +{ + FAR struct mcp2515_can_s *priv; + int ret = -ENOTTY; + + caninfo("cmd=%04x arg=%lu\n", cmd, arg); + + DEBUGASSERT(dev && dev->cd_priv); + priv = dev->cd_priv; + + /* Handle the command */ + + switch (cmd) + { + /* CANIOC_GET_BITTIMING: + * Description: Return the current bit timing settings + * Argument: A pointer to a write-able instance of struct + * canioc_bittiming_s in which current bit timing values + * will be returned. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 (ERROR) + * is returned with the errno variable set to indicate the + * nature of the error. + * Dependencies: None + */ + + case CANIOC_GET_BITTIMING: + { + FAR struct canioc_bittiming_s *bt = + (FAR struct canioc_bittiming_s *)arg; + uint8_t regval; + uint8_t brp; + + DEBUGASSERT(bt != NULL); + + mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1); + bt->bt_sjw = ((regval & CNF1_SJW_MASK) >> CNF1_SJW_SHIFT) + 1; + brp = ((regval & CNF1_BRP_MASK) >> CNF1_BRP_SHIFT) + 1; + + mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1); + bt->bt_tseg1 = ((regval & CNF2_PRSEG_MASK) >> CNF2_PRSEG_SHIFT) + 1; + bt->bt_tseg1 += ((regval & CNF2_PHSEG1_MASK) >> CNF2_PHSEG1_SHIFT) + 1; + + mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1); + bt->bt_tseg2 = ((regval & CNF3_PHSEG2_MASK) >> CNF3_PHSEG2_SHIFT) + 1; + + bt->bt_baud = MCP2515_CANCLK_FREQUENCY / brp / + (bt->bt_tseg1 + bt->bt_tseg2 + 1); + ret = OK; + } + break; + + /* CANIOC_SET_BITTIMING: + * Description: Set new current bit timing values + * Argument: A pointer to a read-able instance of struct + * canioc_bittiming_s in which the new bit timing values + * are provided. + * Returned Value: Zero (OK) is returned on success. Otherwise -1 (ERROR) + * is returned with the errno variable set to indicate the + * nature of the error. + * Dependencies: None + * + * REVISIT: There is probably a limitation here: If there are multiple + * threads trying to send CAN packets, when one of these threads reconfigures + * the bitrate, the MCP2515 hardware will be reset and the context of operation + * will be lost. Hence, this IOCTL can only safely be executed in quiescent + * time periods. + */ + + case CANIOC_SET_BITTIMING: + { + FAR const struct canioc_bittiming_s *bt = + (FAR const struct canioc_bittiming_s *)arg; + irqstate_t flags; + uint8_t brp; + uint8_t sjw; + uint8_t tseg1; + uint8_t tseg2; + uint8_t prseg; + uint8_t phseg1; + uint8_t regval; + + DEBUGASSERT(bt != NULL); + DEBUGASSERT(bt->bt_baud < MCP2515_CANCLK_FREQUENCY); + DEBUGASSERT(bt->bt_sjw > 0 && bt->bt_sjw <= 4); + DEBUGASSERT(bt->bt_tseg1 > 1 && bt->bt_tseg1 <= 16); + DEBUGASSERT(bt->bt_tseg2 > 1 && bt->bt_tseg2 <= 8); + DEBUGASSERT(bt->bt_tseg1 > bt->bt_tseg2); + DEBUGASSERT(bt->bt_tseg2 > bt->bt_sjw); + + /* Extract bit timing data */ + + tseg1 = bt->bt_tseg1 - 1; + tseg2 = bt->bt_tseg2 - 1; + sjw = bt->bt_sjw - 1; + + /* PRSEG = TSEG1 - PHSEG1 + * Because we don't have PHSEG1 then let us to assume: + * PHSEG1 == PHSEG2 (PHSEG2 = TSEG2) + * + * See more at: + * + * http://www.analog.com/en/analog-dialogue/articles/configure-can-bit-timing.html + */ + + phseg1 = tseg2; + prseg = tseg1 - phseg1; + + brp = (uint32_t) + (((float) MCP2515_CANCLK_FREQUENCY / + ((float)(tseg1 + tseg2 + 1) * (float)bt->bt_baud)) - 1); + + /* Save the value of the new bit timing register */ + + flags = enter_critical_section(); + + /* Setup the CONFIG Mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_CONFIG); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + /* Setup CNF1 register */ + + mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1); + regval = (regval & ~CNF1_BRP_MASK) | (brp << CNF1_BRP_SHIFT); + regval = (regval & ~CNF1_SJW_MASK) | ((sjw) << CNF1_SJW_SHIFT); + mcp2515_writeregs(priv, MCP2515_CNF1, ®val, 1); + + /* Setup CNF2 register */ + + mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1); + regval = (regval & ~CNF2_PRSEG_MASK) | ((prseg - 1) << CNF2_PRSEG_SHIFT); + regval = (regval & ~CNF2_PHSEG1_MASK) | (phseg1 << CNF2_PHSEG1_SHIFT); + regval = (regval | CNF2_SAM | CNF2_BTLMODE); + mcp2515_writeregs(priv, MCP2515_CNF2, ®val, 1); + + /* Setup CNF3 register */ + + mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1); + regval = (regval & ~CNF3_PHSEG2_MASK) | (tseg2 << CNF3_PHSEG2_SHIFT); + regval = (regval | CNF3_SOF); + mcp2515_writeregs(priv, MCP2515_CNF3, ®val, 1); + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + leave_critical_section(flags); + + ret = OK; + } + break; + +#ifdef CONFIG_CAN_EXTID + /* CANIOC_ADD_EXTFILTER: + * Description: Add an address filter for a extended 29 bit + * address. + * Argument: A reference to struct canioc_extfilter_s + * Returned Value: A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * variable set to indicate the nature of the error. + */ + + case CANIOC_ADD_EXTFILTER: + { + DEBUGASSERT(arg != 0); + ret = mcp2515_add_extfilter(priv, (FAR struct canioc_extfilter_s *)arg); + } + break; + + /* CANIOC_DEL_EXTFILTER: + * Description: Remove an address filter for a standard 29 bit address. + * Argument: The filter index previously returned by the + * CANIOC_ADD_EXTFILTER command + * Returned Value: Zero (OK) is returned on success. Otherwise -1 (ERROR) + * is returned with the errno variable set to indicate the + * nature of the error. + */ + + case CANIOC_DEL_EXTFILTER: + { + FAR int *ndx = (FAR int *)((uintptr_t)arg); + + DEBUGASSERT(*ndx <= priv->config->nfilters); + ret = mcp2515_del_extfilter(priv, (int)*ndx); + } + break; +#endif + + /* CANIOC_ADD_STDFILTER: + * Description: Add an address filter for a standard 11 bit + * address. + * Argument: A reference to struct canioc_stdfilter_s + * Returned Value: A non-negative filter ID is returned on success. + * Otherwise -1 (ERROR) is returned with the errno + * variable set to indicate the nature of the error. + */ + + case CANIOC_ADD_STDFILTER: + { + DEBUGASSERT(arg != 0); + ret = mcp2515_add_stdfilter(priv, (FAR struct canioc_stdfilter_s *)arg); + } + break; + + /* CANIOC_DEL_STDFILTER: + * Description: Remove an address filter for a standard 11 bit address. + * Argument: The filter index previously returned by the + * CANIOC_ADD_STDFILTER command + * Returned Value: Zero (OK) is returned on success. Otherwise -1 (ERROR) + * is returned with the errno variable set to indicate the + * nature of the error. + */ + + case CANIOC_DEL_STDFILTER: + { + FAR int *ndx = (FAR int *)((uintptr_t)arg); + + DEBUGASSERT(*ndx <= priv->config->nfilters); + ret = mcp2515_del_stdfilter(priv, (int)*ndx); + } + break; + + /* Unsupported/unrecognized command */ + + default: + canerr("ERROR: Unrecognized command: %04x\n", cmd); + break; + } + + return ret; +} + +/**************************************************************************** + * Name: mcp2515_remoterequest + * + * Description: + * Send a remote request + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int mcp2515_remoterequest(FAR struct can_dev_s *dev, uint16_t id) +{ + /* REVISIT: Remote request not implemented */ + + return -ENOSYS; +} + +/**************************************************************************** + * Name: mcp2515_send + * + * Description: + * Send one can message. + * + * One CAN-message consists of a maximum of 10 bytes. A message is + * composed of at least the first 2 bytes (when there are no data bytes). + * + * Byte 0: Bits 0-7: Bits 3-10 of the 11-bit CAN identifier + * Byte 1: Bits 5-7: Bits 0-2 of the 11-bit CAN identifier + * Bit 4: Remote Transmission Request (RTR) + * Bits 0-3: Data Length Code (DLC) + * Bytes 2-10: CAN data + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * Zero on success; a negated errno on failure + * + ****************************************************************************/ + +static int mcp2515_send(FAR struct can_dev_s *dev, FAR struct can_msg_s *msg) +{ + FAR struct mcp2515_can_s *priv; + FAR struct mcp2515_config_s *config; + uint8_t regval; + uint8_t offset; + unsigned int nbytes; + unsigned int i; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv && priv->config); + config = priv->config; + + caninfo("CAN%d\n", config->devid); + caninfo("CAN%d ID: %d DLC: %d\n", + config->devid, msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc); + + /* That that FIFO elements were configured. + * + * REVISIT: Dedicated TX buffers are not used by this driver. + */ + + DEBUGASSERT(config->ntxbuffers > 0); + + /* Select one empty transmit buffer */ + + mcp2515_readregs(priv, MCP2515_TXB0CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + offset = MCP2515_TX0_OFFSET; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB1CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + offset = MCP2515_TX1_OFFSET; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB2CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + offset = MCP2515_TX2_OFFSET; + } + else + { + canerr("ERROR: No available transmit buffer!\n"); + return -EBUSY; + } + } + } + + /* Get exclusive access to the MCP2515 peripheral */ + + mcp2515_dev_lock(priv); + + /* Setup the MCP2515 TX Buffer with the message to send */ + +#ifdef CONFIG_CAN_EXTID + if (msg->cm_hdr.ch_extid) + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_EXTMSGID); + + /* EID7 - EID0 */ + + regval = (msg->cm_hdr.ch_id & 0xff); + mcp2515_writeregs(priv, MCP2515_TXB0EID0 + offset, ®val, 1); + + /* EID15 - EID8 */ + + regval = (msg->cm_hdr.ch_id & 0xff00) >> 8; + mcp2515_writeregs(priv, MCP2515_TXB0EID8 + offset, ®val, 1); + + /* EID17 and EID16 */ + + regval = (msg->cm_hdr.ch_id & 0x30000) >> 16; + regval |= TXBSIDL_EXIDE; + + /* STD2 - STD0 */ + + regval |= (msg->cm_hdr.ch_id & 0x1c0000) >> 18; + mcp2515_writeregs(priv, MCP2515_TXB0SIDL + offset, ®val, 1); + + /* STD10 - STD3 */ + + regval = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21; + mcp2515_writeregs(priv, MCP2515_TXB0SIDH + offset, ®val, 1); + } + else +#endif + { + DEBUGASSERT(msg->cm_hdr.ch_id <= CAN_MAX_STDMSGID); + + /* Setup the Standard ID of the message to send */ + + /* STD10 - STD3 */ + + regval = (msg->cm_hdr.ch_id & 0x7f8) >> 3; + mcp2515_writeregs(priv, MCP2515_TXB0SIDH + offset, ®val, 1); + + /* STD2 - STD0 */ + + regval = (msg->cm_hdr.ch_id & 0x007) << 5; + mcp2515_writeregs(priv, MCP2515_TXB0SIDL + offset, ®val, 1); + } + + /* Setup the DLC */ + + regval = (msg->cm_hdr.ch_dlc & 0xf); + + if (msg->cm_hdr.ch_rtr) + { + regval |= TXBDLC_RTR; + } + + mcp2515_writeregs(priv, MCP2515_TXB0DLC + offset, ®val, 1); + + /* Fill the data buffer */ + + nbytes = msg->cm_hdr.ch_dlc; + + for (i = 0; i < nbytes; i++) + { + /* Little endian is assumed */ + + regval = msg->cm_data[i]; + mcp2515_writeregs(priv, MCP2515_TXB0D0 + offset + i, ®val, 1); + } + + /* And request to send the message */ + + mcp2515_readregs(priv, MCP2515_TXB0CTRL, ®val, 1); + regval |= TXBCTRL_TXREQ; + mcp2515_writeregs(priv, MCP2515_TXB0CTRL, ®val, 1); + + mcp2515_dev_unlock(priv); + + /* Report that the TX transfer is complete to the upper half logic. Of + * course, the transfer is not complete, but this early notification + * allows the upper half logic to free resources sooner. + * + * REVISIT: Should we disable interrupts? can_txdone() was designed to + * be called from an interrupt handler and, hence, may be unsafe when + * called from the tasking level. + */ + + (void)can_txdone(dev); + return OK; +} + +/**************************************************************************** + * Name: mcp2515_txready + * + * Description: + * Return true if the MCP2515 hardware can accept another TX message. + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if the MCP2515 hardware is ready to accept another TX message. + * + ****************************************************************************/ + +static bool mcp2515_txready(FAR struct can_dev_s *dev) +{ + FAR struct mcp2515_can_s *priv; + uint8_t regval; + bool empty; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + + /* That that FIFO elements were configured. + * + * REVISIT: Dedicated TX buffers are not used by this driver. + */ + + DEBUGASSERT(config->ntxbuffers > 0); + + /* Get exclusive access to the MCP2515 peripheral */ + + mcp2515_dev_lock(priv); + + /* Select one empty transmit buffer */ + + mcp2515_readregs(priv, MCP2515_TXB0CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + empty = true; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB1CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + empty = true; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB2CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) == 0) + { + empty = true; + } + else + { + empty = false; + } + } + } + + mcp2515_dev_unlock(priv); + + return empty; +} + +/**************************************************************************** + * Name: mcp2515_txempty + * + * Description: + * Return true if all message have been sent. If for example, the MCP2515 + * hardware implements FIFOs, then this would mean the transmit FIFO is + * empty. This method is called when the driver needs to make sure that + * all characters are "drained" from the TX hardware before calling + * co_shutdown(). + * + * Input Parameters: + * dev - An instance of the "upper half" can driver state structure. + * + * Returned Value: + * True if there are no pending TX transfers in the MCP2515 hardware. + * + ****************************************************************************/ + +static bool mcp2515_txempty(FAR struct can_dev_s *dev) +{ + FAR struct mcp2515_can_s *priv; + uint8_t regval; + bool empty; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + + /* That that FIFO elements were configured. + * + * REVISIT: Dedicated TX buffers are not used by this driver. + */ + + DEBUGASSERT(config->ntxbuffers > 0); + + /* Get exclusive access to the MCP2515 peripheral */ + + mcp2515_dev_lock(priv); + + /* Select one empty transmit buffer */ + + mcp2515_readregs(priv, MCP2515_TXB0CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) != 0) + { + empty = false; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB1CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) != 0) + { + empty = false; + } + else + { + mcp2515_readregs(priv, MCP2515_TXB2CTRL, ®val, 1); + if ((regval & TXBCTRL_TXREQ) != 0) + { + empty = false; + } + else + { + empty = true; + } + } + } + + mcp2515_dev_unlock(priv); + + return empty; +} + +/**************************************************************************** + * Name: mcp2515_error + * + * Description: + * Report a CAN error + * + * Input Parameters: + * dev - CAN-common state data + * status - Interrupt status with error bits set + * oldstatus - Previous Interrupt status with error bits set + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_CAN_ERRORS +static void mcp2515_error(FAR struct can_dev_s *dev, uint8_t status, + uint8_t oldstatus) +{ + FAR struct mcp2515_can_s *priv = dev->cd_priv; + struct can_hdr_s hdr; + uint8_t eflg; + uint8_t txerr; + uint8_t txb0err; + uint8_t txb1err; + uint8_t txb2err; + uint16_t errbits; + uint8_t data[CAN_ERROR_DLC]; + int ret; + + /* Encode error bits */ + + errbits = 0; + memset(data, 0, sizeof(data)); + + /* Please note that MCP2515_CANINTF only reports if an error + * happened. It doesn't report what error it is. + * We need to check EFLG and TXBnCTRL to discover. + */ + + mcp2515_readregs(priv, MCP2515_EFLG, &eflg, 1); + if (eflg & EFLG_TXBO) + { + errbits |= CAN_ERROR_BUSOFF; + } + + if (eflg & EFLG_RXEP) + { + data[1] |= CAN_ERROR1_RXPASSIVE; + } + + if (eflg & EFLG_TXEP) + { + data[1] |= CAN_ERROR1_TXPASSIVE; + } + + if (eflg & EFLG_RXWAR) + { + data[1] |= CAN_ERROR1_RXWARNING; + } + + if (eflg & EFLG_TXWAR) + { + data[1] |= CAN_ERROR1_TXWARNING; + } + + if (eflg & (EFLG_RX0OVR | EFLG_RX1OVR)) + { + data[1] |= CAN_ERROR1_RXOVERFLOW; + } + + /* Verify Message Error */ + + mcp2515_readregs(priv, MCP2515_TXB0CTRL, &txb0err, 1); + mcp2515_readregs(priv, MCP2515_TXB1CTRL, &txb1err, 1); + mcp2515_readregs(priv, MCP2515_TXB2CTRL, &txb2err, 1); + + txerr = txb0err | txb1err | txb2err; + + if (txerr & (TXBCTRL_MLOA)) + { + errbits |= CAN_ERROR_LOSTARB; + } + + if (txerr & (TXBCTRL_ABTF)) + { + errbits |= CAN_ERROR_LOSTARB; + } + + if (txerr & (TXBCTRL_MLOA)) + { + data[0] |= CAN_ERROR0_UNSPEC; + } + + if ((status & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0) + { + /* If Message Error or Other error */ + + errbits |= CAN_ERROR_CONTROLLER; + } + else if ((oldstatus & (MCP2515_INT_ERR | MCP2515_INT_MERR)) != 0) + { + errbits |= CAN_ERROR_CONTROLLER; + } + + if (errbits != 0) + { + /* Format the CAN header for the error report. */ + + hdr.ch_id = errbits; + hdr.ch_dlc = CAN_ERROR_DLC; + hdr.ch_rtr = 0; + hdr.ch_error = 1; +#ifdef CONFIG_CAN_EXTID + hdr.ch_extid = 0; +#endif + hdr.ch_unused = 0; + + /* And provide the error report to the upper half logic */ + + ret = can_receive(dev, &hdr, data); + if (ret < 0) + { + canerr("ERROR: can_receive failed: %d\n", ret); + } + } +} +#endif /* CONFIG_CAN_ERRORS */ + +/**************************************************************************** + * Name: mcp2515_receive + * + * Description: + * Receive an MCP2515 messages + * + * Input Parameters: + * dev - CAN-common state data + * rxbuffer - The RX buffer containing the received messages + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void mcp2515_receive(FAR struct can_dev_s *dev, uint8_t offset) +{ + FAR struct mcp2515_can_s *priv; + struct can_hdr_s hdr; + int ret; + uint8_t regval; + uint8_t data[CAN_MAXDATALEN]; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv); + + /* Format the CAN header */ + + /* Get the CAN identifier. */ + + mcp2515_readregs(priv, MCP2515_RXB0SIDL + offset, ®val, 1); + +#ifdef CONFIG_CAN_EXTID + if ((regval & RXBSIDL_IDE) != 0) + { + + /* Save the extended ID of the newly received message */ + + /* EID7 - EID0 */ + + mcp2515_readregs(priv, MCP2515_RXB0EID0 + offset, ®val, 1); + hdr.ch_id = regval ; + + /* EID15 - EID8 */ + + mcp2515_readregs(priv, MCP2515_RXB0EID8 + offset, ®val, 1); + hdr.ch_id = hdr.ch_id | (regval << 8); + + /* EID17 and EID16 */ + + mcp2515_readregs(priv, MCP2515_RXB0SIDL + offset, ®val, 1); + hdr.ch_id = hdr.ch_id | ((regval & RXBSIDL_EID_MASK) << 16); + + /* STD2 - STD0 */ + + hdr.ch_id = hdr.ch_id | ((regval >> 5) << 18); + + /* STD10 - STD3 */ + + mcp2515_readregs(priv, MCP2515_RXB0SIDH + offset, ®val, 1); + hdr.ch_id = hdr.ch_id | (regval << 21); + hdr.ch_extid = true; + } + else + { + /* Save the standard ID of the newly received message */ + + mcp2515_readregs(priv, MCP2515_RXB0SIDH + offset, ®val, 1); + hdr.ch_id = regval; + mcp2515_readregs(priv, MCP2515_RXB0SIDL + offset, ®val, 1); + hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5); + hdr.ch_extid = false; + } + +#else + if ((regval & RXBSIDL_IDE) != 0) + { + /* Drop any messages with extended IDs */ + + canerr("ERROR: Extended MSG in Standard Mode\n"); + + return; + } + + /* Save the standard ID of the newly received message */ + + mcp2515_readregs(priv, MCP2515_RXB0SIDH + offset, ®val, 1); + hdr.ch_id = regval; + mcp2515_readregs(priv, MCP2515_RXB0SIDL + offset, ®val, 1); + hdr.ch_id = (hdr.ch_id << 3) | (regval >> 5); +#endif + +#ifdef CONFIG_CAN_ERRORS + hdr.ch_error = 0; /* Error reporting not supported */ +#endif + hdr.ch_unused = 0; + + /* Extract the RTR bit */ + + mcp2515_readregs(priv, MCP2515_RXB0CTRL, ®val, 1); + hdr.ch_rtr = (regval & RXBCTRL_RXRTR) != 0; + + /* Get the DLC */ + + mcp2515_readregs(priv, MCP2515_RXB0DLC, ®val, 1); + hdr.ch_dlc = (regval & RXBDLC_DLC_MASK) >> RXBDLC_DLC_SHIFT; + + /* Save the message data */ + + mcp2515_readregs(priv, MCP2515_RXB0D0, data, (uint8_t) hdr.ch_dlc); + + ret = can_receive(dev, &hdr, (FAR uint8_t *) data); + + if (ret < 0) + { + canerr("ERROR: can_receive failed: %d\n", ret); + } +} + +/**************************************************************************** + * Name: mcp2515_interrupt + * + * Description: + * Common MCP2515 interrupt handler + * + * Input Parameters: + * dev - CAN-common state data + * + * Returned Value: + * None + * + ****************************************************************************/ + +static int mcp2515_interrupt(FAR struct mcp2515_config_s *config, FAR void *arg) +{ + FAR struct can_dev_s *dev = (FAR struct can_dev_s *)arg; + FAR struct mcp2515_can_s *priv; + uint8_t ir; + uint8_t ie; + uint8_t pending; + bool handled; + + DEBUGASSERT(dev); + priv = dev->cd_priv; + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv && priv->config); + + /* Loop while there are pending interrupt events */ + + do + { + /* Get the set of pending interrupts. */ + + mcp2515_readregs(priv, MCP2515_CANINTF, &ir, 1); + mcp2515_readregs(priv, MCP2515_CANINTE, &ie, 1); + + pending = (ir & ie); + handled = false; + + /* Check for any errors */ + + if ((pending & MCP2515_ERROR_INTS) != 0) + { + /* Clear interrupt errors */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_ERROR_INTS, ~MCP2515_ERROR_INTS); + +#ifdef CONFIG_CAN_ERRORS + /* Report errors */ + + mcp2515_error(dev, pending & MCP2515_ERROR_INTS, priv->olderrors); + + priv->olderrors = (pending & MCP2515_ERROR_INTS); +#endif + handled = true; + } +#ifdef CONFIG_CAN_ERRORS + else if (priv->olderrors != 0) + { + /* All (old) errors cleared */ + + canerr("ERROR: CLEARED\n"); + + mcp2515_error(dev, 0, priv->olderrors); + + priv->olderrors = 0; + handled = true; + } +#endif + + /* Check for successful completion of a transmission */ + + if ((pending & MCP2515_TXBUFFER_INTS) != 0) + { + /* Clear the pending TX completion interrupt (and all + * other TX-related interrupts) + */ + + if (pending & MCP2515_INT_TX0) + { + caninfo("TX0 is empty to transmit new message!\n"); + + /* Clear TX0 interrupt */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_INT_TX0, ~MCP2515_INT_TX0); + } + + if (pending & MCP2515_INT_TX1) + { + caninfo("TX1 is empty to transmit new message!\n"); + + /* Clear TX1 interrupt */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_INT_TX1, ~MCP2515_INT_TX1); + } + + if (pending & MCP2515_INT_TX2) + { + caninfo("TX2 is empty to transmit new message!\n"); + + /* Clear TX2 interrupt */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_INT_TX2, ~MCP2515_INT_TX2); + } + + handled = true; + +#ifdef CONFIG_CAN_TXREADY + /* Inform the upper half driver that we are again ready to accept + * data in mcp2515_send(). + */ + + (void)can_txready(dev); +#endif + } + else if ((pending & priv->txints) != 0) + { + /* Clear unhandled TX events */ + + //mcp2515_putreg(priv, MCP2515_IR_OFFSET, priv->txints); + handled = true; + } + + /* Check if there is a new message to read */ + + if ((pending & MCP2515_RXBUFFER_INTS) != 0) + { + /* RX Buffer 0 is the "high priority" buffer: We will process + * all messages in RXB0 before processing any message from RX + * RXB1. + */ + + if ((pending & MCP2515_INT_RX0) != 0) + { + mcp2515_receive(dev, MCP2515_RX0_OFFSET); + + /* Clear RX0 interrupt */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_INT_RX0, ~MCP2515_INT_RX0); + } + else + { + if ((pending & MCP2515_INT_RX1) != 0) + { + mcp2515_receive(dev, MCP2515_RX1_OFFSET); + + /* Clear RX1 interrupt */ + + mcp2515_modifyreg(priv, MCP2515_CANINTF, MCP2515_INT_RX1, ~MCP2515_INT_RX1); + } + } + + /* Acknowledge reading the FIFO entry */ + handled = true; + } + } + while (handled); + + return OK; +} + +/**************************************************************************** + * Name: mcp2515_hw_initialize + * + * Description: + * MCP2515 hardware initialization + * + * Input Parameter: + * priv - A pointer to the private data structure for this MCP2515 peripheral + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int mcp2515_hw_initialize(struct mcp2515_can_s *priv) +{ + FAR struct mcp2515_config_s *config = priv->config; + uint8_t regval; + + caninfo("CAN%d\n", config->devid); + + /* Setup CNF1 register */ + + mcp2515_readregs(priv, MCP2515_CNF1, ®val, 1); + regval = (regval & ~CNF1_BRP_MASK) | (MCP2515_BRP << CNF1_BRP_SHIFT); + regval = (regval & ~CNF1_SJW_MASK) | ((MCP2515_SJW - 1) << CNF1_SJW_SHIFT); + mcp2515_writeregs(priv, MCP2515_CNF1, ®val, 1); + + /* Setup CNF2 register */ + + mcp2515_readregs(priv, MCP2515_CNF2, ®val, 1); + regval = (regval & ~CNF2_PRSEG_MASK) | ((MCP2515_PROPSEG - 1) << CNF2_PRSEG_SHIFT); + regval = (regval & ~CNF2_PHSEG1_MASK) | ((MCP2515_PHSEG1 - 1) << CNF2_PHSEG1_SHIFT); + regval = (regval | CNF2_SAM | CNF2_BTLMODE); + mcp2515_writeregs(priv, MCP2515_CNF2, ®val, 1); + + /* Setup CNF3 register */ + + mcp2515_readregs(priv, MCP2515_CNF3, ®val, 1); + regval = (regval & ~CNF3_PHSEG2_MASK) | ((MCP2515_PHSEG2 - 1) << CNF3_PHSEG2_SHIFT); + regval = (regval | CNF3_SOF); + mcp2515_writeregs(priv, MCP2515_CNF3, ®val, 1); + + /* Mask all messages to be received */ + + mcp2515_readregs(priv, MCP2515_RXB0CTRL, ®val, 1); + regval = (regval & ~RXBCTRL_RXM_MASK) | (RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT); + regval = (regval | RXB0CTRL_BUKT); /* Enable Rollover from RXB0 to RXB1 */ + mcp2515_writeregs(priv, MCP2515_RXB0CTRL, ®val, 1); + + mcp2515_readregs(priv, MCP2515_RXB1CTRL, ®val, 1); + regval = (regval & ~RXBCTRL_RXM_MASK) | (RXBCTRL_RXM_ALLVALID << RXBCTRL_RXM_SHIFT); + mcp2515_writeregs(priv, MCP2515_RXB1CTRL, ®val, 1); + + regval = 0x00; + mcp2515_writeregs(priv, MCP2515_RXM0SIDH, ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0SIDL, ®val, 1); +#ifdef CONFIG_CAN_EXTID + mcp2515_writeregs(priv, MCP2515_RXM0EID8, ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM0EID0, ®val, 1); +#endif + + mcp2515_writeregs(priv, MCP2515_RXM1SIDH, ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM1SIDL, ®val, 1); +#ifdef CONFIG_CAN_EXTID + mcp2515_writeregs(priv, MCP2515_RXM1EID8, ®val, 1); + mcp2515_writeregs(priv, MCP2515_RXM1EID0, ®val, 1); +#endif + +#ifdef CONFIG_CAN_EXTID + mcp2515_modifyreg(priv, MCP2515_RXM0SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE); + mcp2515_modifyreg(priv, MCP2515_RXM1SIDL, RXFSIDL_EXIDE, RXFSIDL_EXIDE); +#endif + + /* Leave the Configuration mode, Move to Normal mode */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, ®val, 1); + regval = (regval & ~CANCTRL_REQOP_MASK) | (CANCTRL_REQOP_NORMAL); + mcp2515_writeregs(priv, MCP2515_CANCTRL, ®val, 1); + + usleep(100); + + /* Read the CANINTF */ + + mcp2515_readregs(priv, MCP2515_CANINTF, ®val, 1); + caninfo("CANINFT = 0x%02X\n", regval); + +#ifdef MCP2515_LOOPBACK + /* Is loopback mode selected for this peripheral? */ + + if (config->loopback) + { + /* To Be Implemented */ + } +#endif + + /* Configure interrupt lines */ + + /* Select RX-related interrupts */ + + priv->rxints = MCP2515_RXBUFFER_INTS; + + /* Select TX-related interrupts */ + + priv->txints = MCP2515_TXBUFFER_INTS; + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: mcp2515_instantiate + * + * Description: + * Initialize the selected MCP2515 CAN Bus Controller over SPI + * + * Input Parameter: + * config - The configuration structure passed by the board. + * + * Returned Value: + * Valid CAN device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct mcp2515_can_s *mcp2515_instantiate(FAR struct mcp2515_config_s *config) +{ + FAR struct mcp2515_can_s *priv; + uint8_t canctrl; + + caninfo("Starting mcp2515_instantiate()!\n"); + + priv = (FAR struct mcp2515_can_s *)kmm_malloc(sizeof(struct mcp2515_can_s)); + if (priv == NULL) + { + canerr("ERROR: Failed to allocate instance of mcp2515_can_s!\n"); + return NULL; + } + + /* Setup SPI frequency and mode */ + + SPI_SETFREQUENCY(config->spi, MCP2515_SPI_FREQUENCY); + SPI_SETMODE(config->spi, MCP2515_SPI_MODE); + SPI_SETBITS(config->spi, 8); + (void)SPI_HWFEATURES(config->spi, 0); + + /* Perform one time data initialization */ + + memset(priv, 0, sizeof(struct mcp2515_can_s)); + priv->config = config; + + /* Set the initial bit timing. This might change subsequently + * due to IOCTL command processing. + */ + + //priv->btp = config->btp; + //priv->fbtp = config->fbtp; + + /* Initialize semaphores */ + + sem_init(&priv->locksem, 0, 1); + sem_init(&priv->txfsem, 0, config->ntxbuffers); + + /* And put the hardware in the initial state */ + + mcp2515_reset_lowlevel(priv); + + /* Probe the MCP2515 to confirm it was detected */ + + mcp2515_readregs(priv, MCP2515_CANCTRL, &canctrl, 1); + + if (canctrl != DEFAULT_CANCTRL_CONFMODE) + { + canerr("ERROR: CANCTRL = 0x%02X ! It should be 0x87\n", canctrl); + return NULL; + } + + /* Return our private data structure as an opaque handle */ + + return (MCP2515_HANDLE)priv; +} + +/**************************************************************************** + * Name: mcp2515_initialize + * + * Description: + * Initialize the selected MCP2515 CAN Bus Controller over SPI + * + * Input Parameter: + * config - The configuration structure passed by the board. + * + * Returned Value: + * Valid CAN device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +FAR struct can_dev_s *mcp2515_initialize(FAR struct mcp2515_can_s *mcp2515can) +{ + FAR struct can_dev_s *dev; + + caninfo("Starting mcp2515_initialize()!\n"); + + /* Allocate a CAN Device structure */ + + dev = (FAR struct can_dev_s *)kmm_malloc(sizeof(struct can_dev_s)); + if (dev == NULL) + { + canerr("ERROR: Failed to allocate instance of can_dev_s!\n"); + return NULL; + } + + dev->cd_ops = &g_mcp2515ops; + dev->cd_priv = (FAR void *)mcp2515can; + + return dev; +} + +#endif /* CONFIG_CAN && CONFIG_MCP2515 */ diff --git a/drivers/can/mcp2515.h b/drivers/can/mcp2515.h new file mode 100644 index 00000000000..2b9c9815725 --- /dev/null +++ b/drivers/can/mcp2515.h @@ -0,0 +1,397 @@ +/* Register Addresses */ + +#define MCP2515_RXF0SIDH 0x00 +#define MCP2515_RXF0SIDL 0x01 +#define MCP2515_RXF0EID8 0x02 +#define MCP2515_RXF0EID0 0x03 +#define MCP2515_RXF1SIDH 0x04 +#define MCP2515_RXF1SIDL 0x05 +#define MCP2515_RXF1EID8 0x06 +#define MCP2515_RXF1EID0 0x07 +#define MCP2515_RXF2SIDH 0x08 +#define MCP2515_RXF2SIDL 0x09 +#define MCP2515_RXF2EID8 0x0a +#define MCP2515_RXF2EID0 0x0b +#define MCP2515_BFPCTRL 0x0c +#define MCP2515_TXRTSCTRL 0x0d +#define MCP2515_CANSTAT 0x0e +#define MCP2515_CANCTRL 0x0f +#define MCP2515_RXF3SIDH 0x10 +#define MCP2515_RXF3SIDL 0x11 +#define MCP2515_RXF3EID8 0x12 +#define MCP2515_RXF3EID0 0x13 +#define MCP2515_RXF4SIDH 0x14 +#define MCP2515_RXF4SIDL 0x15 +#define MCP2515_RXF4EID8 0x16 +#define MCP2515_RXF4EID0 0x17 +#define MCP2515_RXF5SIDH 0x18 +#define MCP2515_RXF5SIDL 0x19 +#define MCP2515_RXF5EID8 0x1a +#define MCP2515_RXF5EID0 0x1b +#define MCP2515_TEC 0x1c +#define MCP2515_REC 0x1d +#define MCP2515_RXM0SIDH 0x20 +#define MCP2515_RXM0SIDL 0x21 +#define MCP2515_RXM0EID8 0x22 +#define MCP2515_RXM0EID0 0x23 +#define MCP2515_RXM1SIDH 0x24 +#define MCP2515_RXM1SIDL 0x25 +#define MCP2515_RXM1EID8 0x26 +#define MCP2515_RXM1EID0 0x27 +#define MCP2515_CNF3 0x28 +#define MCP2515_CNF2 0x29 +#define MCP2515_CNF1 0x2a +#define MCP2515_CANINTE 0x2b +#define MCP2515_CANINTF 0x2c +#define MCP2515_EFLG 0x2d +#define MCP2515_TXB0CTRL 0x30 +#define MCP2515_TXB0SIDH 0x31 +#define MCP2515_TXB0SIDL 0x32 +#define MCP2515_TXB0EID8 0x33 +#define MCP2515_TXB0EID0 0x34 +#define MCP2515_TXB0DLC 0x35 +#define MCP2515_TXB0D0 0x36 +#define MCP2515_TXB0D1 0x37 +#define MCP2515_TXB0D2 0x38 +#define MCP2515_TXB0D3 0x39 +#define MCP2515_TXB0D4 0x3a +#define MCP2515_TXB0D5 0x3b +#define MCP2515_TXB0D6 0x3c +#define MCP2515_TXB0D7 0x3d +#define MCP2515_TXB1CTRL 0x40 +#define MCP2515_TXB1SIDH 0x41 +#define MCP2515_TXB1SIDL 0x42 +#define MCP2515_TXB1EID8 0x43 +#define MCP2515_TXB1EID0 0x44 +#define MCP2515_TXB1DLC 0x45 +#define MCP2515_TXB1D0 0x46 +#define MCP2515_TXB1D1 0x47 +#define MCP2515_TXB1D2 0x48 +#define MCP2515_TXB1D3 0x49 +#define MCP2515_TXB1D4 0x4a +#define MCP2515_TXB1D5 0x4b +#define MCP2515_TXB1D6 0x4c +#define MCP2515_TXB1D7 0x4d +#define MCP2515_TXB2CTRL 0x50 +#define MCP2515_TXB2SIDH 0x51 +#define MCP2515_TXB2SIDL 0x52 +#define MCP2515_TXB2EID8 0x53 +#define MCP2515_TXB2EID0 0x54 +#define MCP2515_TXB2DLC 0x55 +#define MCP2515_TXB2D0 0x56 +#define MCP2515_TXB2D1 0x57 +#define MCP2515_TXB2D2 0x58 +#define MCP2515_TXB2D3 0x59 +#define MCP2515_TXB2D4 0x5a +#define MCP2515_TXB2D5 0x5b +#define MCP2515_TXB2D6 0x5c +#define MCP2515_TXB2D7 0x5d +#define MCP2515_RXB0CTRL 0x60 +#define MCP2515_RXB0SIDH 0x61 +#define MCP2515_RXB0SIDL 0x62 +#define MCP2515_RXB0EID8 0x63 +#define MCP2515_RXB0EID0 0x64 +#define MCP2515_RXB0DLC 0x65 +#define MCP2515_RXB0D0 0x66 +#define MCP2515_RXB0D1 0x67 +#define MCP2515_RXB0D2 0x68 +#define MCP2515_RXB0D3 0x69 +#define MCP2515_RXB0D4 0x6a +#define MCP2515_RXB0D5 0x6b +#define MCP2515_RXB0D6 0x6c +#define MCP2515_RXB0D7 0x6d +#define MCP2515_RXB1CTRL 0x70 +#define MCP2515_RXB1SIDH 0x71 +#define MCP2515_RXB1SIDL 0x72 +#define MCP2515_RXB1EID8 0x73 +#define MCP2515_RXB1EID0 0x74 +#define MCP2515_RXB1DLC 0x75 +#define MCP2515_RXB1D0 0x76 +#define MCP2515_RXB1D1 0x77 +#define MCP2515_RXB1D2 0x78 +#define MCP2515_RXB1D3 0x79 +#define MCP2515_RXB1D4 0x7a +#define MCP2515_RXB1D5 0x7b +#define MCP2515_RXB1D6 0x7c +#define MCP2515_RXB1D7 0x7d + +/* Offset to simplify mcp2515_receive() function */ + +#define MCP2515_RX0_OFFSET 0x00 +#define MCP2515_RX1_OFFSET 0x10 + +/* Offset to simplify mcp2515_send() function */ + +#define MCP2515_TX0_OFFSET 0x00 +#define MCP2515_TX1_OFFSET 0x10 +#define MCP2515_TX2_OFFSET 0x20 + +/* CANCTRL: CAN CONTROL REGISTER */ + +#define CANCTRL_CLKPRE_SHIFT (0) /* Bits 0-1: CLKOUT Pin Prescaler bits */ +#define CANCTRL_CLKPRE_MASK (3 << CANCTRL_CLKPRE_SHIFT) +#define CANCTRL_CLKEN (1 << 2) /* Bit 2: CLKOUT Pin Enable bit */ +#define CANCTRL_OSM (1 << 3) /* Bit 3: One-Shot Mode bit */ +#define CANCTRL_ABAT (1 << 4) /* Bit 4: Abort All Pending Transmissions bit */ +#define CANCTRL_REQOP_SHIFT (5) /* Bits 5-7: Request Operation Mode bits */ +#define CANCTRL_REQOP_MASK (7 << CANCTRL_REQOP_SHIFT) +#define CANCTRL_REQOP_NORMAL (0 << CANCTRL_REQOP_SHIFT) +#define CANCTRL_REQOP_SLEEP (1 << CANCTRL_REQOP_SHIFT) +#define CANCTRL_REQOP_LOOPBK (2 << CANCTRL_REQOP_SHIFT) +#define CANCTRL_REQOP_LISTEN (3 << CANCTRL_REQOP_SHIFT) +#define CANCTRL_REQOP_CONFIG (4 << CANCTRL_REQOP_SHIFT) + +/* TXBnCTRL – TRANSMIT BUFFER n CONTROL REGISTER */ + +#define TXBCTRL_TXP_SHIFT (0) /* Bits 0-1: Transmit Buffer Priority */ +#define TXBCTRL_TXP_MASK (3 << MCP2515_TXBCTRL_TXP_SHIFT) + /* Bit 2: Not used */ +#define TXBCTRL_TXREQ (1 << 3) /* Bit 3: Message Transmit Request bit */ +#define TXBCTRL_TXERR (1 << 4) /* Bit 4: Transmission Error Detected bit */ +#define TXBCTRL_MLOA (1 << 5) /* Bit 5: Message Lost Arbitration bit */ +#define TXBCTRL_ABTF (1 << 6) /* Bit 6: Message Aborted Flag bit */ + /* Bit 7: Not used */ + +/* TXRTSCTRL – TXnRTS PIN CONTROL AND STATUS REGISTER */ + +#define TXRTSCTRL_B0RTSM (1 << 0) /* Bit 0: TX0RTS Pin mode bit */ +#define TXRTSCTRL_B1RTSM (1 << 1) /* Bit 1: TX1RTS Pin mode bit */ +#define TXRTSCTRL_B2RTSM (1 << 2) /* Bit 2: TX2RTS Pin mode bit */ +#define TXRTSCTRL_B0RTS (1 << 3) /* Bit 3: TX0RTS Pin State bit */ +#define TXRTSCTRL_B1RTS (1 << 4) /* Bit 4: TX1RTS Pin State bit */ +#define TXRTSCTRL_B2RTS (1 << 5) /* Bit 5: TX2RTS Pin State bit */ + /* Bit 6-7: Not used */ + +/* TXBnSIDH – TRANSMIT BUFFER n STANDARD IDENTIFIER HIGH */ + +#define TXBSIDH_SID_MASK 0xff /* Standard Identifier bits <10:3> */ + + +/* TXBnSIDL – TRANSMIT BUFFER n STANDARD IDENTIFIER LOW */ + +#define TXBSIDL_SID_SHIFT (5) /* Bits 5-7: Standard Identifier bits <2:0> */ +#define TXBSIDL_SID_MASK (0x7 << TXBSIDL_SID_SHIFT) +#define TXBSIDL_EXIDE (1 << 3) /* Bit 3: Extended Identifier Enable bit */ +#define TXBSIDL_EID_SHIFT (0) /* Bits 0-1: Extended Identifier bits <17:16> */ +#define TXBSIDL_EID_MASK (0x03 << TXBSIDL_EID_MASK) + + +/* TXBnEID8 – TRANSMIT BUFFER n EXTENDED IDENTIFIER HIGH */ + +#define TXBEID8_EID_MASK 0xff /* Bits 0-7: Extended Identifier bits <15:8> */ + +/* TXBnEID0 – TRANSMIT BUFFER n EXTENDED IDENTIFIER LOW */ + +#define TXBEID0_EID_MASK 0xff /* Bits 0-7: Extended Identifier bits <7:0> */ + +/* TXBnDLC - TRANSMIT BUFFER n DATA LENGTH CODE */ + +#define TXBDLC_DLC_SHIFT (0) /* Bits 0-3: Data Length Code <3:0> bits */ +#define TXBDLC_DLC_MASK (0xf << TXBDLC_DLC_SHIFT) +#define TXBDLC_RTR (1 << 6) /* Bit 6: Remote Transmission Request bit */ + +/* TXBnDm – TRANSMIT BUFFER n DATA BYTE m */ + +#define TXBD_D0 (1 << 0) /* Bit 0: Transmit Buffer n Data Field Bytes 0 */ +#define TXBD_D1 (1 << 1) /* Bit 1: Transmit Buffer n Data Field Bytes 1 */ +#define TXBD_D2 (1 << 2) /* Bit 2: Transmit Buffer n Data Field Bytes 2 */ +#define TXBD_D3 (1 << 3) /* Bit 3: Transmit Buffer n Data Field Bytes 3 */ +#define TXBD_D4 (1 << 4) /* Bit 4: Transmit Buffer n Data Field Bytes 4 */ +#define TXBD_D5 (1 << 5) /* Bit 5: Transmit Buffer n Data Field Bytes 5 */ +#define TXBD_D6 (1 << 6) /* Bit 6: Transmit Buffer n Data Field Bytes 6 */ +#define TXBD_D7 (1 << 7) /* Bit 7: Transmit Buffer n Data Field Bytes 7 */ + +/* RXB0CTRL – RECEIVE BUFFER 0 CONTROL */ + +#define RXB0CTRL_FILHIT (1 << 0) /* Bit 0: Filter Hit bit - 1 = Msg was accepted by Filter 1; 0 = Filter 0 */ +#define RXB0CTRL_BUKT1 (1 << 1) /* Bit 1: Read-only Copy of BUKT bit (used internally by the MCP2515) */ +#define RXB0CTRL_BUKT (1 << 2) /* Bit 2: Rollover Enable bit */ + +/* These bits are common to RXB0 and RXB1: */ + +#define RXBCTRL_RXRTR (1 << 3) /* Bit 3: Received Remote Transfer Request bit */ + /* Bit 4: Not used */ +#define RXBCTRL_RXM_SHIFT (5) /* Bits 5-6: Receive Buffer Operating Mode bits */ +#define RXBCTRL_RXM_MASK (0x3 << RXBCTRL_RXM_SHIFT) +#define RXBCTRL_RXM_ALLMSG (3 << RXBCTRL_RXM_SHIFT) /* 11: Turn mask/filters off; receive any message */ +#define RXBCTRL_RXM_ALLVALID (0 << RXBCTRL_RXM_SHIFT) /* 00: Receive all valid msgs using (STD or EXT) that meet filter criteria */ + /* Bit 7: Not used */ + +/* N.B.: In the datasheet DS21801D the file RXM of RXBnCTRL could to assume + the value 01 and 10 to receive only STD or EXT msgs respectively. + But in a more recent datasheet DS20001801H it was removed. */ + +/* RXB1CTRL – RECEIVE BUFFER 1 CONTROL */ + +#define RXB1CTRL_FILHIT_SHIFT (0) /* Filter Hit bits - indicates which acceptance filter enabled reception of message */ +#define RXB1CTRL_FILHIT_MASK (0x7 << RXB0CTRL_FILHIT_SHIFT) +#define RXB1CTRL_FILHIT_F5 (5 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 5 (RXF5) */ +#define RXB1CTRL_FILHIT_F4 (4 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 4 (RXF4) */ +#define RXB1CTRL_FILHIT_F3 (3 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 3 (RXF3) */ +#define RXB1CTRL_FILHIT_F2 (2 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 2 (RXF2) */ +#define RXB1CTRL_FILHIT_F1 (1 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 1 (RXF1) (Only if BUKT bit set in RXB0CTRL) */ +#define RXB1CTRL_FILHIT_F0 (0 << RXB1CTRL_FILHIT_SHIFT) /* Acceptance Filter 0 (RXF0) (Only if BUKT bit set in RXB0CTRL) */ + +/* BFPCTRL – RXnBF PIN CONTROL AND STATUS */ + +#define BFPCTRL_B0BFM (1 << 0) /* Bit 0: RX0BF Pin Operation Mode bit */ +#define BFPCTRL_B1BFM (1 << 1) /* Bit 1: RX1BF Pin Operation Mode bit */ +#define BFPCTRL_B0BFE (1 << 2) /* Bit 2: RX0BF Pin Function Enable bit */ +#define BFPCTRL_B1BFE (1 << 3) /* Bit 3: RX1BF Pin Function Enable bit */ +#define BFPCTRL_B0BFS (1 << 4) /* Bit 4: RX0BF Pin State bit (Digital Output mode only) */ +#define BFPCTRL_B1BFS (1 << 5) /* Bit 5: RX1BF Pin State bit (Digital Output mode only) */ + /* Bits 6-7: Not used */ + +/* RXBnSIDH – RECEIVE BUFFER n STANDARD IDENTIFIER HIGH */ + +#define RXBSIDH_SID_MASK 0xff /* Standard Identifier bits <10:3> */ + +/* RXBnSIDL – RECEIVE BUFFER n STANDARD IDENTIFIER LOW */ + +#define RXBSIDL_SID_SHIFT (5) /* Bits 5-7: Standard Identifier bits <2:0> */ +#define RXBSIDL_SID_MASK (0x7 << RXBSIDL_SID_SHIFT) +#define RXBSIDL_SRR (1 << 4) /* Bit 4: Standard Frame Remote Transmit Request bit (valid only if IDE bit = '0')*/ +#define RXBSIDL_IDE (1 << 3) /* Bit 3: Extended Identifier Message received */ + /* Bit 2: Not used */ +#define RXBSIDL_EID_SHIFT (0) /* Bits 0-1: Extended Identifier bits <17:16> */ +#define RXBSIDL_EID_MASK (0x03 << RXBSIDL_EID_SHIFT) + +/* RXBnEID8 – RECEIVE BUFFER n EXTENDED IDENTIFIER HIGH */ + +#define RXBEID8_EID_MASK 0xff /* Bits 0-7: Extended Identifier bits <15:8> */ + +/* RXBnEID0 – RECEIVE BUFFER n EXTENDED IDENTIFIER LOW */ + +#define RXBEID0_EID_MASK 0xff /* Bits 0-7: Extended Identifier bits <7:0> */ + +/* RXBnDLC – RECEIVE BUFFER n DATA LENGHT CODE */ + +#define RXBDLC_DLC_SHIFT (0) /* Bits 0-3: Data Length Code <3:0> bits */ +#define RXBDLC_DLC_MASK (0xf << RXBDLC_DLC_SHIFT) +#define RXBDLC_RB0 (1 << 4) /* Bit 4: Reserved bit 0 */ +#define RXBDLC_RB1 (1 << 5) /* Bit 5: Reserved bit 1 */ +#define RXBDLC_RTR (1 << 6) /* Bit 6: Remote Transmission Request bit */ + /* Bit 7: Not used */ + +/* RXFnSIDH – FILTER n STANDARD IDENTIFIER HIGH */ + +#define RXFSIDH_SID_MASK 0xff /* Standard Identifier Filter bits <10:3> */ + +/* RXFnSIDL – FILTER n STANDARD IDENTIFIER LOW */ + +#define RXFSIDL_EID_SHIFT (0) /* Bit 0-1: Extended Identifier Filter bits <17:16> */ +#define RXFSIDL_EID_MASK (3 << RXFSIDL_EID_SHIFT) +#define RXFSIDL_EXIDE (1 << 3) /* Bit 3: Extended Identifier Enable bit */ +#define RXFSIDL_SID_SHIFT (5) /* Bits 5-7: Standard Identifier Filter bits <2:0> */ +#define RXFSIDL_SID_MASK (0x7 << RXFSIDL_SID_SHIFT) + +/* RXFnEID8 – FILTER n EXTENDED IDENTIFIER HIGH */ + +#define RXFEID8_EID_MASK 0xff /* Extended Identifier bits <15:8> */ + +/* RXFnEID0 – FILTER n EXTENDED IDENTIFIER LOW */ + +#define RXFEID0_EID_MASK 0xff /* Extended Identifier bits <7:0> */ + +/* RXMnSIDH – MASK n STANDARD IDENTIFIER HIGH */ + +#define RXMSIDH_SID_MASK 0xff /* Standard Identifier Mask bits <10:3> */ + +/* RXMnSIDL – MASK n STANDARD IDENTIFIER LOW */ + +#define RXMSIDL_EID_SHIFT (0) /* Bits 0-1: Extended Identifier Mask bits <17:16> */ +#define RXMSIDL_EID_MASK (3 << RXMSIDH_EID_SHIFT) +#define RXMSIDL_SID_SHIFT (5) /* Bits 5-7: Standard Identifier Mask bits <2:0> */ +#define RXMSIDL_MASK (7 << RXMSIDH_SID_SHIFT) + +/* RXMnEID8 – MASK n EXTENDED IDENTIFIER HIGH */ + +#define RXMEID8_EID_MASK 0xff /* Extended Identifier bits <15:8> */ + +/* RXMnEID0 – MASK n EXTENDED IDENTIFIER LOW */ + +#define RXMEID0_EID_MASK 0xff /* Extended Identifier Mask bits <7:0> */ + +/* CNF1 – CONFIGURATION 1 */ + +#define CNF1_BRP_SHIFT (0) /* Bits 0-5: Baud Rate Prescaler bits <5:0>, TQ = 2 x (BRP + 1)/Fosc */ +#define CNF1_BRP_MASK (0x3f << CNF1_BRP_SHIFT) +#define CNF1_SJW_SHIFT (6) /* Bit 6-7: Synchronization Jump Width Length bits <1:0> */ +#define CNF1_SJW_MASK (3 << CNF1_SJW_SHIFT) +# define CNF1_SJW_4xTQ (3 << CNF1_SJW_SHIFT) /* Length = 4 x TQ */ +# define CNF1_SJW_3xTQ (2 << CNF1_SJW_SHIFT) /* Length = 3 x TQ */ +# define CNF1_SJW_2xTQ (1 << CNF1_SJW_SHIFT) /* Length = 2 x TQ */ +# define CNF1_SJW_1xTQ (0 << CNF1_SJW_SHIFT) /* Length = 1 x TQ */ + +/* CNF2 – CONFIGURATION 2 */ + +#define CNF2_PRSEG_SHIFT (0) /* Bits 0-2: Propagation Segment Length bits <2:0>, (PRSEG + 1) x TQ */ +#define CNF2_PRSEG_MASK (7 << CNF2_PRSEG_SHIFT) +#define CNF2_PHSEG1_SHIFT (3) /* Bits 3-5: PS1 Length bits <2:0>, (PHSEG1 + 1) x TQ */ +#define CNF2_PHSEG1_MASK (7 << CNF2_PHSEG1_SHIFT) +#define CNF2_SAM (1 << 6) /* Bit 6: Sample Point Configuration bit */ +#define CNF2_BTLMODE (1 << 7) /* Bit 7: PS2 Bit Time Length bit */ + +/* CNF3 - CONFIGURATION 3 */ + +#define CNF3_PHSEG2_SHIFT (0) /* Bits 0-2: PS2 Length bits<2:0>, (PHSEG2 + 1) x TQ */ +#define CNF3_PHSEG2_MASK (7 << CNF3_PHSEG2_SHIFT) +#define CNF3_WAKFIL (1 << 6) /* Bit 3: Wake-up Filter bit */ +#define CNF3_SOF (1 << 7) /* Bit 7: Start-of-Frame signal bit */ + +/* TEC – TRANSMIT ERROR COUNTER */ + +#define TEC_MASK 0xff /* Transmit Error Count bits <7:0> */ + +/* REC – RECEIVER ERROR COUNTER */ + +#define REC_MASK 0xff /* Receive Error Count bits <7:0> */ + +/* EFLG – ERROR FLAG */ + +#define EFLG_EWARN (1 << 0) /* Bit 0: Error Warning Flag bit */ +#define EFLG_RXWAR (1 << 1) /* Bit 1: Receive Error Warning Flag bit */ +#define EFLG_TXWAR (1 << 2) /* Bit 2: Transmit Error Warning Flag bit */ +#define EFLG_RXEP (1 << 3) /* Bit 3: Receive Error-Passive Flag bit */ +#define EFLG_TXEP (1 << 4) /* Bit 4: Transmit Error-Passive Flag bit */ +#define EFLG_TXBO (1 << 5) /* Bit 5: Bus-Off Error Flag bit */ +#define EFLG_RX0OVR (1 << 6) /* Bit 6: Receive Buffer 0 Overflow Flag bit */ +#define EFLG_RX1OVR (1 << 7) /* Bit 7: Receive Buffer 1 Overflow Flag bit */ + +/* CANINTE/CANINTF – INTERRUPT ENABLE/FLAG */ + +#define MCP2515_INT_RX0 (1 << 0) /* Bit 0: Receive Buffer 0 Full Interrupt Enable bit */ +#define MCP2515_INT_RX1 (1 << 1) /* Bit 1: Receive Buffer 1 Full Interrupt Enable bit */ +#define MCP2515_INT_TX0 (1 << 2) /* Bit 2: Transmit Buffer 0 Empty Interrupt Enable bit */ +#define MCP2515_INT_TX1 (1 << 3) /* Bit 3: Transmit Buffer 1 Empty Interrupt Enable bit */ +#define MCP2515_INT_TX2 (1 << 4) /* Bit 4: Transmit Buffer 2 Empty Interrupt Enable bit */ +#define MCP2515_INT_ERR (1 << 5) /* Bit 5: Error Interrupt Enable bit (multiple sources in EFLG register) */ +#define MCP2515_INT_WAK (1 << 6) /* Bit 6: Wakeup Interrupt Enable bit */ +#define MCP2515_INT_MERR (1 << 7) /* Bit 7: Message Error Interrupt Enable bit */ + +/* MCP2515 SPI Instruction/Command byte */ + +#define MCP2515_RESET 0xC0 +#define MCP2515_READ 0x03 +#define MCP2515_READ_RX0 0x90 +#define MCP2515_READ_RX1 0x94 +#define MCP2515_WRITE 0x02 +#define MCP2515_LOAD_TX0 0x40 +#define MCP2515_LOAD_TX1 0x42 +#define MCP2515_LOAD_TX2 0x44 +#define MCP2515_RTS_TX0 0x81 +#define MCP2515_RTS_TX1 0x82 +#define MCP2515_RTS_TX2 0x84 +#define MCP2515_RTS_ALL 0x87 +#define MCP2515_READ_STATUS 0xA0 +#define MCP2515_RX_STATUS 0xB0 +#define MCP2515_BITMOD 0x05 + +/* CANCTRL register will be 0x87 after reset and in Conf. Mode */ + +#define DEFAULT_CANCTRL_CONFMODE 0x87 + +/* Crystal Frequency used on MCP2515 board */ + +#define MCP2515_CANCLK_FREQUENCY 8000000 + diff --git a/include/nuttx/can/mcp2515.h b/include/nuttx/can/mcp2515.h new file mode 100644 index 00000000000..bca2d0db736 --- /dev/null +++ b/include/nuttx/can/mcp2515.h @@ -0,0 +1,164 @@ +/**************************************************************************** + * include/nuttx/can/mcp2515.h + * + * Copyright (C) 2017 Alan Carvalho de Assis. All rights reserved. + * Author: Alan Carvalho de Assis + * + * 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. + * + ****************************************************************************/ + +#ifndef __INCLUDE_NUTTX_CAN_MCP2515_H +#define __INCLUDE_NUTTX_CAN_MCP2515_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +#if defined(CONFIG_CAN) && defined(CONFIG_CAN_MCP2515) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* SPI BUS PARAMETERS *******************************************************/ + +#define MCP2515_SPI_FREQUENCY (1000000) /* 1 MHz */ +#define MCP2515_SPI_MODE (SPIDEV_MODE0) /* Device uses SPI Mode 0: CPOL=0, CPHA=0 */ + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +typedef void (*mcp2515_handler_t)(FAR struct mcp2515_config_s *config, FAR void *arg); + +/* A reference to a structure of this type must be passed to the MCP2515 driver when the + * driver is instantiated. This structure provides information about the configuration of the + * MCP2515 and provides some board-specific hooks. + * + * Memory for this structure is provided by the caller. It is not copied by the driver + * and is presumed to persist while the driver is active. The memory must be writeable + * because, under certain circumstances, the driver may modify the frequency. + */ + +struct mcp2515_config_s +{ + struct spi_dev_s *spi; /* SPI used for MCP2515 communication */ + uint32_t baud; /* Configured baud */ + uint32_t btp; /* Bit timing/prescaler register setting */ + uint8_t devid; /* MCP2515 device ID */ + uint8_t mode; /* See enum mcp2515_canmod_e */ + uint8_t nfilters; /* Number of standard/extended filters */ + uint8_t ntxbuffers; /* Number of TX Buffer available */ + uint8_t txbuf0[10]; /* Transmit Buffer 0 */ + uint8_t txbuf1[10]; /* Transmit Buffer 1 */ + uint8_t txbuf2[10]; /* Transmit Buffer 2 */ +#ifdef MCP2515_LOOPBACK + bool loopback; /* True: Loopback mode */ +#endif + + /* Device characterization */ + + /* IRQ/GPIO access callbacks. These operations all hidden behind + * callbacks to isolate the ADXL345 driver from differences in GPIO + * interrupt handling by varying boards and MCUs. + * + * attach - Attach the ADXL345 interrupt handler to the GPIO interrupt + */ + + int (*attach)(FAR struct mcp2515_config_s *state, mcp2515_handler_t handler, + FAR void *arg); +}; + + +typedef FAR void *MCP2515_HANDLE; + +struct mcp2515_can_s; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: mcp2515_instantiate + * + * Description: + * Initialize a CAN Driver for MCP2515. + * + * Input Parameters: + * spi - An instance of the SPI interface to use to communicate with + * MCP2515 + * config - Describes the configuration of the MCP2515 part. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +FAR struct mcp2515_can_s *mcp2515_instantiate(FAR struct mcp2515_config_s *config); + +/**************************************************************************** + * Name: mcp2515_initialize + * + * Description: + * Initialize a CAN Driver for MCP2515. + * + * Input Parameters: + * spi - An instance of the SPI interface to use to communicate with + * MCP2515 + * config - Describes the configuration of the MCP2515 part. + * + * Returned Value: + * Zero (OK) on success; a negated errno value on failure. + * + ****************************************************************************/ + +FAR struct can_dev_s *mcp2515_initialize(FAR struct mcp2515_can_s *mcp2515can); + +#undef EXTERN +#ifdef __cplusplus +} +#endif + +#endif /* CONFIG_CAN && CONFIG_CAN_MCP2515 */ + +#endif /* __INCLUDE_NUTTX_CAN_MCP2515_H */