--- /dev/null
+/* ----------------------------------------------------------------------------\r
+ * SAM Software Package License\r
+ * ----------------------------------------------------------------------------\r
+ * Copyright (c) 2015, Atmel Corporation\r
+ *\r
+ * All rights reserved.\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * - Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the disclaimer below.\r
+ *\r
+ * Atmel's name may not be used to endorse or promote products derived from\r
+ * this software without specific prior written permission.\r
+ *\r
+ * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
+ * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
+ * ----------------------------------------------------------------------------\r
+ */\r
+\r
+/** \file\r
+ * Implements functions for Controller Area Network with Flexible Data-rate,\r
+ * relying on the MCAN peripheral.\r
+ */\r
+/** \addtogroup can_module\r
+ *@{*/\r
+\r
+/*----------------------------------------------------------------------------\r
+ * Headers\r
+ *----------------------------------------------------------------------------*/\r
+\r
+#include "board.h"\r
+#include "chip.h"\r
+#include "mcan.h"\r
+#include "pmc.h"\r
+\r
+#include <assert.h>\r
+#include <string.h>\r
+\r
+/*---------------------------------------------------------------------------\r
+ * Local definitions\r
+ *---------------------------------------------------------------------------*/\r
+\r
+enum mcan_dlc\r
+{\r
+ CAN_DLC_0 = 0,\r
+ CAN_DLC_1 = 1,\r
+ CAN_DLC_2 = 2,\r
+ CAN_DLC_3 = 3,\r
+ CAN_DLC_4 = 4,\r
+ CAN_DLC_5 = 5,\r
+ CAN_DLC_6 = 6,\r
+ CAN_DLC_7 = 7,\r
+ CAN_DLC_8 = 8,\r
+ CAN_DLC_12 = 9,\r
+ CAN_DLC_16 = 10,\r
+ CAN_DLC_20 = 11,\r
+ CAN_DLC_24 = 12,\r
+ CAN_DLC_32 = 13,\r
+ CAN_DLC_48 = 14,\r
+ CAN_DLC_64 = 15\r
+};\r
+\r
+/*---------------------------------------------------------------------------\r
+ * Local functions\r
+ *---------------------------------------------------------------------------*/\r
+\r
+/**\r
+ * \brief Convert data length to Data Length Code.\r
+ * \param len length, in bytes\r
+ * \param dlc address where the matching Data Length Code will be written\r
+ * \return true if a code matched the provided length, false if this exact\r
+ * length is not supported.\r
+ */\r
+static bool get_length_code(uint8_t len, enum mcan_dlc *dlc)\r
+{\r
+ assert(dlc);\r
+\r
+ if (len <= 8) {\r
+ *dlc = (enum mcan_dlc)len;\r
+ return true;\r
+ }\r
+ if (len % 4)\r
+ return false;\r
+ len /= 4;\r
+ if (len <= 6) {\r
+ *dlc = (enum mcan_dlc)(len + 6);\r
+ return true;\r
+ }\r
+ if (len % 4)\r
+ return false;\r
+ len /= 4;\r
+ if (len > 4)\r
+ return false;\r
+ *dlc = (enum mcan_dlc)(len + 11);\r
+ return true;\r
+}\r
+\r
+/**\r
+ * \brief Convert Data Length Code to actual data length.\r
+ * \param dlc CAN_DLC_xx enum value\r
+ * \return Data length, expressed in bytes.\r
+ */\r
+static uint8_t get_data_length(enum mcan_dlc dlc)\r
+{\r
+ assert((dlc == CAN_DLC_0 || dlc > CAN_DLC_0) && dlc <= CAN_DLC_64);\r
+\r
+ if (dlc <= CAN_DLC_8)\r
+ return (uint8_t)dlc;\r
+ if (dlc <= CAN_DLC_24)\r
+ return ((uint8_t)dlc - 6) * 4;\r
+ return ((uint8_t)dlc - 11) * 16;\r
+}\r
+\r
+/**\r
+ * \brief Compute the size of the Message RAM, depending on the application.\r
+ * \param set Pointer to a MCAN instance that will be setup accordingly.\r
+ * \param cfg MCAN configuration to be considered. Only integer size parameters\r
+ * need to be configured. The other parameters can be left blank at this stage.\r
+ * \param size address where the required size of the Message RAM will be\r
+ * written, expressed in (32-bit) words.\r
+ * \return true if successful, false if a parameter is set to an unsupported\r
+ * value.\r
+ */\r
+static bool configure_ram(struct mcan_set *set,\r
+ const struct mcan_config *cfg, uint32_t *size)\r
+{\r
+ if (cfg->array_size_filt_std > 128 || cfg->array_size_filt_ext > 64\r
+ || cfg->fifo_size_rx0 > 64 || cfg->fifo_size_rx1 > 64\r
+ || cfg->array_size_rx > 64 || cfg->fifo_size_tx_evt > 32\r
+ || cfg->array_size_tx > 32 || cfg->fifo_size_tx > 32\r
+ || cfg->array_size_tx + cfg->fifo_size_tx > 32\r
+ || cfg->buf_size_rx_fifo0 > 64 || cfg->buf_size_rx_fifo1 > 64\r
+ || cfg->buf_size_rx > 64 || cfg->buf_size_tx > 64)\r
+ return false;\r
+\r
+ set->ram_filt_std = cfg->msg_ram;\r
+ *size = (uint32_t)cfg->array_size_filt_std * MCAN_RAM_FILT_STD_SIZE;\r
+ set->ram_filt_ext = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->array_size_filt_ext * MCAN_RAM_FILT_EXT_SIZE;\r
+ set->ram_fifo_rx0 = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->fifo_size_rx0 * (MCAN_RAM_BUF_HDR_SIZE\r
+ + cfg->buf_size_rx_fifo0 / 4);\r
+ set->ram_fifo_rx1 = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->fifo_size_rx1 * (MCAN_RAM_BUF_HDR_SIZE\r
+ + cfg->buf_size_rx_fifo1 / 4);\r
+ set->ram_array_rx = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->array_size_rx * (MCAN_RAM_BUF_HDR_SIZE\r
+ + cfg->buf_size_rx / 4);\r
+ set->ram_fifo_tx_evt = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->fifo_size_tx_evt * MCAN_RAM_TX_EVT_SIZE;\r
+ set->ram_array_tx = cfg->msg_ram + *size;\r
+ *size += (uint32_t)cfg->array_size_tx * (MCAN_RAM_BUF_HDR_SIZE\r
+ + cfg->buf_size_tx / 4);\r
+ *size += (uint32_t)cfg->fifo_size_tx * (MCAN_RAM_BUF_HDR_SIZE\r
+ + cfg->buf_size_tx / 4);\r
+ return true;\r
+}\r
+\r
+/*---------------------------------------------------------------------------\r
+ * Exported Functions\r
+ *---------------------------------------------------------------------------*/\r
+\r
+bool mcan_configure_msg_ram(const struct mcan_config *cfg, uint32_t *size)\r
+{\r
+ assert(cfg);\r
+ assert(size);\r
+\r
+ struct mcan_set tmp_set = { .cfg = { 0 } };\r
+\r
+ return configure_ram(&tmp_set, cfg, size);\r
+}\r
+\r
+bool mcan_initialize(struct mcan_set *set, const struct mcan_config *cfg)\r
+{\r
+ assert(set);\r
+ assert(cfg);\r
+ assert(cfg->regs);\r
+ assert(cfg->msg_ram);\r
+\r
+ Mcan *mcan = cfg->regs;\r
+ uint32_t *element = NULL, *elem_end = NULL;\r
+ uint32_t freq, regVal32;\r
+ enum mcan_dlc dlc;\r
+\r
+ memset(set, 0, sizeof(*set));\r
+ if (!configure_ram(set, cfg, ®Val32))\r
+ return false;\r
+ set->cfg = *cfg;\r
+\r
+ /* Configure the MSB of the Message RAM Base Address */\r
+ regVal32 = (uint32_t)cfg->msg_ram >> 16;\r
+ if (cfg->id == ID_CAN0_INT0 || cfg->id == ID_CAN0_INT1)\r
+ regVal32 = (SFR->SFR_CAN & ~SFR_CAN_EXT_MEM_CAN0_ADDR_Msk)\r
+ | SFR_CAN_EXT_MEM_CAN0_ADDR(regVal32);\r
+ else\r
+ regVal32 = (SFR->SFR_CAN & ~SFR_CAN_EXT_MEM_CAN1_ADDR_Msk)\r
+ | SFR_CAN_EXT_MEM_CAN1_ADDR(regVal32);\r
+ SFR->SFR_CAN = regVal32;\r
+\r
+ /* Reset the CC Control Register */\r
+ mcan->MCAN_CCCR = 0 | MCAN_CCCR_INIT_ENABLED;\r
+\r
+ mcan_disable(set);\r
+ mcan_reconfigure(set);\r
+\r
+ /* Global Filter Configuration: Reject remote frames, reject non-matching frames */\r
+ mcan->MCAN_GFC = MCAN_GFC_RRFE_REJECT | MCAN_GFC_RRFS_REJECT\r
+ | MCAN_GFC_ANFE(2) | MCAN_GFC_ANFS(2);\r
+\r
+ /* Extended ID Filter AND mask */\r
+ mcan->MCAN_XIDAM = 0x1FFFFFFF;\r
+\r
+ /* Interrupt configuration - leave initialization with all interrupts off\r
+ * Disable all interrupts */\r
+ mcan->MCAN_IE = 0;\r
+ mcan->MCAN_TXBTIE = 0x00000000;\r
+ /* All interrupts directed to Line 0 */\r
+ mcan->MCAN_ILS = 0x00000000;\r
+ /* Disable both interrupt LINE 0 & LINE 1 */\r
+ mcan->MCAN_ILE = 0x00;\r
+ /* Clear all interrupt flags */\r
+ mcan->MCAN_IR = 0xFFCFFFFF;\r
+\r
+ /* Configure CAN bit timing */\r
+ if (cfg->bit_rate == 0\r
+ || cfg->quanta_before_sp < 3 || cfg->quanta_before_sp > 257\r
+ || cfg->quanta_after_sp < 1 || cfg->quanta_after_sp > 128\r
+ || cfg->quanta_sync_jump < 1 || cfg->quanta_sync_jump > 128)\r
+ return false;\r
+ /* Retrieve the frequency of the CAN core clock i.e. the Generated Clock */\r
+ freq = pmc_get_gck_clock(cfg->id);\r
+ /* Compute the Nominal Baud Rate Prescaler */\r
+ regVal32 = ROUND_INT_DIV(freq, cfg->bit_rate\r
+ * (cfg->quanta_before_sp + cfg->quanta_after_sp));\r
+ if (regVal32 < 1 || regVal32 > 512)\r
+ return false;\r
+ /* Apply bit timing configuration */\r
+ mcan->MCAN_NBTP = MCAN_NBTP_NBRP(regVal32 - 1)\r
+ | MCAN_NBTP_NTSEG1(cfg->quanta_before_sp - 1 - 1)\r
+ | MCAN_NBTP_NTSEG2(cfg->quanta_after_sp - 1)\r
+ | MCAN_NBTP_NSJW(cfg->quanta_sync_jump - 1);\r
+\r
+ /* Configure fast CAN FD bit timing */\r
+ if (cfg->bit_rate_fd < cfg->bit_rate\r
+ || cfg->quanta_before_sp_fd < 3 || cfg->quanta_before_sp_fd > 33\r
+ || cfg->quanta_after_sp_fd < 1 || cfg->quanta_after_sp_fd > 16\r
+ || cfg->quanta_sync_jump_fd < 1 || cfg->quanta_sync_jump_fd > 8)\r
+ return false;\r
+ /* Compute the Fast Baud Rate Prescaler */\r
+ regVal32 = ROUND_INT_DIV(freq, cfg->bit_rate_fd\r
+ * (cfg->quanta_before_sp_fd + cfg->quanta_after_sp_fd));\r
+ if (regVal32 < 1 || regVal32 > 32)\r
+ return false;\r
+ /* Apply bit timing configuration */\r
+ mcan->MCAN_DBTP = MCAN_DBTP_FBRP(regVal32 - 1)\r
+ | MCAN_DBTP_DTSEG1(cfg->quanta_before_sp_fd - 1 - 1)\r
+ | MCAN_DBTP_DTSEG2(cfg->quanta_after_sp_fd - 1)\r
+ | MCAN_DBTP_DSJW(cfg->quanta_sync_jump_fd - 1);\r
+\r
+ /* Configure Message RAM starting addresses and element count */\r
+ /* 11-bit Message ID Rx Filters */\r
+ mcan->MCAN_SIDFC =\r
+ MCAN_SIDFC_FLSSA((uint32_t)set->ram_filt_std >> 2)\r
+ | MCAN_SIDFC_LSS(cfg->array_size_filt_std);\r
+ /* 29-bit Message ID Rx Filters */\r
+ mcan->MCAN_XIDFC =\r
+ MCAN_XIDFC_FLESA((uint32_t)set->ram_filt_ext >> 2)\r
+ | MCAN_XIDFC_LSE(cfg->array_size_filt_ext);\r
+ /* Rx FIFO 0 */\r
+ mcan->MCAN_RXF0C =\r
+ MCAN_RXF0C_F0SA((uint32_t)set->ram_fifo_rx0 >> 2)\r
+ | MCAN_RXF0C_F0S(cfg->fifo_size_rx0)\r
+ | MCAN_RXF0C_F0WM(0)\r
+ | 0; /* clear MCAN_RXF0C_F0OM */\r
+ /* Rx FIFO 1 */\r
+ mcan->MCAN_RXF1C =\r
+ MCAN_RXF1C_F1SA((uint32_t)set->ram_fifo_rx1 >> 2)\r
+ | MCAN_RXF1C_F1S(cfg->fifo_size_rx1)\r
+ | MCAN_RXF1C_F1WM(0)\r
+ | 0; /* clear MCAN_RXF1C_F1OM */\r
+ /* Dedicated Rx Buffers\r
+ * Note: the HW does not know (and does not care about) how many\r
+ * dedicated Rx Buffers are used by the application. */\r
+ mcan->MCAN_RXBC =\r
+ MCAN_RXBC_RBSA((uint32_t)set->ram_array_rx >> 2);\r
+ /* Tx Event FIFO */\r
+ mcan->MCAN_TXEFC =\r
+ MCAN_TXEFC_EFSA((uint32_t)set->ram_fifo_tx_evt >> 2)\r
+ | MCAN_TXEFC_EFS(cfg->fifo_size_tx_evt)\r
+ | MCAN_TXEFC_EFWM(0);\r
+ /* Tx Buffers */\r
+ mcan->MCAN_TXBC =\r
+ MCAN_TXBC_TBSA((uint32_t)set->ram_array_tx >> 2)\r
+ | MCAN_TXBC_NDTB(cfg->array_size_tx)\r
+ | MCAN_TXBC_TFQS(cfg->fifo_size_tx)\r
+ | 0; /* clear MCAN_TXBC_TFQM */\r
+\r
+ /* Configure the size of data fields in Rx and Tx Buffer Elements */\r
+ if (!get_length_code(cfg->buf_size_rx_fifo0, &dlc))\r
+ return false;\r
+ regVal32 = MCAN_RXESC_F0DS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);\r
+ if (!get_length_code(cfg->buf_size_rx_fifo1, &dlc))\r
+ return false;\r
+ regVal32 |= MCAN_RXESC_F1DS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);\r
+ if (!get_length_code(cfg->buf_size_rx, &dlc))\r
+ return false;\r
+ regVal32 |= MCAN_RXESC_RBDS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);\r
+ mcan->MCAN_RXESC = regVal32;\r
+ if (!get_length_code(cfg->buf_size_tx, &dlc))\r
+ return false;\r
+ mcan->MCAN_TXESC =\r
+ MCAN_TXESC_TBDS(dlc < CAN_DLC_8 ? 0 : dlc - CAN_DLC_8);\r
+\r
+ /* Configure Message ID Filters\r
+ * ...Disable all standard filters */\r
+ for (element = set->ram_filt_std, elem_end = set->ram_filt_std\r
+ + (uint32_t)cfg->array_size_filt_std * MCAN_RAM_FILT_STD_SIZE;\r
+ element < elem_end;\r
+ element += MCAN_RAM_FILT_STD_SIZE)\r
+ element[0] = MCAN_RAM_FILT_SFEC_DIS;\r
+ /* ...Disable all extended filters */\r
+ for (element = set->ram_filt_ext, elem_end = set->ram_filt_ext\r
+ + (uint32_t)cfg->array_size_filt_ext * MCAN_RAM_FILT_EXT_SIZE;\r
+ element < elem_end;\r
+ element += MCAN_RAM_FILT_EXT_SIZE)\r
+ element[0] = MCAN_RAM_FILT_EFEC_DIS;\r
+\r
+ mcan->MCAN_NDAT1 = 0xFFFFFFFF; /* clear new (rx) data flags */\r
+ mcan->MCAN_NDAT2 = 0xFFFFFFFF; /* clear new (rx) data flags */\r
+\r
+ regVal32 = mcan->MCAN_CCCR & ~(MCAN_CCCR_BRSE | MCAN_CCCR_FDOE);\r
+ mcan->MCAN_CCCR = regVal32 | MCAN_CCCR_PXHD | MCAN_CCCR_BRSE_DISABLED\r
+ | MCAN_CCCR_FDOE_DISABLED;\r
+\r
+ DSB();\r
+ ISB();\r
+ return true;\r
+}\r
+\r
+void mcan_reconfigure(struct mcan_set *set)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ uint32_t regVal32;\r
+\r
+ regVal32 = mcan->MCAN_CCCR & ~MCAN_CCCR_CCE;\r
+ assert((regVal32 & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_ENABLED);\r
+ /* Enable writing to configuration registers */\r
+ mcan->MCAN_CCCR = regVal32 | MCAN_CCCR_CCE_CONFIGURABLE;\r
+}\r
+\r
+void mcan_set_mode(struct mcan_set *set, enum mcan_can_mode mode)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ uint32_t regVal32;\r
+\r
+ regVal32 = mcan->MCAN_CCCR & ~(MCAN_CCCR_BRSE | MCAN_CCCR_FDOE);\r
+ switch (mode) {\r
+ case MCAN_MODE_CAN:\r
+ regVal32 |= MCAN_CCCR_BRSE_DISABLED | MCAN_CCCR_FDOE_DISABLED;\r
+ break;\r
+ case MCAN_MODE_EXT_LEN_CONST_RATE:\r
+ regVal32 |= MCAN_CCCR_BRSE_DISABLED | MCAN_CCCR_FDOE_ENABLED;\r
+ break;\r
+ case MCAN_MODE_EXT_LEN_DUAL_RATE:\r
+ regVal32 |= MCAN_CCCR_BRSE_ENABLED | MCAN_CCCR_FDOE_ENABLED;\r
+ break;\r
+ default:\r
+ return;\r
+ }\r
+ mcan->MCAN_CCCR = regVal32;\r
+}\r
+\r
+enum mcan_can_mode mcan_get_mode(const struct mcan_set *set)\r
+{\r
+ const uint32_t cccr = set->cfg.regs->MCAN_CCCR;\r
+\r
+ if ((cccr & MCAN_CCCR_FDOE) == MCAN_CCCR_FDOE_DISABLED)\r
+ return MCAN_MODE_CAN;\r
+ if ((cccr & MCAN_CCCR_BRSE) == MCAN_CCCR_BRSE_DISABLED)\r
+ return MCAN_MODE_EXT_LEN_CONST_RATE;\r
+ return MCAN_MODE_EXT_LEN_DUAL_RATE;\r
+}\r
+\r
+void mcan_init_loopback(struct mcan_set *set)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+\r
+ mcan->MCAN_CCCR |= MCAN_CCCR_TEST_ENABLED;\r
+#if 0\r
+ mcan->MCAN_CCCR |= MCAN_CCCR_MON_ENABLED; /* for internal loop back */\r
+#endif\r
+ mcan->MCAN_TEST |= MCAN_TEST_LBCK_ENABLED;\r
+}\r
+\r
+void mcan_set_tx_queue_mode(struct mcan_set *set)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ mcan->MCAN_TXBC |= MCAN_TXBC_TFQM;\r
+}\r
+\r
+void mcan_enable(struct mcan_set *set)\r
+{\r
+ uint32_t index, val;\r
+\r
+ /* Depending on bus condition, the HW may switch back to the\r
+ * Initialization state, by itself. Therefore, upon timeout, return.\r
+ * [Using an arbitrary timeout criterion.] */\r
+ for (index = 0; index < 1024; index++) {\r
+ val = set->cfg.regs->MCAN_CCCR;\r
+ if ((val & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_DISABLED)\r
+ break;\r
+ if (index == 0)\r
+ set->cfg.regs->MCAN_CCCR = (val & ~MCAN_CCCR_INIT)\r
+ | MCAN_CCCR_INIT_DISABLED;\r
+ }\r
+}\r
+\r
+void mcan_disable(struct mcan_set *set)\r
+{\r
+ uint32_t val;\r
+ bool initial;\r
+\r
+ for (initial = true; true; initial = false) {\r
+ val = set->cfg.regs->MCAN_CCCR;\r
+ if ((val & MCAN_CCCR_INIT) == MCAN_CCCR_INIT_ENABLED)\r
+ break;\r
+ if (initial)\r
+ set->cfg.regs->MCAN_CCCR = (val & ~MCAN_CCCR_INIT)\r
+ | MCAN_CCCR_INIT_ENABLED;\r
+ }\r
+}\r
+\r
+void mcan_loopback_on(struct mcan_set *set)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ mcan->MCAN_TEST |= MCAN_TEST_LBCK_ENABLED;\r
+}\r
+\r
+void mcan_loopback_off(struct mcan_set *set)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ mcan->MCAN_TEST &= ~MCAN_TEST_LBCK_ENABLED;\r
+}\r
+\r
+void mcan_enable_rx_array_flag(struct mcan_set *set, uint8_t line)\r
+{\r
+ assert(line == 0 || line == 1);\r
+\r
+ Mcan *mcan = set->cfg.regs;\r
+ if (line) {\r
+ mcan->MCAN_ILS |= MCAN_ILS_DRXL;\r
+ mcan->MCAN_ILE |= MCAN_ILE_EINT1;\r
+ } else {\r
+ mcan->MCAN_ILS &= ~MCAN_ILS_DRXL;\r
+ mcan->MCAN_ILE |= MCAN_ILE_EINT0;\r
+ }\r
+ mcan->MCAN_IR = MCAN_IR_DRX; /* clear previous flag */\r
+ mcan->MCAN_IE |= MCAN_IE_DRXE; /* enable it */\r
+}\r
+\r
+uint8_t * mcan_prepare_tx_buffer(struct mcan_set *set, uint8_t buf_idx,\r
+ uint32_t id, uint8_t len)\r
+{\r
+ assert(buf_idx < set->cfg.array_size_tx);\r
+ assert(len <= set->cfg.buf_size_tx);\r
+\r
+ Mcan *mcan = set->cfg.regs;\r
+ uint32_t *pThisTxBuf = 0;\r
+ uint32_t val;\r
+ const enum mcan_can_mode mode = mcan_get_mode(set);\r
+ enum mcan_dlc dlc;\r
+\r
+ if (buf_idx >= set->cfg.array_size_tx)\r
+ return NULL;\r
+ if (!get_length_code(len, &dlc))\r
+ dlc = CAN_DLC_0;\r
+ pThisTxBuf = set->ram_array_tx + buf_idx\r
+ * (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_tx / 4);\r
+ if (mcan_is_extended_id(id))\r
+ *pThisTxBuf++ = MCAN_RAM_BUF_XTD | MCAN_RAM_BUF_ID_XTD(id);\r
+ else\r
+ *pThisTxBuf++ = MCAN_RAM_BUF_ID_STD(id);\r
+ val = MCAN_RAM_BUF_MM(0) | MCAN_RAM_BUF_DLC((uint32_t)dlc);\r
+ if (mode == MCAN_MODE_EXT_LEN_CONST_RATE)\r
+ val |= MCAN_RAM_BUF_FDF;\r
+ else if (mode == MCAN_MODE_EXT_LEN_DUAL_RATE)\r
+ val |= MCAN_RAM_BUF_FDF | MCAN_RAM_BUF_BRS;\r
+ *pThisTxBuf++ = val;\r
+ /* enable transmit from buffer to set TC interrupt bit in IR,\r
+ * but interrupt will not happen unless TC interrupt is enabled */\r
+ mcan->MCAN_TXBTIE = (1 << buf_idx);\r
+ return (uint8_t *)pThisTxBuf; /* now it points to the data field */\r
+}\r
+\r
+void mcan_send_tx_buffer(struct mcan_set *set, uint8_t buf_idx)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+\r
+ if (buf_idx < set->cfg.array_size_tx)\r
+ mcan->MCAN_TXBAR = (1 << buf_idx);\r
+}\r
+\r
+uint8_t mcan_enqueue_outgoing_msg(struct mcan_set *set, uint32_t id,\r
+ uint8_t len, const uint8_t *data)\r
+{\r
+ assert(len <= set->cfg.buf_size_tx);\r
+\r
+ Mcan *mcan = set->cfg.regs;\r
+ uint32_t val;\r
+ uint32_t *pThisTxBuf = 0;\r
+ const enum mcan_can_mode mode = mcan_get_mode(set);\r
+ enum mcan_dlc dlc;\r
+ uint8_t putIdx = 255;\r
+\r
+ if (!get_length_code(len, &dlc))\r
+ dlc = CAN_DLC_0;\r
+ /* Configured for FifoQ and FifoQ not full? */\r
+ if (set->cfg.fifo_size_tx == 0 || (mcan->MCAN_TXFQS & MCAN_TXFQS_TFQF))\r
+ return putIdx;\r
+ putIdx = (uint8_t)((mcan->MCAN_TXFQS & MCAN_TXFQS_TFQPI_Msk)\r
+ >> MCAN_TXFQS_TFQPI_Pos);\r
+ pThisTxBuf = set->ram_array_tx + (uint32_t)\r
+ putIdx * (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_tx / 4);\r
+ if (mcan_is_extended_id(id))\r
+ *pThisTxBuf++ = MCAN_RAM_BUF_XTD | MCAN_RAM_BUF_ID_XTD(id);\r
+ else\r
+ *pThisTxBuf++ = MCAN_RAM_BUF_ID_STD(id);\r
+ val = MCAN_RAM_BUF_MM(0) | MCAN_RAM_BUF_DLC((uint32_t)dlc);\r
+ if (mode == MCAN_MODE_EXT_LEN_CONST_RATE)\r
+ val |= MCAN_RAM_BUF_FDF;\r
+ else if (mode == MCAN_MODE_EXT_LEN_DUAL_RATE)\r
+ val |= MCAN_RAM_BUF_FDF | MCAN_RAM_BUF_BRS;\r
+ *pThisTxBuf++ = val;\r
+ memcpy(pThisTxBuf, data, len);\r
+ /* enable transmit from buffer to set TC interrupt bit in IR,\r
+ * but interrupt will not happen unless TC interrupt is enabled\r
+ */\r
+ mcan->MCAN_TXBTIE = (1 << putIdx);\r
+ /* request to send */\r
+ mcan->MCAN_TXBAR = (1 << putIdx);\r
+ return putIdx;\r
+}\r
+\r
+bool mcan_is_buffer_sent(const struct mcan_set *set, uint8_t buf_idx)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+ return mcan->MCAN_TXBTO & (1 << buf_idx) ? true : false;\r
+}\r
+\r
+void mcan_filter_single_id(struct mcan_set *set,\r
+ uint8_t buf_idx, uint8_t filter, uint32_t id)\r
+{\r
+ assert(buf_idx < set->cfg.array_size_rx);\r
+ assert(id & CAN_EXT_MSG_ID ? filter < set->cfg.array_size_filt_ext\r
+ : filter < set->cfg.array_size_filt_std);\r
+ assert(id & CAN_EXT_MSG_ID ? (id & ~CAN_EXT_MSG_ID) <= 0x1fffffff :\r
+ id <= 0x7ff);\r
+\r
+ uint32_t *pThisRxFilt = 0;\r
+\r
+ if (buf_idx >= set->cfg.array_size_rx)\r
+ return;\r
+ if (mcan_is_extended_id(id)) {\r
+ pThisRxFilt = set->ram_filt_ext + filter\r
+ * MCAN_RAM_FILT_EXT_SIZE;\r
+ *pThisRxFilt++ = MCAN_RAM_FILT_EFEC_BUF\r
+ | MCAN_RAM_FILT_EFID1(id);\r
+ *pThisRxFilt = MCAN_RAM_FILT_EFID2_BUF\r
+ | MCAN_RAM_FILT_EFID2_BUF_IDX(buf_idx);\r
+ } else {\r
+ pThisRxFilt = set->ram_filt_std + filter\r
+ * MCAN_RAM_FILT_STD_SIZE;\r
+ *pThisRxFilt = MCAN_RAM_FILT_SFEC_BUF\r
+ | MCAN_RAM_FILT_SFID1(id)\r
+ | MCAN_RAM_FILT_SFID2_BUF\r
+ | MCAN_RAM_FILT_SFID2_BUF_IDX(buf_idx);\r
+ }\r
+}\r
+\r
+void mcan_filter_id_mask(struct mcan_set *set, uint8_t fifo, uint8_t filter,\r
+ uint32_t id, uint32_t mask)\r
+{\r
+ assert(fifo == 0 || fifo == 1);\r
+ assert(id & CAN_EXT_MSG_ID ? filter < set->cfg.array_size_filt_ext\r
+ : filter < set->cfg.array_size_filt_std);\r
+ assert(id & CAN_EXT_MSG_ID ? (id & ~CAN_EXT_MSG_ID) <= 0x1fffffff :\r
+ id <= 0x7ff);\r
+ assert(id & CAN_EXT_MSG_ID ? mask <= 0x1fffffff : mask <= 0x7ff);\r
+\r
+ uint32_t *pThisRxFilt = 0;\r
+ uint32_t val;\r
+\r
+ if (mcan_is_extended_id(id)) {\r
+ pThisRxFilt = set->ram_filt_ext + filter\r
+ * MCAN_RAM_FILT_EXT_SIZE;\r
+ *pThisRxFilt++ = (fifo ? MCAN_RAM_FILT_EFEC_FIFO1\r
+ : MCAN_RAM_FILT_EFEC_FIFO0) | MCAN_RAM_FILT_EFID1(id);\r
+ *pThisRxFilt = MCAN_RAM_FILT_EFT_CLASSIC\r
+ | MCAN_RAM_FILT_EFID2(mask);\r
+ } else {\r
+ pThisRxFilt = set->ram_filt_std + filter\r
+ * MCAN_RAM_FILT_STD_SIZE;\r
+ val = MCAN_RAM_FILT_SFT_CLASSIC\r
+ | MCAN_RAM_FILT_SFID1(id)\r
+ | MCAN_RAM_FILT_SFID2(mask);\r
+ *pThisRxFilt = (fifo ? MCAN_RAM_FILT_SFEC_FIFO1\r
+ : MCAN_RAM_FILT_SFEC_FIFO0) | val;\r
+ }\r
+}\r
+\r
+bool mcan_rx_buffer_data(const struct mcan_set *set, uint8_t buf_idx)\r
+{\r
+ Mcan *mcan = set->cfg.regs;\r
+\r
+ if (buf_idx < 32)\r
+ return mcan->MCAN_NDAT1 & (1 << buf_idx) ? true : false;\r
+ else if (buf_idx < 64)\r
+ return mcan->MCAN_NDAT2 & (1 << (buf_idx - 32)) ? true : false;\r
+ else\r
+ return false;\r
+}\r
+\r
+void mcan_read_rx_buffer(struct mcan_set *set, uint8_t buf_idx,\r
+ struct mcan_msg_info *msg)\r
+{\r
+ assert(buf_idx < set->cfg.array_size_rx);\r
+\r
+ Mcan *mcan = set->cfg.regs;\r
+ const uint32_t *pThisRxBuf = 0;\r
+ uint32_t tempRy; /* temp copy of RX buffer word */\r
+ uint8_t len;\r
+\r
+ if (buf_idx >= set->cfg.array_size_rx) {\r
+ msg->id = 0;\r
+ msg->timestamp = 0;\r
+ msg->full_len = 0;\r
+ msg->data_len = 0;\r
+ return;\r
+ }\r
+ pThisRxBuf = set->ram_array_rx + (buf_idx\r
+ * (MCAN_RAM_BUF_HDR_SIZE + set->cfg.buf_size_rx / 4));\r
+ tempRy = *pThisRxBuf++; /* word R0 contains ID */\r
+ if (tempRy & MCAN_RAM_BUF_XTD)\r
+ msg->id = CAN_EXT_MSG_ID | (tempRy & MCAN_RAM_BUF_ID_XTD_Msk)\r
+ >> MCAN_RAM_BUF_ID_XTD_Pos;\r
+ else\r
+ msg->id = (tempRy & MCAN_RAM_BUF_ID_STD_Msk)\r
+ >> MCAN_RAM_BUF_ID_STD_Pos;\r
+ tempRy = *pThisRxBuf++; /* word R1 contains DLC & time stamp */\r
+ msg->full_len = len = get_data_length((enum mcan_dlc)\r
+ ((tempRy & MCAN_RAM_BUF_DLC_Msk) >> MCAN_RAM_BUF_DLC_Pos));\r
+ msg->timestamp = (tempRy & MCAN_RAM_BUF_RXTS_Msk)\r
+ >> MCAN_RAM_BUF_RXTS_Pos;\r
+ if (msg->data) {\r
+ /* copy the data from the Rx Buffer Element to the\r
+ * application-owned buffer */\r
+ if (len > set->cfg.buf_size_rx)\r
+ len = set->cfg.buf_size_rx;\r
+ if (len > msg->data_len)\r
+ len = msg->data_len;\r
+ memcpy(msg->data, pThisRxBuf, len);\r
+ msg->data_len = len;\r
+ }\r
+ else\r
+ msg->data_len = 0;\r
+ /* clear the new data flag for the buffer */\r
+ if (buf_idx < 32)\r
+ mcan->MCAN_NDAT1 = (1 << buf_idx);\r
+ else\r
+ mcan->MCAN_NDAT2 = (1 << (buf_idx - 32));\r
+}\r
+\r
+uint8_t mcan_dequeue_received_msg(struct mcan_set *set, uint8_t fifo,\r
+ struct mcan_msg_info *msg)\r
+{\r
+ assert(fifo == 0 || fifo == 1);\r
+\r
+ Mcan *mcan = set->cfg.regs;\r
+ uint32_t *pThisRxBuf = 0;\r
+ uint32_t tempRy; /* temp copy of RX buffer word */\r
+ uint8_t buf_elem_data_size, len;\r
+ uint32_t *fifo_ack_reg;\r
+ uint32_t get_index;\r
+ uint8_t fill_level = 0; /* default: fifo empty */\r
+\r
+ if (fifo) {\r
+ get_index = (mcan->MCAN_RXF1S & MCAN_RXF1S_F1GI_Msk) >>\r
+ MCAN_RXF1S_F1GI_Pos;\r
+ fill_level = (uint8_t)((mcan->MCAN_RXF1S & MCAN_RXF1S_F1FL_Msk)\r
+ >> MCAN_RXF1S_F1FL_Pos);\r
+ pThisRxBuf = set->ram_fifo_rx1;\r
+ buf_elem_data_size = set->cfg.buf_size_rx_fifo1;\r
+ fifo_ack_reg = (uint32_t *) & mcan->MCAN_RXF1A;\r
+ } else {\r
+ get_index = (mcan->MCAN_RXF0S & MCAN_RXF0S_F0GI_Msk)\r
+ >> MCAN_RXF0S_F0GI_Pos;\r
+ fill_level = (uint8_t)((mcan->MCAN_RXF0S & MCAN_RXF0S_F0FL_Msk)\r
+ >> MCAN_RXF0S_F0FL_Pos);\r
+ pThisRxBuf = set->ram_fifo_rx0;\r
+ buf_elem_data_size = set->cfg.buf_size_rx_fifo0;\r
+ fifo_ack_reg = (uint32_t *) & mcan->MCAN_RXF0A;\r
+ }\r
+\r
+ if (fill_level == 0)\r
+ return 0;\r
+\r
+ pThisRxBuf += get_index * (MCAN_RAM_BUF_HDR_SIZE + buf_elem_data_size\r
+ / 4);\r
+ tempRy = *pThisRxBuf++; /* word R0 contains ID */\r
+ if (tempRy & MCAN_RAM_BUF_XTD)\r
+ msg->id = CAN_EXT_MSG_ID | (tempRy & MCAN_RAM_BUF_ID_XTD_Msk)\r
+ >> MCAN_RAM_BUF_ID_XTD_Pos;\r
+ else\r
+ msg->id = (tempRy & MCAN_RAM_BUF_ID_STD_Msk)\r
+ >> MCAN_RAM_BUF_ID_STD_Pos;\r
+ tempRy = *pThisRxBuf++; /* word R1 contains DLC & timestamps */\r
+ msg->full_len = len = get_data_length((enum mcan_dlc)\r
+ ((tempRy & MCAN_RAM_BUF_DLC_Msk) >> MCAN_RAM_BUF_DLC_Pos));\r
+ msg->timestamp = (tempRy & MCAN_RAM_BUF_RXTS_Msk)\r
+ >> MCAN_RAM_BUF_RXTS_Pos;\r
+ if (msg->data) {\r
+ /* copy the data from the Rx Buffer Element to the\r
+ * application-owned buffer */\r
+ if (len > buf_elem_data_size)\r
+ len = buf_elem_data_size;\r
+ if (len > msg->data_len)\r
+ len = msg->data_len;\r
+ memcpy(msg->data, pThisRxBuf, len);\r
+ msg->data_len = len;\r
+ }\r
+ else\r
+ msg->data_len = 0;\r
+ /* acknowledge reading the fifo entry */\r
+ *fifo_ack_reg = get_index;\r
+ /* return entries remaining in FIFO */\r
+ return (fill_level);\r
+}\r
+\r
+/**@}*/\r