/**************************************************************************** * drivers/can/mcp2515.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #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 ****************************************************************************/ /* MCP2515 Configuration ****************************************************/ #define CAN_FRAME_MAX_DATA_LEN 8 #define SPI_TRANSFER_BUF_LEN (6 + CAN_FRAME_MAX_DATA_LEN) #define MCP2515_NUM_TX_BUFFERS 3 /* 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)(2 * CONFIG_MCP2515_BITRATE))) - 1)) #define MCP2515_SJW CONFIG_MCP2515_SJW #if MCP2515_PROPSEG < 1 # error Invalid PROPSEG. It cannot be lower than 1 #endif #if MCP2515_PROPSEG > 8 # error Invalid PROPSEG. It cannot be greater than 8 #endif #if MCP2515_PHSEG1 < 1 # error Invalid PHSEG1. It cannot be lower than 1 #endif #if MCP2515_PHSEG1 > 8 # error Invalid PHSEG1. It cannot be greater than 1 #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 - Transmit Buffer 0 Empty Interrupt * MCP2515_INT_TX1 - Transmit Buffer 1 Empty Interrupt * MCP2515_INT_TX2 - Transmit Buffer 2 Empty Interrupt */ #define MCP2515_TXBUFFER_INTS (MCP2515_INT_TX0 | MCP2515_INT_TX1 | MCP2515_INT_TX2) /* Helpers ******************************************************************/ #define TXREGVAL(reg) priv->spi_txbuf[reg - MCP2515_TXB0CTRL] /* 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 { FAR struct mcp2515_config_s *config; /* The constant configuration */ uint8_t state; /* See enum can_state_s */ uint8_t nalloc; /* Number of allocated filters */ mutex_t lock; /* 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 txbuffers; /* TX Buffers bit allocator. */ FAR uint8_t *spi_txbuf; FAR uint8_t *spi_rxbuf; #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_config_spi(FAR struct mcp2515_can_s *priv); static void mcp2515_readregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, FAR uint8_t *buffer, uint8_t len); static void mcp2515_writeregs(FAR struct mcp2515_can_s *priv, uint8_t regaddr, FAR const 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 /* 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 = { mcp2515_reset, /* co_reset */ mcp2515_setup, /* co_setup */ mcp2515_shutdown, /* co_shutdown */ mcp2515_rxint, /* co_rxint */ mcp2515_txint, /* co_txint */ mcp2515_ioctl, /* co_ioctl */ mcp2515_remoterequest, /* co_remoterequest */ mcp2515_send, /* co_send */ mcp2515_txready, /* co_txready */ mcp2515_txempty /* co_txempty */ }; /**************************************************************************** * Private Functions ****************************************************************************/ static void mcp2515_config_spi(FAR struct mcp2515_can_s *priv) { /* Setup SPI frequency and mode */ SPI_SETFREQUENCY(priv->config->spi, CONFIG_MCP2515_SPI_SCK_FREQUENCY); SPI_SETMODE(priv->config->spi, MCP2515_SPI_MODE); SPI_SETBITS(priv->config->spi, 8); SPI_HWFEATURES(priv->config->spi, 0); } static void mcp2515_read_2regs(FAR struct mcp2515_can_s *priv, uint8_t reg, FAR uint8_t *v1, FAR uint8_t *v2) { priv->spi_txbuf[0] = MCP2515_READ; priv->spi_txbuf[1] = reg; SPI_LOCK(priv->config->spi, true); mcp2515_config_spi(priv); SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), true); SPI_EXCHANGE(priv->config->spi, priv->spi_txbuf, priv->spi_rxbuf, 4); SPI_SELECT(priv->config->spi, SPIDEV_CANBUS(priv->config->devid), false); SPI_LOCK(priv->config->spi, false); *v1 = priv->spi_rxbuf[2]; *v2 = priv->spi_rxbuf[3]; } /**************************************************************************** * 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, FAR uint8_t *buffer, uint8_t len) { FAR struct mcp2515_config_s *config = priv->config; #ifdef CONFIG_CANBUS_REGDEBUG int i; #endif SPI_LOCK(config->spi, true); mcp2515_config_spi(priv); /* Select the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true); /* Send the READ command */ SPI_SEND(config->spi, MCP2515_READ); /* Send register to read and get the next bytes read back */ SPI_SEND(config->spi, regaddr); SPI_RECVBLOCK(config->spi, buffer, len); /* Deselect the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false); /* Unlock bus */ SPI_LOCK(config->spi, false); #ifdef CONFIG_CANBUS_REGDEBUG for (i = 0; i < len; i++) { caninfo("%02x->%02x\n", regaddr, buffer[i]); } #endif } static void mcp2515_transfer(FAR struct mcp2515_can_s *priv, uint8_t len) { FAR struct mcp2515_config_s *config = priv->config; SPI_LOCK(config->spi, true); mcp2515_config_spi(priv); /* Select the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true); /* Send the READ command */ SPI_EXCHANGE(config->spi, priv->spi_txbuf, priv->spi_rxbuf, len); /* Deselect the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false); /* Unlock bus */ SPI_LOCK(config->spi, false); } /**************************************************************************** * 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, FAR const 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 SPI_LOCK(config->spi, true); mcp2515_config_spi(priv); /* Select the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true); /* Send the READ command */ SPI_SEND(config->spi, MCP2515_WRITE); /* Send initial register to be written */ SPI_SEND(config->spi, regaddr); SPI_SNDBLOCK(config->spi, buffer, len); /* Deselect the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false); /* Unlock bus */ 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; uint8_t wr[4] = { MCP2515_BITMOD, regaddr, mask, value }; SPI_LOCK(config->spi, true); mcp2515_config_spi(priv); /* Select the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true); SPI_SNDBLOCK(config->spi, wr, 4); /* Deselect the MCP2515 */ SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false); /* Unlock bus */ 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_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; int ret; DEBUGASSERT(priv != NULL && priv->config != NULL); config = priv->config; /* Get exclusive excess to the MCP2515 hardware */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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->xf_id1 <= CAN_MAX_STDMSGID); DEBUGASSERT(extconfig->xf_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); nxmutex_unlock(&priv->lock); return ndx; } } nxmutex_unlock(&priv->lock); 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; int ret; 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 */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Check if this filter is really assigned */ if ((priv->filters & (1 << ndx)) == 0) { /* No, error out */ nxmutex_unlock(&priv->lock); 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); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(priv != NULL && priv->config != NULL); config = priv->config; /* Get exclusive excess to the MCP2515 hardware */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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); nxmutex_unlock(&priv->lock); return ndx; } } nxmutex_unlock(&priv->lock); 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; int ret; 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 */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Check if this filter is really assigned */ if ((priv->filters & (1 << ndx)) == 0) { /* No, error out */ nxmutex_unlock(&priv->lock); 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); nxmutex_unlock(&priv->lock); 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; int ret; DEBUGASSERT(priv); config = priv->config; DEBUGASSERT(config); UNUSED(config); /* Get exclusive access to the MCP2515 peripheral */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return; } /* Send SPI reset command to MCP2515 */ SPI_LOCK(config->spi, true); mcp2515_config_spi(priv); SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), true); SPI_SEND(config->spi, MCP2515_RESET); SPI_SELECT(config->spi, SPIDEV_CANBUS(config->devid), false); SPI_LOCK(config->spi, false); /* Wait 1ms to let MCP2515 restart */ nxsig_usleep(1000); /* Make sure that all buffers are released. */ nxsem_reset(&priv->txfsem, MCP2515_NUM_TX_BUFFERS); priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1; /* Define the current state and unlock */ priv->state = MCP2515_STATE_RESET; nxmutex_unlock(&priv->lock); } /**************************************************************************** * 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); /* Get exclusive access to the MCP2515 peripheral */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* 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); nxmutex_unlock(&priv->lock); 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; DEBUGASSERT(dev); priv = dev->cd_priv; DEBUGASSERT(priv); config = priv->config; DEBUGASSERT(config); caninfo("CAN%d enable: %d\n", config->devid, enable); UNUSED(config); /* Enable/disable the receive interrupts */ flags = enter_critical_section(); if (enable) { mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->rxints | MCP2515_ERROR_INTS, priv->rxints | MCP2515_ERROR_INTS); } else { mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->rxints, ~priv->rxints); } 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; DEBUGASSERT(priv && priv->config); caninfo("CAN%d enable: %d\n", priv->config->devid, enable); /* Enable/disable the receive interrupts */ flags = enter_critical_section(); if (enable) { mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->txints | MCP2515_ERROR_INTS, priv->txints | MCP2515_ERROR_INTS); } else { mcp2515_modifyreg(priv, MCP2515_CANINTE, priv->txints, ~priv->txints); } 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) * 2; 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)(2 * 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 txbuf; unsigned int nbytes; int ret; DEBUGASSERT(dev); priv = dev->cd_priv; DEBUGASSERT(priv && priv->config); config = priv->config; caninfo("CAN%d\n", config->devid); caninfo("CAN%d ID: %" PRId32 " DLC: %d\n", config->devid, (uint32_t)msg->cm_hdr.ch_id, msg->cm_hdr.ch_dlc); UNUSED(config); /* Get exclusive access to the MCP2515 peripheral */ ret = nxmutex_lock(&priv->lock); if (ret < 0) { return ret; } /* Acquire buffer */ ret = nxsem_wait(&priv->txfsem); if (ret < 0) { nxmutex_unlock(&priv->lock); return ret; } DEBUGASSERT(priv->txbuffers != 0); txbuf = ffs(priv->txbuffers) - 1; priv->txbuffers &= ~(1 << txbuf); /* Select one empty transmit buffer */ /* 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); TXREGVAL(MCP2515_TXB0EID0) = regval; /* EID15 - EID8 */ regval = (msg->cm_hdr.ch_id & 0xff00) >> 8; TXREGVAL(MCP2515_TXB0EID8) = regval; /* EID17 and EID16 */ regval = (msg->cm_hdr.ch_id & 0x30000) >> 16; regval |= TXBSIDL_EXIDE; /* STD2 - STD0 */ regval |= (msg->cm_hdr.ch_id & 0x1c0000) >> 13; TXREGVAL(MCP2515_TXB0SIDL) = regval; /* STD10 - STD3 */ regval = (msg->cm_hdr.ch_id & 0x1fe00000) >> 21; TXREGVAL(MCP2515_TXB0SIDH) = regval; } 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; TXREGVAL(MCP2515_TXB0SIDH) = regval; /* STD2 - STD0 */ regval = (msg->cm_hdr.ch_id & 0x007) << 5; TXREGVAL(MCP2515_TXB0SIDL) = regval; } /* Setup the DLC */ regval = (msg->cm_hdr.ch_dlc & 0xf); if (msg->cm_hdr.ch_rtr) { regval |= TXBDLC_RTR; } TXREGVAL(MCP2515_TXB0DLC) = regval; /* Fill the data buffer */ nbytes = msg->cm_hdr.ch_dlc; memcpy(&TXREGVAL(MCP2515_TXB0D0), msg->cm_data, nbytes); TXREGVAL(MCP2515_TXB0CTRL) = MCP2515_LOAD_TXB(txbuf); mcp2515_transfer(priv, SPI_TRANSFER_BUF_LEN); /* Request to send */ priv->spi_txbuf[0] = MCP2515_RTS(txbuf); mcp2515_transfer(priv, 1); nxmutex_unlock(&priv->lock); /* 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. */ 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; DEBUGASSERT(dev); priv = dev->cd_priv; DEBUGASSERT(priv); return priv->txbuffers != 0; } /**************************************************************************** * 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; DEBUGASSERT(dev); priv = dev->cd_priv; DEBUGASSERT(priv); return priv->txbuffers == (1 << MCP2515_NUM_TX_BUFFERS) - 1; } /**************************************************************************** * 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_tcf = 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 * ****************************************************************************/ #define RXREGVAL(reg) priv->spi_rxbuf[reg-MCP2515_RXB0CTRL] 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; DEBUGASSERT(dev); priv = dev->cd_priv; DEBUGASSERT(priv); mcp2515_readregs(priv, MCP2515_RXB0CTRL + offset, priv->spi_rxbuf, SPI_TRANSFER_BUF_LEN); regval = RXREGVAL(MCP2515_RXB0SIDL); #ifdef CONFIG_CAN_EXTID if ((regval & RXBSIDL_IDE) != 0) { /* Save the extended ID of the newly received message */ /* EID7 - EID0 */ regval = RXREGVAL(MCP2515_RXB0EID0); hdr.ch_id = regval ; /* EID15 - EID8 */ regval = RXREGVAL(MCP2515_RXB0EID8); hdr.ch_id = hdr.ch_id | (regval << 8); /* EID17 and EID16 */ regval = RXREGVAL(MCP2515_RXB0SIDL); hdr.ch_id = hdr.ch_id | ((regval & RXBSIDL_EID_MASK) << 16); /* STD2 - STD0 */ hdr.ch_id = hdr.ch_id | ((regval >> 5) << 18); /* STD10 - STD3 */ regval = RXREGVAL(MCP2515_RXB0SIDH); hdr.ch_id = hdr.ch_id | (regval << 21); hdr.ch_extid = true; } else { /* Save the standard ID of the newly received message */ regval = RXREGVAL(MCP2515_RXB0SIDH); hdr.ch_id = regval; regval = RXREGVAL(MCP2515_RXB0SIDL); 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 */ regval = RXREGVAL(MCP2515_RXB0SIDH); hdr.ch_id = regval; regval = RXREGVAL(MCP2515_RXB0SIDL); 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_tcf = 0; /* Extract the RTR bit */ regval = RXREGVAL(MCP2515_RXB0CTRL); hdr.ch_rtr = (regval & RXBCTRL_RXRTR) != 0; /* Get the DLC */ regval = RXREGVAL(MCP2515_RXB0DLC); hdr.ch_dlc = (regval & RXBDLC_DLC_MASK) >> RXBDLC_DLC_SHIFT; /* Save the message data */ ret = can_receive(dev, &hdr, (FAR uint8_t *)&RXREGVAL(MCP2515_RXB0D0)); 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; uint8_t clrmask; 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_read_2regs(priv, MCP2515_CANINTE, &ie, &ir); pending = (ir & ie); handled = false; clrmask = 0; if (pending == 0) { return OK; } /* Check for any errors */ if ((pending & MCP2515_ERROR_INTS) != 0) { /* Clear interrupt errors */ pending &= ~MCP2515_ERROR_INTS; clrmask |= 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 */ priv->txbuffers |= (1 << 0); nxsem_post(&priv->txfsem); pending &= ~MCP2515_INT_TX0; clrmask |= MCP2515_INT_TX0; } if (pending & MCP2515_INT_TX1) { caninfo("TX1 is empty to transmit new message!\n"); priv->txbuffers |= (1 << 1); nxsem_post(&priv->txfsem); /* Clear TX1 interrupt */ pending &= ~MCP2515_INT_TX1; clrmask |= MCP2515_INT_TX1; } if (pending & MCP2515_INT_TX2) { caninfo("TX2 is empty to transmit new message!\n"); priv->txbuffers |= (1 << 2); nxsem_post(&priv->txfsem); /* Clear TX2 interrupt */ pending &= ~MCP2515_INT_TX2; clrmask |= 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(). */ can_txready(dev); #endif } else if ((pending & priv->txints) != 0) { /* Clear unhandled TX events */ 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 */ pending &= ~MCP2515_INT_RX0; clrmask |= MCP2515_INT_RX0; } else { if ((pending & MCP2515_INT_RX1) != 0) { mcp2515_receive(dev, MCP2515_RX1_OFFSET); /* Clear RX1 interrupt */ pending &= ~MCP2515_INT_RX1; clrmask |= MCP2515_INT_RX1; } } /* Acknowledge reading the FIFO entry */ handled = true; } mcp2515_modifyreg(priv, MCP2515_CANINTF, clrmask, pending); } while (handled); return OK; } /**************************************************************************** * Name: mcp2515_hw_initialize * * Description: * MCP2515 hardware initialization * * Input Parameters: * 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); UNUSED(config); /* 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); nxsig_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; /* In this option we set a special receive mode in the * RXM[1:0] bits (RXBnCTRL[6:5]). In both registers: * RXB0CTRL and RXB1CTRL. * 11 = Turns mask/filters off; receives any message. * * In this mode it is tested that it receives both * extended and standard id messages. */ #ifdef CONFIG_CAN_EXTID mcp2515_readregs(priv, MCP2515_RXB0CTRL, ®val, 1); regval &= ~RXBCTRL_RXM_ALLMSG; regval |= RXBCTRL_RXM_ALLMSG; mcp2515_writeregs(priv, MCP2515_RXB0CTRL, ®val, 1); mcp2515_readregs(priv, MCP2515_RXB1CTRL, ®val, 1); regval &= ~RXBCTRL_RXM_ALLMSG; regval |= RXBCTRL_RXM_ALLMSG; mcp2515_writeregs(priv, MCP2515_RXB1CTRL, ®val, 1); #endif return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: mcp2515_instantiate * * Description: * Initialize the selected MCP2515 CAN Bus Controller over SPI * * Input Parameters: * 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; } /* Perform one time data initialization */ memset(priv, 0, sizeof(struct mcp2515_can_s)); priv->config = config; priv->spi_txbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN); priv->spi_rxbuf = kmm_zalloc(SPI_TRANSFER_BUF_LEN); /* Set the initial bit timing. This might change subsequently * due to IOCTL command processing. */ /* Initialize mutex & semaphores */ nxmutex_init(&priv->lock); nxsem_init(&priv->txfsem, 0, MCP2515_NUM_TX_BUFFERS); /* Initialize bitmask */ priv->txbuffers = (1 << MCP2515_NUM_TX_BUFFERS) - 1; /* 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); nxmutex_destroy(&priv->lock); kmm_free(priv); return NULL; } /* Return our private data structure as an opaque handle */ return priv; } /**************************************************************************** * Name: mcp2515_initialize * * Description: * Initialize the selected MCP2515 CAN Bus Controller over SPI * * Input Parameters: * 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 = kmm_zalloc(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 */