diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 5a83bb63486..e0420efe784 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -10,7 +10,7 @@ zephyr_library_sources_ifdef(CONFIG_CAN_FAKE can_fake.c) zephyr_library_sources_ifdef(CONFIG_CAN_LOOPBACK can_loopback.c) zephyr_library_sources_ifdef(CONFIG_CAN_MCAN can_mcan.c) zephyr_library_sources_ifdef(CONFIG_CAN_MCP2515 can_mcp2515.c) -zephyr_library_sources_ifdef(CONFIG_CAN_MCP25XXFD can_mcp25xxfd.c) +zephyr_library_sources_ifdef(CONFIG_CAN_MCP251XFD can_mcp251xfd.c) zephyr_library_sources_ifdef(CONFIG_CAN_MCUX_FLEXCAN can_mcux_flexcan.c) zephyr_library_sources_ifdef(CONFIG_CAN_SAM can_sam.c) zephyr_library_sources_ifdef(CONFIG_CAN_SAM0 can_sam0.c) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 90e17889129..30381b09780 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -100,7 +100,7 @@ source "drivers/can/Kconfig.kvaser" source "drivers/can/Kconfig.fake" source "drivers/can/Kconfig.nxp_s32" source "drivers/can/Kconfig.tcan4x5x" -source "drivers/can/Kconfig.mcp25xxfd" +source "drivers/can/Kconfig.mcp251xfd" source "drivers/can/transceiver/Kconfig" diff --git a/drivers/can/Kconfig.mcp251xfd b/drivers/can/Kconfig.mcp251xfd new file mode 100644 index 00000000000..8e6cb5ad91c --- /dev/null +++ b/drivers/can/Kconfig.mcp251xfd @@ -0,0 +1,61 @@ +# MCP25XXFD CAN configuration options + +# Copyright (c) 2020 Abram Early +# Copyright (c) 2023 Andriy Gelman +# SPDX-License-Identifier: Apache-2.0 + +config CAN_MCP251XFD + bool "MCP25XXFD CAN Driver" + default y + depends on DT_HAS_MICROCHIP_MCP251XFD_ENABLED + select CRC + select SPI + help + Enable MCP25XXFD CAN Driver + +if CAN_MCP251XFD + +config CAN_MCP251XFD_MAX_TX_QUEUE + int "Maximum number of queued messages" + default 8 + range 1 32 + help + Defines the array size of transmit callback pointers and semaphores, + as well as the number of messages in the TX queue. + +config CAN_MCP251XFD_RX_FIFO_ITEMS + int "Number of CAN messages in the RX fifo" + default 16 + range 1 32 + help + Defines the number of CAN messages in the RX fifo. + +config CAN_MCP251XFD_INT_THREAD_STACK_SIZE + int "Stack size for interrupt handler" + default 768 + help + Size of the stack used for internal thread which is ran for + interrupt handling and incoming packets. + +config CAN_MCP251XFD_INT_THREAD_PRIO + int "Priority for interrupt handler" + default 2 + help + Thread priority of the interrupt handler. A higher number implies a + higher priority. The thread is cooperative and will not be interrupted by + another thread until execution is released. + +config CAN_MCP251XFD_READ_CRC_RETRIES + int "Number of retries during SFR register read" + default 5 + help + Number of retries during SFR register read if CRC fails. + +config CAN_MAX_FILTER + int "Maximum number of concurrent active filters" + default 5 + range 1 31 + help + Maximum number of filters supported by the can_add_rx_callback() API call. + +endif # CAN_MCP251XFD diff --git a/drivers/can/Kconfig.mcp25xxfd b/drivers/can/Kconfig.mcp25xxfd deleted file mode 100644 index 21cdb032f95..00000000000 --- a/drivers/can/Kconfig.mcp25xxfd +++ /dev/null @@ -1,56 +0,0 @@ -# MCP25XXFD CAN configuration options - -# Copyright (c) 2020 Abram Early -# SPDX-License-Identifier: Apache-2.0 - -DT_COMPAT_MICROCHIP_MCP25XXFD_CAN := microchip,mcp25xxfd - -config CAN_MCP25XXFD - bool "MCP25XXFD CAN Driver" - default $(dt_compat_enabled,$(DT_COMPAT_MICROCHIP_MCP25XXFD_CAN)) - depends on SPI - select CAN_AUTO_BUS_OFF_RECOVERY - select CAN_HAS_CANFD - select CAN_HAS_RX_TIMESTAMP - help - Enable MCP25XXFD CAN Driver - -if CAN_MCP25XXFD - -config CAN_MCP25XXFD_MAX_TX_QUEUE - int "Maximum number of queued messages" - default 4 - range 1 31 - help - Defines the array size of transmit callback pointers and semaphores, - as well as the number of TX FIFOs allocated on the MCP25XXFD. - -config CAN_MCP25XXFD_INT_THREAD_STACK_SIZE - int "Stack size for interrupt handler" - default 768 - help - Size of the stack used for internal thread which is ran for - interrupt handling and incoming packets. - -config CAN_MCP25XXFD_INT_THREAD_PRIO - int "Priority for interrupt handler" - default 2 - help - Priority level of the internal thread which is ran for - interrupt handling and incoming packets. - -config CAN_MAX_FILTER - int "Maximum number of concurrent active filters" - default 5 - range 1 31 - help - Defines the array size of the callback/msgq pointers. - Must be at least the size of concurrent reads. - -config CAN_MCP25XXFD_INIT_PRIORITY - int "Init priority" - default 80 - help - MCP25XXFD driver initialization priority, must be higher than SPI. - -endif # CAN_MCP25XXFD diff --git a/drivers/can/can_mcp251xfd.c b/drivers/can/can_mcp251xfd.c new file mode 100644 index 00000000000..83b35f38102 --- /dev/null +++ b/drivers/can/can_mcp251xfd.c @@ -0,0 +1,1786 @@ +/* + * Copyright (c) 2020 Abram Early + * Copyright (c) 2023 Andriy Gelman + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_mcp251xfd + +#include "can_mcp251xfd.h" + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(can_mcp251xfd, CONFIG_CAN_LOG_LEVEL); + +#define SP_IS_SET(inst) DT_INST_NODE_HAS_PROP(inst, sample_point) || + +/* + * Macro to exclude the sample point algorithm from compilation if not used + * Without the macro, the algorithm would always waste ROM + */ +#define USE_SP_ALGO (DT_INST_FOREACH_STATUS_OKAY(SP_IS_SET) 0) + +static void mcp251xfd_canframe_to_txobj(const struct can_frame *src, int mailbox_idx, + struct mcp251xfd_txobj *dst) +{ + dst->flags = 0; + + if ((src->flags & CAN_FRAME_IDE) != 0) { + dst->id = FIELD_PREP(MCP251XFD_OBJ_ID_SID_MASK, src->id >> 18); + dst->id |= FIELD_PREP(MCP251XFD_OBJ_ID_EID_MASK, src->id); + + dst->flags |= MCP251XFD_OBJ_FLAGS_IDE; + } else { + dst->id = FIELD_PREP(MCP251XFD_OBJ_ID_SID_MASK, src->id); + } + + if ((src->flags & CAN_FRAME_BRS) != 0) { + dst->flags |= MCP251XFD_OBJ_FLAGS_BRS; + } + + if ((src->flags & CAN_FRAME_RTR) != 0) { + dst->flags |= MCP251XFD_OBJ_FLAGS_RTR; + } + + dst->flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_DLC_MASK, src->dlc); +#if defined(CONFIG_CAN_FD_MODE) + if ((src->flags & CAN_FRAME_FDF) != 0) { + dst->flags |= MCP251XFD_OBJ_FLAGS_FDF; + } +#endif + dst->flags |= FIELD_PREP(MCP251XFD_OBJ_FLAGS_SEQ_MASK, mailbox_idx); + + dst->id = sys_cpu_to_le32(dst->id); + dst->flags = sys_cpu_to_le32(dst->flags); + + memcpy(dst->data, src->data, MIN(can_dlc_to_bytes(src->dlc), CAN_MAX_DLEN)); +} + +static void *mcp251xfd_read_reg(const struct device *dev, uint16_t addr, int len) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + struct mcp251xfd_spi_data *spi_data = &dev_data->spi_data; + uint16_t spi_cmd; + int ret; + + spi_cmd = sys_cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ | addr); + memcpy(&spi_data->header[1], &spi_cmd, sizeof(spi_cmd)); + + struct spi_buf tx_buf = {.buf = &spi_data->header[1], .len = MCP251XFD_SPI_CMD_LEN + len}; + struct spi_buf rx_buf = {.buf = &spi_data->header[1], .len = MCP251XFD_SPI_CMD_LEN + len}; + + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; + + ret = spi_transceive_dt(&dev_cfg->bus, &tx, &rx); + if (ret < 0) { + return NULL; + } + + return &spi_data->buf[0]; +} + +static void *mcp251xfd_read_crc(const struct device *dev, uint16_t addr, int len) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + struct mcp251xfd_spi_data *spi_data = &dev_data->spi_data; + int num_retries = CONFIG_CAN_MCP251XFD_READ_CRC_RETRIES + 1; + int ret; + + while (num_retries-- > 0) { + uint16_t crc_in, crc, spi_cmd; + + struct spi_buf tx_buf = {.buf = &spi_data->header[0], + .len = MCP251XFD_SPI_CMD_LEN + + MCP251XFD_SPI_LEN_FIELD_LEN + len + + MCP251XFD_SPI_CRC_LEN}; + + struct spi_buf rx_buf = {.buf = &spi_data->header[0], + .len = MCP251XFD_SPI_CMD_LEN + + MCP251XFD_SPI_LEN_FIELD_LEN + len + + MCP251XFD_SPI_CRC_LEN}; + + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + const struct spi_buf_set rx = {.buffers = &rx_buf, .count = 1}; + + spi_cmd = sys_cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_READ_CRC | addr); + memcpy(&spi_data->header[0], &spi_cmd, sizeof(spi_cmd)); + spi_data->header[2] = len; + + /* + * Evaluate initial crc over spi_cmd and length as these value will change after + * spi transaction is finished. + */ + crc_in = crc16(MCP251XFD_CRC_POLY, MCP251XFD_CRC_SEED, + (uint8_t *)(&spi_data->header[0]), + MCP251XFD_SPI_CMD_LEN + MCP251XFD_SPI_LEN_FIELD_LEN); + + ret = spi_transceive_dt(&dev_cfg->bus, &tx, &rx); + if (ret < 0) { + continue; + } + + /* Continue crc calculation over the data field and the crc field */ + crc = crc16(MCP251XFD_CRC_POLY, crc_in, &spi_data->buf[0], + len + MCP251XFD_SPI_CRC_LEN); + if (crc == 0) { + return &spi_data->buf[0]; + } + } + + return NULL; +} + +static inline void *mcp251xfd_get_spi_buf_ptr(const struct device *dev) +{ + struct mcp251xfd_data *dev_data = dev->data; + struct mcp251xfd_spi_data *spi_data = &dev_data->spi_data; + + return &spi_data->buf[0]; +} + +static int mcp251xfd_write(const struct device *dev, uint16_t addr, int len) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + struct mcp251xfd_spi_data *spi_data = &dev_data->spi_data; + uint16_t spi_cmd; + + struct spi_buf tx_buf = {.buf = &spi_data->header[1], .len = MCP251XFD_SPI_CMD_LEN + len}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + + spi_cmd = sys_cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_WRITE | addr); + memcpy(&spi_data->header[1], &spi_cmd, sizeof(spi_cmd)); + + return spi_write_dt(&dev_cfg->bus, &tx); +} + +static int mcp251xfd_fifo_write(const struct device *dev, int mailbox_idx, + const struct can_frame *msg) +{ + uint32_t *regs; + struct mcp251xfd_txobj *txobj; + uint8_t *reg_byte; + uint16_t address; + int tx_len; + int ret; + + /* read fifosta and ua at the same time */ + regs = mcp251xfd_read_crc(dev, MCP251XFD_REG_TXQSTA, MCP251XFD_REG_SIZE * 2); + if (!regs) { + LOG_ERR("Failed to read 8 bytes from REG_TXQSTA"); + return -EINVAL; + } + + /* check if fifo is full */ + if (!(regs[0] & MCP251XFD_REG_TXQSTA_TXQNIF)) { + return -ENOMEM; + } + + address = MCP251XFD_RAM_START_ADDR + regs[1]; + + txobj = mcp251xfd_get_spi_buf_ptr(dev); + mcp251xfd_canframe_to_txobj(msg, mailbox_idx, txobj); + + tx_len = MCP251XFD_OBJ_HEADER_SIZE + + ROUND_UP(can_dlc_to_bytes(msg->dlc), MCP251XFD_RAM_ALIGNMENT); + + ret = mcp251xfd_write(dev, address, tx_len); + if (ret < 0) { + return ret; + } + + reg_byte = mcp251xfd_get_spi_buf_ptr(dev); + *reg_byte = MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(MCP251XFD_REG_TXQCON_UINC | + MCP251XFD_REG_TXQCON_TXREQ); + + return mcp251xfd_write(dev, MCP251XFD_REG_TXQCON + 1, 1); +} + +static void mcp251xfd_rxobj_to_canframe(struct mcp251xfd_rxobj *src, struct can_frame *dst) +{ + memset(dst, 0, offsetof(struct can_frame, data)); + + src->id = sys_le32_to_cpu(src->id); + src->flags = sys_le32_to_cpu(src->flags); + + if ((src->flags & MCP251XFD_OBJ_FLAGS_IDE) != 0) { + dst->id = FIELD_GET(MCP251XFD_OBJ_ID_EID_MASK, src->id); + dst->id |= FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, src->id) << 18; + dst->flags |= CAN_FRAME_IDE; + } else { + dst->id = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, src->id); + } + + if ((src->flags & MCP251XFD_OBJ_FLAGS_BRS) != 0) { + dst->flags |= CAN_FRAME_BRS; + } + + if ((src->flags & MCP251XFD_OBJ_FLAGS_RTR) != 0) { + dst->flags |= CAN_FRAME_RTR; + } + +#if defined(CONFIG_CAN_FD_MODE) + if ((src->flags & MCP251XFD_OBJ_FLAGS_FDF) != 0) { + dst->flags |= CAN_FRAME_FDF; + } +#endif + + dst->dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC_MASK, src->flags); + +#if defined(CONFIG_CAN_RX_TIMESTAMP) + dst->timestamp = sys_le32_to_cpu(src->timestamp); +#endif + + memcpy(dst->data, src->data, MIN(can_dlc_to_bytes(dst->dlc), CAN_MAX_DLEN)); +} + +static int mcp251xfd_get_mode_internal(const struct device *dev, uint8_t *mode) +{ + uint8_t *reg_byte; + uint32_t mask = MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(MCP251XFD_REG_CON_OPMOD_MASK); + + reg_byte = mcp251xfd_read_crc(dev, MCP251XFD_REG_CON_B2, 1); + if (!reg_byte) { + return -EINVAL; + } + + *mode = FIELD_GET(mask, *reg_byte); + + return 0; +} + +static int mcp251xfd_reg_check_value_wtimeout(const struct device *dev, uint16_t addr, + uint32_t value, uint32_t mask, + uint32_t timeout_usec, int retries, bool allow_yield) +{ + uint32_t *reg; + uint32_t delay = timeout_usec / retries; + + for (;;) { + reg = mcp251xfd_read_crc(dev, addr, MCP251XFD_REG_SIZE); + if (!reg) { + return -EINVAL; + } + + *reg = sys_le32_to_cpu(*reg); + + if ((*reg & mask) == value) { + return 0; + } + + if (--retries < 0) { + LOG_ERR("Timeout validing 0x%x", addr); + return -EIO; + } + + if (allow_yield) { + k_sleep(K_USEC(delay)); + } else { + k_busy_wait(delay); + } + } + return 0; +} + +static int mcp251xfd_set_tdc(const struct device *dev, bool is_enabled, int tdc_offset) +{ + uint32_t *reg; + + if (is_enabled && + (tdc_offset < MCP251XFD_REG_TDC_TDCO_MIN || tdc_offset > MCP251XFD_REG_TDC_TDCO_MAX)) { + return -EINVAL; + } + + reg = mcp251xfd_get_spi_buf_ptr(dev); + + if (is_enabled) { + *reg = FIELD_PREP(MCP251XFD_REG_TDC_TDCMOD_MASK, MCP251XFD_REG_TDC_TDCMOD_AUTO); + *reg |= FIELD_PREP(MCP251XFD_REG_TDC_TDCO_MASK, tdc_offset); + } else { + *reg = FIELD_PREP(MCP251XFD_REG_TDC_TDCMOD_MASK, MCP251XFD_REG_TDC_TDCMOD_DISABLED); + } + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_TDC, MCP251XFD_REG_SIZE); +} + +static int mcp251xfd_set_mode_internal(const struct device *dev, uint8_t requested_mode) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + uint32_t opmod, reg_con; + int ret = 0; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg = mcp251xfd_read_crc(dev, MCP251XFD_REG_CON, MCP251XFD_REG_SIZE); + if (!reg) { + ret = -EINVAL; + goto done; + } + + reg_con = sys_le32_to_cpu(*reg); + + opmod = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, reg_con); + if (opmod == requested_mode) { + goto done; + } + +#if defined(CONFIG_CAN_FD_MODE) + if (dev_data->current_mcp251xfd_mode == MCP251XFD_REG_CON_MODE_CONFIG) { + if (requested_mode == MCP251XFD_REG_CON_MODE_CAN2_0 || + requested_mode == MCP251XFD_REG_CON_MODE_EXT_LOOPBACK || + requested_mode == MCP251XFD_REG_CON_MODE_INT_LOOPBACK) { + ret = mcp251xfd_set_tdc(dev, false, 0); + } else if (requested_mode == MCP251XFD_REG_CON_MODE_MIXED) { + ret = mcp251xfd_set_tdc(dev, true, dev_data->tdco); + } + + if (ret < 0) { + goto done; + } + } +#endif + + reg_con &= ~MCP251XFD_REG_CON_REQOP_MASK; + reg_con |= FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, requested_mode); + + *reg = sys_cpu_to_le32(reg_con); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_CON, MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write REG_CON register [%d]", MCP251XFD_REG_CON); + goto done; + } + + ret = mcp251xfd_reg_check_value_wtimeout( + dev, MCP251XFD_REG_CON, FIELD_PREP(MCP251XFD_REG_CON_OPMOD_MASK, requested_mode), + MCP251XFD_REG_CON_OPMOD_MASK, MCP251XFD_MODE_CHANGE_TIMEOUT_USEC, + MCP251XFD_MODE_CHANGE_RETRIES, true); +done: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static int mcp251xfd_set_mode(const struct device *dev, can_mode_t mode) +{ + struct mcp251xfd_data *dev_data = dev->data; + + if (dev_data->started) { + return -EBUSY; + } + + /* todo: Add CAN_MODE_ONE_SHOT support */ + if ((mode & (CAN_MODE_3_SAMPLES | CAN_MODE_ONE_SHOT)) != 0) { + return -ENOTSUP; + } + + if (mode == CAN_MODE_NORMAL) { + dev_data->next_mcp251xfd_mode = MCP251XFD_REG_CON_MODE_CAN2_0; + } + + if ((mode & CAN_MODE_FD) != 0) { +#if defined(CONFIG_CAN_FD_MODE) + dev_data->next_mcp251xfd_mode = MCP251XFD_REG_CON_MODE_MIXED; +#else + return -ENOTSUP; +#endif + } + + if ((mode & CAN_MODE_LISTENONLY) != 0) { + dev_data->next_mcp251xfd_mode = MCP251XFD_REG_CON_MODE_LISTENONLY; + } + + if ((mode & CAN_MODE_LOOPBACK) != 0) { + dev_data->next_mcp251xfd_mode = MCP251XFD_REG_CON_MODE_EXT_LOOPBACK; + } + + dev_data->mode = mode; + + return 0; +} + +static int mcp251xfd_set_timing(const struct device *dev, const struct can_timing *timing) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + int ret; + + if (!timing) { + return -EINVAL; + } + + if (dev_data->started) { + return -EBUSY; + } + + __ASSERT_NO_MSG(timing->prop_seg == 0); + __ASSERT_NO_MSG(timing->sjw >= 1 && timing->sjw <= 128); + __ASSERT_NO_MSG(timing->phase_seg1 >= 2 && timing->phase_seg1 <= 256); + __ASSERT_NO_MSG(timing->phase_seg2 >= 1 && timing->phase_seg2 <= 128); + __ASSERT_NO_MSG(timing->prescaler >= 1 && timing->prescaler <= 256); + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg = mcp251xfd_get_spi_buf_ptr(dev); + *reg = FIELD_PREP(MCP251XFD_REG_NBTCFG_BRP_MASK, timing->prescaler - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_NBTCFG_TSEG1_MASK, + timing->prop_seg + timing->phase_seg1 - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_NBTCFG_TSEG2_MASK, timing->phase_seg2 - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_NBTCFG_SJW_MASK, timing->sjw - 1); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_NBTCFG, MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write NBTCFG register [%d]", ret); + } + + k_mutex_unlock(&dev_data->mutex); + + return ret; +} + + +#if defined(CONFIG_CAN_FD_MODE) +static int mcp251xfd_set_timing_data(const struct device *dev, const struct can_timing *timing) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + int ret; + + if (!timing) { + return -EINVAL; + } + + if (dev_data->started) { + return -EBUSY; + } + + __ASSERT_NO_MSG(timing->prop_seg == 0); + __ASSERT_NO_MSG(timing->sjw >= 1 && timing->sjw <= 16); + __ASSERT_NO_MSG(timing->phase_seg1 >= 1 && timing->phase_seg1 <= 32); + __ASSERT_NO_MSG(timing->phase_seg2 >= 1 && timing->phase_seg2 <= 16); + __ASSERT_NO_MSG(timing->prescaler >= 1 && timing->prescaler <= 256); + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg = mcp251xfd_get_spi_buf_ptr(dev); + + *reg = FIELD_PREP(MCP251XFD_REG_DBTCFG_BRP_MASK, timing->prescaler - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_DBTCFG_TSEG1_MASK, + timing->prop_seg + timing->phase_seg1 - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_DBTCFG_TSEG2_MASK, timing->phase_seg2 - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_DBTCFG_SJW_MASK, timing->sjw - 1); + + *reg = sys_cpu_to_le32(*reg); + + dev_data->tdco = timing->prescaler * (timing->prop_seg + timing->phase_seg1); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_DBTCFG, MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write DBTCFG register [%d]", ret); + } + + k_mutex_unlock(&dev_data->mutex); + + return ret; +} +#endif + +static int mcp251xfd_send(const struct device *dev, const struct can_frame *msg, + k_timeout_t timeout, can_tx_callback_t callback, void *callback_arg) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint8_t mailbox_idx; + int ret = 0; + + LOG_DBG("Sending %d bytes. Id: 0x%x, ID type: %s %s %s %s", can_dlc_to_bytes(msg->dlc), + msg->id, msg->flags & CAN_FRAME_IDE ? "extended" : "standard", + msg->flags & CAN_FRAME_RTR ? "RTR" : "", + msg->flags & CAN_FRAME_FDF ? "FD frame" : "", + msg->flags & CAN_FRAME_BRS ? "BRS" : ""); + + __ASSERT_NO_MSG(callback != NULL); + + if (!dev_data->started) { + return -ENETDOWN; + } + + if (dev_data->state == CAN_STATE_BUS_OFF) { + return -ENETUNREACH; + } + + if ((msg->flags & CAN_FRAME_FDF) == 0 && msg->dlc > CAN_MAX_DLC) { + LOG_ERR("DLC of %d without fd flag set.", msg->dlc); + return -EINVAL; + } + + if ((msg->flags & CAN_FRAME_FDF) && !(dev_data->mode & CAN_MODE_FD)) { + return -ENOTSUP; + } + + if (k_sem_take(&dev_data->tx_sem, timeout) != 0) { + return -EAGAIN; + } + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + for (mailbox_idx = 0; mailbox_idx < MCP251XFD_TX_QUEUE_ITEMS; mailbox_idx++) { + if ((BIT(mailbox_idx) & dev_data->mailbox_usage) == 0) { + dev_data->mailbox_usage |= BIT(mailbox_idx); + break; + } + } + + if (mailbox_idx >= MCP251XFD_TX_QUEUE_ITEMS) { + k_sem_give(&dev_data->tx_sem); + ret = -EIO; + goto done; + } + + dev_data->mailbox[mailbox_idx].cb = callback; + dev_data->mailbox[mailbox_idx].cb_arg = callback_arg; + + ret = mcp251xfd_fifo_write(dev, mailbox_idx, msg); + + if (ret < 0) { + dev_data->mailbox_usage &= ~BIT(mailbox_idx); + dev_data->mailbox[mailbox_idx].cb = NULL; + k_sem_give(&dev_data->tx_sem); + } + +done: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static int mcp251xfd_add_rx_filter(const struct device *dev, can_rx_callback_t rx_cb, void *cb_arg, + const struct can_filter *filter) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + uint8_t *reg_byte; + int filter_idx; + int ret; + + __ASSERT(rx_cb != NULL, "rx_cb can not be null"); + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + for (filter_idx = 0; filter_idx < CONFIG_CAN_MAX_FILTER ; filter_idx++) { + if ((BIT(filter_idx) & dev_data->filter_usage) == 0) { + break; + } + } + + if (filter_idx >= CONFIG_CAN_MAX_FILTER) { + filter_idx = -ENOSPC; + goto done; + } + + if ((filter->flags & CAN_FILTER_RTR) != 0) { + filter_idx = -ENOTSUP; + goto done; + } + + reg = mcp251xfd_get_spi_buf_ptr(dev); + + if ((filter->flags & CAN_FILTER_IDE) != 0) { + *reg = FIELD_PREP(MCP251XFD_REG_FLTOBJ_SID_MASK, filter->id >> 18); + *reg |= FIELD_PREP(MCP251XFD_REG_FLTOBJ_EID_MASK, filter->id); + *reg |= MCP251XFD_REG_FLTOBJ_EXIDE; + } else { + *reg = FIELD_PREP(MCP251XFD_REG_FLTOBJ_SID_MASK, filter->id); + } + + *reg = sys_cpu_to_le32(*reg); + ret = mcp251xfd_write(dev, MCP251XFD_REG_FLTOBJ(filter_idx), MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write FLTOBJ register [%d]", ret); + goto done; + } + + reg = mcp251xfd_get_spi_buf_ptr(dev); + if ((filter->flags & CAN_FILTER_IDE) != 0) { + *reg = FIELD_PREP(MCP251XFD_REG_MASK_MSID_MASK, filter->mask >> 18); + *reg |= FIELD_PREP(MCP251XFD_REG_MASK_MEID_MASK, filter->mask); + } else { + *reg = FIELD_PREP(MCP251XFD_REG_MASK_MSID_MASK, filter->mask); + } + *reg |= MCP251XFD_REG_MASK_MIDE; + + *reg = sys_cpu_to_le32(*reg); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_FLTMASK(filter_idx), MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write FLTMASK register [%d]", ret); + goto done; + } + + reg_byte = mcp251xfd_get_spi_buf_ptr(dev); + *reg_byte = MCP251XFD_REG_BYTE_FLTCON_FLTEN; + *reg_byte |= FIELD_PREP(MCP251XFD_REG_BYTE_FLTCON_FBP_MASK, MCP251XFD_RX_FIFO_IDX); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_BYTE_FLTCON(filter_idx), 1); + if (ret < 0) { + LOG_ERR("Failed to write FLTCON register [%d]", ret); + goto done; + } + + dev_data->filter_usage |= BIT(filter_idx); + dev_data->filter[filter_idx] = *filter; + dev_data->rx_cb[filter_idx] = rx_cb; + dev_data->cb_arg[filter_idx] = cb_arg; + +done: + k_mutex_unlock(&dev_data->mutex); + + return filter_idx; +} + +static void mcp251xfd_remove_rx_filter(const struct device *dev, int filter_idx) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint8_t *reg_byte; + uint32_t *reg; + int ret; + + if (filter_idx < 0 || filter_idx >= CONFIG_CAN_MAX_FILTER) { + LOG_ERR("Filter ID %d out of bounds", filter_idx); + return; + } + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg_byte = mcp251xfd_get_spi_buf_ptr(dev); + *reg_byte = 0; + + ret = mcp251xfd_write(dev, MCP251XFD_REG_BYTE_FLTCON(filter_idx), 1); + if (ret < 0) { + LOG_ERR("Failed to write FLTCON register [%d]", ret); + goto done; + } + + dev_data->filter_usage &= ~BIT(filter_idx); + + reg = mcp251xfd_get_spi_buf_ptr(dev); + reg[0] = 0; + + ret = mcp251xfd_write(dev, MCP251XFD_REG_FLTCON(filter_idx), MCP251XFD_REG_SIZE); + if (ret < 0) { + LOG_ERR("Failed to write FLTCON register [%d]", ret); + } + +done: + k_mutex_unlock(&dev_data->mutex); +} + +static void mcp251xfd_set_state_change_callback(const struct device *dev, + can_state_change_callback_t cb, void *user_data) +{ + struct mcp251xfd_data *dev_data = dev->data; + + dev_data->state_change_cb = cb; + dev_data->state_change_cb_data = user_data; +} + +static int mcp251xfd_get_state(const struct device *dev, enum can_state *state, + struct can_bus_err_cnt *err_cnt) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + int ret = 0; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg = mcp251xfd_read_crc(dev, MCP251XFD_REG_TREC, MCP251XFD_REG_SIZE); + if (!reg) { + ret = -EINVAL; + goto done; + } + + *reg = sys_le32_to_cpu(*reg); + + if (err_cnt != NULL) { + err_cnt->tx_err_cnt = FIELD_GET(MCP251XFD_REG_TREC_TEC_MASK, *reg); + err_cnt->rx_err_cnt = FIELD_GET(MCP251XFD_REG_TREC_REC_MASK, *reg); + } + + if (state == NULL) { + goto done; + } + + if (!dev_data->started) { + *state = CAN_STATE_STOPPED; + goto done; + } + + if ((*reg & MCP251XFD_REG_TREC_TXBO) != 0) { + *state = CAN_STATE_BUS_OFF; + } else if ((*reg & MCP251XFD_REG_TREC_TXBP) != 0) { + *state = CAN_STATE_ERROR_PASSIVE; + } else if ((*reg & MCP251XFD_REG_TREC_RXBP) != 0) { + *state = CAN_STATE_ERROR_PASSIVE; + } else if ((*reg & MCP251XFD_REG_TREC_TXWARN) != 0) { + *state = CAN_STATE_ERROR_WARNING; + } else if ((*reg & MCP251XFD_REG_TREC_RXWARN) != 0) { + *state = CAN_STATE_ERROR_WARNING; + } else { + *state = CAN_STATE_ERROR_ACTIVE; + } + +done: + k_mutex_unlock(&dev_data->mutex); + return 0; +} + +static int mcp251xfd_get_core_clock(const struct device *dev, uint32_t *rate) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + + *rate = dev_cfg->osc_freq; + return 0; +} + +static int mcp251xfd_get_max_filters(const struct device *dev, bool ide) +{ + ARG_UNUSED(ide); + + return CONFIG_CAN_MAX_FILTER; +} + +static int mcp251xfd_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + + *max_bitrate = dev_cfg->max_bitrate; + + return 0; +} + +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +static int mcp251xfd_recover(const struct device *dev, k_timeout_t timeout) +{ + struct mcp251xfd_data *dev_data = dev->data; + + ARG_UNUSED(timeout); + + if (!dev_data->started) { + return -ENETDOWN; + } + + return -ENOTSUP; +} +#endif + +static int mcp251xfd_handle_fifo_read(const struct device *dev, const struct mcp251xfd_fifo *fifo, + uint8_t fifo_type) +{ + int ret = 0; + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *regs, fifosta, ua; + uint8_t *reg_byte; + + int len; + int fetch_total = 0; + int ui_inc = 0; + uint32_t fifo_tail_index, fifo_tail_addr; + uint8_t fifo_head_index; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + /* read in FIFOSTA and FIFOUA at the same time */ + regs = mcp251xfd_read_crc(dev, MCP251XFD_REG_FIFOCON_TO_STA(fifo->reg_fifocon_addr), + 2 * MCP251XFD_REG_SIZE); + if (!regs) { + ret = -EINVAL; + goto done; + } + fifosta = sys_le32_to_cpu(regs[0]); + ua = sys_le32_to_cpu(regs[1]); + + /* is there any data in the fifo? */ + if (!(fifosta & MCP251XFD_REG_FIFOSTA_TFNRFNIF)) { + goto done; + } + + fifo_tail_addr = ua; + fifo_tail_index = (fifo_tail_addr - fifo->ram_start_addr) / fifo->item_size; + + if (fifo_type == MCP251XFD_FIFO_TYPE_RX) { + /* + * fifo_head_index points where the next message will be written. + * It points to one past the end of the fifo. + */ + fifo_head_index = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifosta); + if (fifo_head_index == 0) { + fifo_head_index = fifo->capacity - 1; + } else { + fifo_head_index -= 1; + } + + if (fifo_tail_index > fifo_head_index) { + /* fetch to the end of the memory and then wrap to the start */ + fetch_total = fifo->capacity - 1 - fifo_tail_index + 1; + fetch_total += fifo_head_index + 1; + } else { + fetch_total = fifo_head_index - fifo_tail_index + 1; + } + } else if (fifo_type == MCP251XFD_FIFO_TYPE_TEF) { + /* FIFOCI doesn't exist for TEF queues, so fetch one message at a time */ + fifo_head_index = fifo_tail_index; + fetch_total = 1; + } else { + ret = -EINVAL; + goto done; + } + + while (fetch_total > 0) { + uint16_t memory_addr; + uint8_t *data; + + if (fifo_tail_index > fifo_head_index) { + len = fifo->capacity - 1 - fifo_tail_index + 1; + } else { + len = fifo_head_index - fifo_tail_index + 1; + } + + memory_addr = MCP251XFD_RAM_START_ADDR + fifo->ram_start_addr + + fifo_tail_index * fifo->item_size; + + data = mcp251xfd_read_reg(dev, memory_addr, len * fifo->item_size); + if (!data) { + LOG_ERR("Error fetching batch message"); + ret = -EINVAL; + goto done; + } + + for (int i = 0; i < len; i++) { + fifo->msg_handler(dev, (void *)(&data[i * fifo->item_size])); + } + + fifo_tail_index = (fifo_tail_index + len) % fifo->capacity; + fetch_total -= len; + ui_inc += len; + } + + reg_byte = mcp251xfd_get_spi_buf_ptr(dev); + *reg_byte = MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(MCP251XFD_REG_FIFOCON_UINC); + + for (int i = 0; i < ui_inc; i++) { + ret = mcp251xfd_write(dev, fifo->reg_fifocon_addr + 1, 1); + if (ret < 0) { + LOG_ERR("Failed to increment pointer"); + goto done; + } + } + +done: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static void mcp251xfd_reset_tx_fifos(const struct device *dev, int status) +{ + struct mcp251xfd_data *dev_data = dev->data; + + LOG_INF("All FIFOs Reset"); + k_mutex_lock(&dev_data->mutex, K_FOREVER); + for (int i = 0; i < MCP251XFD_TX_QUEUE_ITEMS; i++) { + can_tx_callback_t callback; + + if (!(dev_data->mailbox_usage & BIT(i))) { + continue; + } + + callback = dev_data->mailbox[i].cb; + if (callback) { + callback(dev, status, dev_data->mailbox[i].cb_arg); + } + + dev_data->mailbox_usage &= ~BIT(i); + dev_data->mailbox[i].cb = NULL; + k_sem_give(&dev_data->tx_sem); + } + k_mutex_unlock(&dev_data->mutex); +} + +/* + * CERRIF will be set each time a threshold in the TEC/REC counter is crossed by the following + * conditions: + * • TEC or REC exceeds the Error Warning state threshold + * • The transmitter or receiver transitions to Error Passive state + * • The transmitter transitions to Bus Off state + * • The transmitter or receiver transitions from Error Passive to Error Active state + * • The module transitions from Bus Off to Error Active state, after the bus off recovery + * sequence + * When the user clears CERRIF, it will remain clear until a new counter crossing occurs. + */ +static int mcp251xfd_handle_cerrif(const struct device *dev) +{ + enum can_state new_state; + struct mcp251xfd_data *dev_data = dev->data; + struct can_bus_err_cnt err_cnt; + int ret = 0; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + ret = mcp251xfd_get_state(dev, &new_state, &err_cnt); + if (ret < 0) { + goto done; + } + + if (new_state == dev_data->state) { + goto done; + } + + LOG_INF("State %d -> %d (tx: %d, rx: %d)", dev_data->state, new_state, err_cnt.tx_err_cnt, + err_cnt.rx_err_cnt); + + /* Upon entering bus-off, all the fifos are reset. */ + dev_data->state = new_state; + if (new_state == CAN_STATE_BUS_OFF) { + mcp251xfd_reset_tx_fifos(dev, -ENETDOWN); + } + + if (dev_data->state_change_cb) { + dev_data->state_change_cb(dev, new_state, err_cnt, dev_data->state_change_cb_data); + } + +done: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static int mcp251xfd_handle_modif(const struct device *dev) +{ + struct mcp251xfd_data *dev_data = dev->data; + uint8_t mode; + int ret; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + ret = mcp251xfd_get_mode_internal(dev, &mode); + if (ret < 0) { + goto finish; + } + + dev_data->current_mcp251xfd_mode = mode; + + LOG_INF("Switched to mode %d", mode); + + if (mode == dev_data->next_mcp251xfd_mode) { + ret = 0; + goto finish; + } + + /* try to transition back into our target mode */ + if (dev_data->started) { + LOG_INF("Switching back into mode %d", dev_data->next_mcp251xfd_mode); + ret = mcp251xfd_set_mode_internal(dev, dev_data->next_mcp251xfd_mode); + } + +finish: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static int mcp251xfd_handle_ivmif(const struct device *dev) +{ + uint32_t *reg; + struct mcp251xfd_data *dev_data = dev->data; + int ret; + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + reg = mcp251xfd_read_crc(dev, MCP251XFD_REG_BDIAG1, MCP251XFD_REG_SIZE); + if (!reg) { + ret = -EINVAL; + goto done; + } + + *reg = sys_le32_to_cpu(*reg); + + if ((*reg & MCP251XFD_REG_BDIAG1_TXBOERR) != 0) { + LOG_INF("ivmif bus-off error"); + mcp251xfd_reset_tx_fifos(dev, -ENETDOWN); + } + + /* Clear the values in diag */ + reg = mcp251xfd_get_spi_buf_ptr(dev); + reg[0] = 0; + ret = mcp251xfd_write(dev, MCP251XFD_REG_BDIAG1, MCP251XFD_REG_SIZE); + +done: + k_mutex_unlock(&dev_data->mutex); + return ret; +} + +static void mcp251xfd_handle_interrupts(const struct device *dev) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + uint16_t *reg_int_hw; + uint32_t reg_int; + int ret; + uint8_t consecutive_calls = 0; + + while (1) { + k_mutex_lock(&dev_data->mutex, K_FOREVER); + reg_int_hw = mcp251xfd_read_crc(dev, MCP251XFD_REG_INT, sizeof(*reg_int_hw)); + + if (!reg_int_hw) { + k_mutex_unlock(&dev_data->mutex); + continue; + } + + *reg_int_hw = sys_le16_to_cpu(*reg_int_hw); + + reg_int = *reg_int_hw; + + /* these interrupt flags need to be explicitly cleared */ + if (*reg_int_hw & MCP251XFD_REG_INT_IF_CLEARABLE_MASK) { + + *reg_int_hw &= ~MCP251XFD_REG_INT_IF_CLEARABLE_MASK; + + *reg_int_hw = sys_cpu_to_le16(*reg_int_hw); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_INT, sizeof(*reg_int_hw)); + if (ret) { + LOG_ERR("Error clearing REG_INT interrupts [%d]", ret); + } + } + + k_mutex_unlock(&dev_data->mutex); + + if ((reg_int & MCP251XFD_REG_INT_RXIF) != 0) { + ret = mcp251xfd_handle_fifo_read(dev, &dev_cfg->rx_fifo, + MCP251XFD_FIFO_TYPE_RX); + if (ret < 0) { + LOG_ERR("Error handling RXIF [%d]", ret); + } + } + + if ((reg_int & MCP251XFD_REG_INT_TEFIF) != 0) { + ret = mcp251xfd_handle_fifo_read(dev, &dev_cfg->tef_fifo, + MCP251XFD_FIFO_TYPE_TEF); + if (ret < 0) { + LOG_ERR("Error handling TEFIF [%d]", ret); + } + } + + if ((reg_int & MCP251XFD_REG_INT_IVMIF) != 0) { + ret = mcp251xfd_handle_ivmif(dev); + if (ret < 0) { + LOG_ERR("Error handling IVMIF [%d]", ret); + } + } + + if ((reg_int & MCP251XFD_REG_INT_MODIF) != 0) { + ret = mcp251xfd_handle_modif(dev); + if (ret < 0) { + LOG_ERR("Error handling MODIF [%d]", ret); + } + } + + /* + * From Linux mcp251xfd driver + * On the MCP2527FD and MCP2518FD, we don't get a CERRIF IRQ on the transition + * TX ERROR_WARNING -> TX ERROR_ACTIVE. + */ + if ((reg_int & MCP251XFD_REG_INT_CERRIF) || + dev_data->state > CAN_STATE_ERROR_ACTIVE) { + ret = mcp251xfd_handle_cerrif(dev); + if (ret < 0) { + LOG_ERR("Error handling CERRIF [%d]", ret); + } + } + + /* Break from loop if INT pin is inactive */ + consecutive_calls++; + ret = gpio_pin_get_dt(&dev_cfg->int_gpio_dt); + if (ret < 0) { + LOG_ERR("Couldn't read INT pin [%d]", ret); + } else if (ret == 0) { + /* All interrupt flags handled */ + break; + } else if (consecutive_calls % MCP251XFD_MAX_INT_HANDLER_CALLS == 0) { + /* If there are clock problems, then MODIF cannot be cleared. */ + /* This is detected if there are too many consecutive calls. */ + /* Sleep this thread if this happens. */ + k_sleep(K_USEC(MCP251XFD_INT_HANDLER_SLEEP_USEC)); + } + } +} + +static void mcp251xfd_int_thread(const struct device *dev) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + + while (1) { + int ret; + + k_sem_take(&dev_data->int_sem, K_FOREVER); + mcp251xfd_handle_interrupts(dev); + + /* Re-enable pin interrupts */ + ret = gpio_pin_interrupt_configure_dt(&dev_cfg->int_gpio_dt, GPIO_INT_LEVEL_ACTIVE); + if (ret < 0) { + LOG_ERR("Couldn't enable pin interrupt [%d]", ret); + k_oops(); + } + } +} + +static void mcp251xfd_int_gpio_callback(const struct device *dev_gpio, struct gpio_callback *cb, + uint32_t pins) +{ + ARG_UNUSED(dev_gpio); + struct mcp251xfd_data *dev_data = CONTAINER_OF(cb, struct mcp251xfd_data, int_gpio_cb); + const struct device *dev = dev_data->dev; + const struct mcp251xfd_config *dev_cfg = dev->config; + int ret; + + /* Disable pin interrupts */ + ret = gpio_pin_interrupt_configure_dt(&dev_cfg->int_gpio_dt, GPIO_INT_DISABLE); + if (ret < 0) { + LOG_ERR("Couldn't disable pin interrupt [%d]", ret); + k_oops(); + } + + k_sem_give(&dev_data->int_sem); +} + +static int mcp251xfd_get_capabilities(const struct device *dev, can_mode_t *cap) +{ + ARG_UNUSED(dev); + + *cap = CAN_MODE_NORMAL | CAN_MODE_LISTENONLY | CAN_MODE_LOOPBACK; + +#if defined(CONFIG_CAN_FD_MODE) + *cap |= CAN_MODE_FD; +#endif + + return 0; +} + +static int mcp251xfd_start(const struct device *dev) +{ + struct mcp251xfd_data *dev_data = dev->data; + const struct mcp251xfd_config *dev_cfg = dev->config; + int ret; + + if (dev_data->started) { + return -EALREADY; + } + + /* in case of a race between mcp251xfd_send() and mcp251xfd_stop() */ + mcp251xfd_reset_tx_fifos(dev, -ENETDOWN); + + if (dev_cfg->phy != NULL) { + ret = can_transceiver_enable(dev_cfg->phy); + if (ret < 0) { + LOG_ERR("Failed to enable CAN transceiver [%d]", ret); + return ret; + } + } + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + ret = mcp251xfd_set_mode_internal(dev, dev_data->next_mcp251xfd_mode); + if (ret < 0) { + LOG_ERR("Failed to set the mode [%d]", ret); + if (dev_cfg->phy != NULL) { + /* Attempt to disable the CAN transceiver in case of error */ + (void)can_transceiver_disable(dev_cfg->phy); + } + } else { + dev_data->started = true; + } + + k_mutex_unlock(&dev_data->mutex); + + return ret; +} + +static int mcp251xfd_stop(const struct device *dev) +{ + struct mcp251xfd_data *dev_data = dev->data; + const struct mcp251xfd_config *dev_cfg = dev->config; + uint8_t *reg_byte; + int ret; + + if (!dev_data->started) { + return -EALREADY; + } + + k_mutex_lock(&dev_data->mutex, K_FOREVER); + + /* abort all transmissions */ + reg_byte = mcp251xfd_get_spi_buf_ptr(dev); + *reg_byte = MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(MCP251XFD_REG_CON_ABAT); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_CON_B3, 1); + if (ret < 0) { + k_mutex_unlock(&dev_data->mutex); + return ret; + } + + /* wait for all the messages to be aborted */ + while (1) { + reg_byte = mcp251xfd_read_crc(dev, MCP251XFD_REG_CON_B3, 1); + + if (!reg_byte || + (*reg_byte & MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(MCP251XFD_REG_CON_ABAT)) == 0) { + break; + } + } + + mcp251xfd_reset_tx_fifos(dev, -ENETDOWN); + + ret = mcp251xfd_set_mode_internal(dev, MCP251XFD_REG_CON_MODE_CONFIG); + if (ret < 0) { + k_mutex_unlock(&dev_data->mutex); + return ret; + } + + dev_data->started = false; + k_mutex_unlock(&dev_data->mutex); + + if (dev_cfg->phy != NULL) { + ret = can_transceiver_disable(dev_cfg->phy); + if (ret < 0) { + LOG_ERR("Failed to disable CAN transceiver [%d]", ret); + return ret; + } + } + + return 0; +} + +static void mcp251xfd_rx_fifo_handler(const struct device *dev, void *data) +{ + struct can_frame dst; + struct mcp251xfd_data *dev_data = dev->data; + struct mcp251xfd_rxobj *rxobj = data; + uint32_t filhit; + + mcp251xfd_rxobj_to_canframe(rxobj, &dst); + + filhit = FIELD_GET(MCP251XFD_OBJ_FILHIT_MASK, rxobj->flags); + if ((dev_data->filter_usage & BIT(filhit)) != 0) { + LOG_DBG("Received msg CAN id: 0x%x", dst.id); + dev_data->rx_cb[filhit](dev, &dst, dev_data->cb_arg[filhit]); + } +} + +static void mcp251xfd_tef_fifo_handler(const struct device *dev, void *data) +{ + struct mcp251xfd_data *dev_data = dev->data; + can_tx_callback_t callback; + struct mcp251xfd_tefobj *tefobj = data; + uint8_t mailbox_idx; + + mailbox_idx = FIELD_GET(MCP251XFD_OBJ_FLAGS_SEQ_MASK, tefobj->flags); + if (mailbox_idx >= MCP251XFD_TX_QUEUE_ITEMS) { + mcp251xfd_reset_tx_fifos(dev, -EIO); + LOG_ERR("Invalid mailbox index"); + return; + } + + callback = dev_data->mailbox[mailbox_idx].cb; + if (callback != NULL) { + callback(dev, 0, dev_data->mailbox[mailbox_idx].cb_arg); + } + + dev_data->mailbox_usage &= ~BIT(mailbox_idx); + dev_data->mailbox[mailbox_idx].cb = NULL; + k_sem_give(&dev_data->tx_sem); +} + +static int mcp251xfd_init_timing_struct(struct can_timing *timing, + const struct device *dev, + const struct mcp251xfd_timing_params *timing_params, + bool is_nominal) +{ + int ret; + + if (USE_SP_ALGO && timing_params->sample_point > 0) { + if (is_nominal) { + ret = can_calc_timing(dev, timing, timing_params->bus_speed, + timing_params->sample_point); + } else { + ret = can_calc_timing_data(dev, timing, timing_params->bus_speed, + timing_params->sample_point); + } + if (ret < 0) { + return ret; + } + LOG_DBG("Presc: %d, BS1: %d, BS2: %d", timing->prescaler, timing->phase_seg1, + timing->phase_seg2); + LOG_DBG("Sample-point err : %d", ret); + } else { + timing->sjw = timing_params->sjw; + timing->prop_seg = timing_params->prop_seg; + timing->phase_seg1 = timing_params->phase_seg1; + timing->phase_seg2 = timing_params->phase_seg2; + ret = can_calc_prescaler(dev, timing, timing_params->bus_speed); + if (ret > 0) { + LOG_WRN("Bitrate error: %d", ret); + } + } + + return ret; +} + +static inline int mcp251xfd_init_con_reg(const struct device *dev) +{ + uint32_t *reg; + + reg = mcp251xfd_get_spi_buf_ptr(dev); + *reg = MCP251XFD_REG_CON_ISOCRCEN | MCP251XFD_REG_CON_WAKFIL | MCP251XFD_REG_CON_TXQEN | + MCP251XFD_REG_CON_STEF; + *reg |= FIELD_PREP(MCP251XFD_REG_CON_WFT_MASK, MCP251XFD_REG_CON_WFT_T11FILTER) | + FIELD_PREP(MCP251XFD_REG_CON_REQOP_MASK, MCP251XFD_REG_CON_MODE_CONFIG); + + return mcp251xfd_write(dev, MCP251XFD_REG_CON, MCP251XFD_REG_SIZE); +} + +static inline int mcp251xfd_init_osc_reg(const struct device *dev) +{ + int ret; + const struct mcp251xfd_config *dev_cfg = dev->config; + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + uint32_t reg_value = MCP251XFD_REG_OSC_OSCRDY; + + *reg = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, dev_cfg->clko_div); + if (dev_cfg->pll_enable) { + *reg |= MCP251XFD_REG_OSC_PLLEN; + reg_value |= MCP251XFD_REG_OSC_PLLRDY; + } + + *reg = sys_cpu_to_le32(*reg); + + ret = mcp251xfd_write(dev, MCP251XFD_REG_OSC, MCP251XFD_REG_SIZE); + if (ret < 0) { + return ret; + } + + return mcp251xfd_reg_check_value_wtimeout(dev, MCP251XFD_REG_OSC, reg_value, reg_value, + MCP251XFD_PLLRDY_TIMEOUT_USEC, + MCP251XFD_PLLRDY_RETRIES, false); +} + +static inline int mcp251xfd_init_iocon_reg(const struct device *dev) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + +/* + * MCP2518FD Errata: DS80000789 + * Writing Byte 2/3 of the IOCON register using single SPI write cleat LAT0 and LAT1. + * This has no effect in the current version since LAT0/1 are set to zero anyway. + * However, it needs to be properly handled if other values are needed. Errata suggests + * to do single byte writes instead. + */ + + *reg = MCP251XFD_REG_IOCON_TRIS0 | MCP251XFD_REG_IOCON_TRIS1 | MCP251XFD_REG_IOCON_PM0 | + MCP251XFD_REG_IOCON_PM1; + + if (dev_cfg->sof_on_clko) { + *reg |= MCP251XFD_REG_IOCON_SOF; + } + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_IOCON, MCP251XFD_REG_SIZE); +} + +static inline int mcp251xfd_init_int_reg(const struct device *dev) +{ + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + + *reg = MCP251XFD_REG_INT_RXIE | MCP251XFD_REG_INT_MODIE | MCP251XFD_REG_INT_TEFIE | + MCP251XFD_REG_INT_CERRIE; + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_INT, MCP251XFD_REG_SIZE); +} + +static inline int mcp251xfd_init_tef_fifo(const struct device *dev) +{ + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + + *reg = MCP251XFD_REG_TEFCON_TEFNEIE | MCP251XFD_REG_TEFCON_FRESET; + *reg |= FIELD_PREP(MCP251XFD_REG_TEFCON_FSIZE_MASK, MCP251XFD_TX_QUEUE_ITEMS - 1); + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_TEFCON, MCP251XFD_REG_SIZE); +} + +static inline int mcp251xfd_init_tx_queue(const struct device *dev) +{ + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + + *reg = MCP251XFD_REG_TXQCON_TXEN | MCP251XFD_REG_TXQCON_FRESET; + *reg |= FIELD_PREP(MCP251XFD_REG_TXQCON_TXAT_MASK, MCP251XFD_REG_TXQCON_TXAT_UNLIMITED); + *reg |= FIELD_PREP(MCP251XFD_REG_TXQCON_FSIZE_MASK, MCP251XFD_TX_QUEUE_ITEMS - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_TXQCON_PLSIZE_MASK, + can_bytes_to_dlc(MCP251XFD_PAYLOAD_SIZE) - 8); + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_TXQCON, MCP251XFD_REG_SIZE); +} + +static inline int mcp251xfd_init_rx_fifo(const struct device *dev) +{ + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + + *reg = MCP251XFD_REG_FIFOCON_TFNRFNIE | MCP251XFD_REG_FIFOCON_FRESET; + *reg |= FIELD_PREP(MCP251XFD_REG_FIFOCON_FSIZE_MASK, MCP251XFD_RX_FIFO_ITEMS - 1); + *reg |= FIELD_PREP(MCP251XFD_REG_FIFOCON_PLSIZE_MASK, + can_bytes_to_dlc(MCP251XFD_PAYLOAD_SIZE) - 8); +#if defined(CONFIG_CAN_RX_TIMESTAMP) + *reg |= MCP251XFD_REG_FIFOCON_RXTSEN; +#endif + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_FIFOCON(MCP251XFD_RX_FIFO_IDX), + MCP251XFD_REG_SIZE); +} + +#if defined(CONFIG_CAN_RX_TIMESTAMP) +static int mcp251xfd_init_tscon(const struct device *dev) +{ + uint32_t *reg = mcp251xfd_get_spi_buf_ptr(dev); + const struct mcp251xfd_config *dev_cfg = dev->config; + + *reg = MCP251XFD_REG_TSCON_TBCEN; + *reg |= FIELD_PREP(MCP251XFD_REG_TSCON_TBCPRE_MASK, + dev_cfg->timestamp_prescaler - 1); + + *reg = sys_cpu_to_le32(*reg); + + return mcp251xfd_write(dev, MCP251XFD_REG_TSCON, MCP251XFD_REG_SIZE); +} +#endif + +static int mcp251xfd_reset(const struct device *dev) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + uint16_t cmd = sys_cpu_to_be16(MCP251XFD_SPI_INSTRUCTION_RESET); + const struct spi_buf tx_buf = {.buf = &cmd, .len = sizeof(cmd),}; + const struct spi_buf_set tx = {.buffers = &tx_buf, .count = 1}; + int ret; + + /* device can only be reset when in configuration mode */ + ret = mcp251xfd_set_mode_internal(dev, MCP251XFD_REG_CON_MODE_CONFIG); + if (ret < 0) { + return ret; + } + + return spi_write_dt(&dev_cfg->bus, &tx); +} + +static int mcp251xfd_init(const struct device *dev) +{ + const struct mcp251xfd_config *dev_cfg = dev->config; + struct mcp251xfd_data *dev_data = dev->data; + uint32_t *reg; + uint8_t opmod; + int ret; + struct can_timing timing = { 0 }; +#if defined(CONFIG_CAN_FD_MODE) + struct can_timing timing_data = { 0 }; +#endif + + dev_data->dev = dev; + + if (dev_cfg->clk_dev != NULL) { + uint32_t clk_id = dev_cfg->clk_id; + + if (!device_is_ready(dev_cfg->clk_dev)) { + LOG_ERR("Clock controller not ready"); + return -ENODEV; + } + + ret = clock_control_on(dev_cfg->clk_dev, (clock_control_subsys_t)clk_id); + if (ret < 0) { + LOG_ERR("Failed to enable clock [%d]", ret); + return ret; + } + } + + k_sem_init(&dev_data->int_sem, 0, 1); + k_sem_init(&dev_data->tx_sem, MCP251XFD_TX_QUEUE_ITEMS, MCP251XFD_TX_QUEUE_ITEMS); + + k_mutex_init(&dev_data->mutex); + + if (!spi_is_ready_dt(&dev_cfg->bus)) { + LOG_ERR("SPI bus %s not ready", dev_cfg->bus.bus->name); + return -ENODEV; + } + + if (!gpio_is_ready_dt(&dev_cfg->int_gpio_dt)) { + LOG_ERR("GPIO port not ready"); + return -ENODEV; + } + + if (gpio_pin_configure_dt(&dev_cfg->int_gpio_dt, GPIO_INPUT) < 0) { + LOG_ERR("Unable to configure GPIO pin"); + return -EINVAL; + } + + gpio_init_callback(&dev_data->int_gpio_cb, mcp251xfd_int_gpio_callback, + BIT(dev_cfg->int_gpio_dt.pin)); + + if (gpio_add_callback_dt(&dev_cfg->int_gpio_dt, &dev_data->int_gpio_cb) < 0) { + return -EINVAL; + } + + if (gpio_pin_interrupt_configure_dt(&dev_cfg->int_gpio_dt, GPIO_INT_LEVEL_ACTIVE) < 0) { + return -EINVAL; + } + + k_thread_create(&dev_data->int_thread, dev_data->int_thread_stack, + CONFIG_CAN_MCP251XFD_INT_THREAD_STACK_SIZE, + (k_thread_entry_t)mcp251xfd_int_thread, (void *)dev, NULL, NULL, + K_PRIO_COOP(CONFIG_CAN_MCP251XFD_INT_THREAD_PRIO), 0, K_NO_WAIT); + + (void)k_thread_name_set(&dev_data->int_thread, "MCP251XFD interrupt thread"); + + ret = mcp251xfd_reset(dev); + if (ret < 0) { + LOG_ERR("Failed to reset the device [%d]", ret); + goto done; + } + + ret = mcp251xfd_init_timing_struct(&timing, dev, &dev_cfg->timing_params, true); + if (ret < 0) { + LOG_ERR("Can't find timing for given param"); + goto done; + } + +#if defined(CONFIG_CAN_FD_MODE) + ret = mcp251xfd_init_timing_struct(&timing_data, dev, &dev_cfg->timing_params_data, false); + if (ret < 0) { + LOG_ERR("Can't find timing for given param"); + goto done; + } +#endif + + reg = mcp251xfd_read_crc(dev, MCP251XFD_REG_CON, MCP251XFD_REG_SIZE); + if (!reg) { + ret = -EINVAL; + goto done; + } + + *reg = sys_le32_to_cpu(*reg); + + opmod = FIELD_GET(MCP251XFD_REG_CON_OPMOD_MASK, *reg); + + if (opmod != MCP251XFD_REG_CON_MODE_CONFIG) { + LOG_ERR("Device did not reset into configuration mode [%d]", opmod); + ret = -EIO; + goto done; + } + + dev_data->current_mcp251xfd_mode = MCP251XFD_REG_CON_MODE_CONFIG; + + ret = mcp251xfd_init_con_reg(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_init_osc_reg(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_init_iocon_reg(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_init_int_reg(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_set_tdc(dev, false, 0); + if (ret < 0) { + goto done; + } + +#if defined(CONFIG_CAN_RX_TIMESTAMP) + ret = mcp251xfd_init_tscon(dev); + if (ret < 0) { + goto done; + } +#endif + + ret = mcp251xfd_init_tef_fifo(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_init_tx_queue(dev); + if (ret < 0) { + goto done; + } + + ret = mcp251xfd_init_rx_fifo(dev); + if (ret < 0) { + goto done; + } + + LOG_DBG("%d TX FIFOS: 1 element", MCP251XFD_TX_QUEUE_ITEMS); + LOG_DBG("1 RX FIFO: %d elements", MCP251XFD_RX_FIFO_ITEMS); + LOG_DBG("%db of %db RAM Allocated", + MCP251XFD_TEF_FIFO_SIZE + MCP251XFD_TX_QUEUE_SIZE + MCP251XFD_RX_FIFO_SIZE, + MCP251XFD_RAM_SIZE); + +done: + ret = can_set_timing(dev, &timing); + if (ret < 0) { + return ret; + } + +#if defined(CONFIG_CAN_FD_MODE) + ret = can_set_timing_data(dev, &timing_data); + if (ret < 0) { + return ret; + } +#endif + + return ret; +} + +static const struct can_driver_api mcp251xfd_api_funcs = { + .get_capabilities = mcp251xfd_get_capabilities, + .set_mode = mcp251xfd_set_mode, + .set_timing = mcp251xfd_set_timing, +#if defined(CONFIG_CAN_FD_MODE) + .set_timing_data = mcp251xfd_set_timing_data, +#endif + .start = mcp251xfd_start, + .stop = mcp251xfd_stop, + .send = mcp251xfd_send, + .add_rx_filter = mcp251xfd_add_rx_filter, + .remove_rx_filter = mcp251xfd_remove_rx_filter, +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + .recover = mcp251xfd_recover, +#endif + .get_state = mcp251xfd_get_state, + .set_state_change_callback = mcp251xfd_set_state_change_callback, + .get_core_clock = mcp251xfd_get_core_clock, + .get_max_filters = mcp251xfd_get_max_filters, + .get_max_bitrate = mcp251xfd_get_max_bitrate, + .timing_min = { + .sjw = 1, + .prop_seg = 0, + .phase_seg1 = 2, + .phase_seg2 = 1, + .prescaler = 1, + }, + .timing_max = { + .sjw = 128, + .prop_seg = 0, + .phase_seg1 = 256, + .phase_seg2 = 128, + .prescaler = 256, + }, +#if defined(CONFIG_CAN_FD_MODE) + .timing_data_min = { + .sjw = 1, + .prop_seg = 0, + .phase_seg1 = 1, + .phase_seg2 = 1, + .prescaler = 1, + }, + .timing_data_max = { + .sjw = 16, + .prop_seg = 0, + .phase_seg1 = 32, + .phase_seg2 = 16, + .prescaler = 256, + }, +#endif +}; + +#define MCP251XFD_SET_TIMING_MACRO(inst, type) \ + .timing_params##type = { \ + .sjw = DT_INST_PROP(inst, sjw##type), \ + .prop_seg = DT_INST_PROP_OR(inst, prop_seg##type, 0), \ + .phase_seg1 = DT_INST_PROP_OR(inst, phase_seg1##type, 0), \ + .phase_seg2 = DT_INST_PROP_OR(inst, phase_seg2##type, 0), \ + .bus_speed = DT_INST_PROP(inst, bus_speed##type), \ + .sample_point = DT_INST_PROP_OR(inst, sample_point##type, 0), \ + } + +#if defined(CONFIG_CAN_FD_MODE) +#define MCP251XFD_SET_TIMING(inst) \ + MCP251XFD_SET_TIMING_MACRO(inst,), \ + MCP251XFD_SET_TIMING_MACRO(inst, _data) +#else +#define MCP251XFD_SET_TIMING(inst) \ + MCP251XFD_SET_TIMING_MACRO(inst,) +#endif + +#define MCP251XFD_SET_CLOCK(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clocks), \ + (.clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \ + .clk_id = DT_INST_CLOCKS_CELL(inst, id)), \ + ()) + +#define MCP251XFD_INIT(inst) \ + static K_KERNEL_STACK_DEFINE(mcp251xfd_int_stack_##inst, \ + CONFIG_CAN_MCP251XFD_INT_THREAD_STACK_SIZE); \ + \ + static struct mcp251xfd_data mcp251xfd_data_##inst = { \ + .int_thread_stack = mcp251xfd_int_stack_##inst, \ + }; \ + static const struct mcp251xfd_config mcp251xfd_config_##inst = { \ + .bus = SPI_DT_SPEC_INST_GET(inst, SPI_WORD_SET(8), 0), \ + .int_gpio_dt = GPIO_DT_SPEC_INST_GET(inst, int_gpios), \ + \ + .sof_on_clko = DT_INST_PROP(inst, sof_on_clko), \ + .clko_div = DT_INST_ENUM_IDX(inst, clko_div), \ + .pll_enable = DT_INST_PROP(inst, pll_enable), \ + .timestamp_prescaler = DT_INST_PROP(inst, timestamp_prescaler), \ + \ + .osc_freq = DT_INST_PROP(inst, osc_freq), \ + MCP251XFD_SET_TIMING(inst), \ + .phy = DEVICE_DT_GET_OR_NULL(DT_INST_PHANDLE(inst, phys)), \ + .max_bitrate = DT_INST_CAN_TRANSCEIVER_MAX_BITRATE(inst, 8000000), \ + .rx_fifo = {.ram_start_addr = MCP251XFD_RX_FIFO_START_ADDR, \ + .reg_fifocon_addr = MCP251XFD_REG_FIFOCON(MCP251XFD_RX_FIFO_IDX), \ + .capacity = MCP251XFD_RX_FIFO_ITEMS, \ + .item_size = MCP251XFD_RX_FIFO_ITEM_SIZE, \ + .msg_handler = mcp251xfd_rx_fifo_handler}, \ + .tef_fifo = {.ram_start_addr = MCP251XFD_TEF_FIFO_START_ADDR, \ + .reg_fifocon_addr = MCP251XFD_REG_TEFCON, \ + .capacity = MCP251XFD_TEF_FIFO_ITEMS, \ + .item_size = MCP251XFD_TEF_FIFO_ITEM_SIZE, \ + .msg_handler = mcp251xfd_tef_fifo_handler}, \ + MCP251XFD_SET_CLOCK(inst) \ + }; \ + \ + CAN_DEVICE_DT_INST_DEFINE(inst, &mcp251xfd_init, NULL, &mcp251xfd_data_##inst, \ + &mcp251xfd_config_##inst, POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \ + &mcp251xfd_api_funcs); + +DT_INST_FOREACH_STATUS_OKAY(MCP251XFD_INIT) diff --git a/drivers/can/can_mcp251xfd.h b/drivers/can/can_mcp251xfd.h new file mode 100644 index 00000000000..f0414103bed --- /dev/null +++ b/drivers/can/can_mcp251xfd.h @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2020 Abram Early + * Copyright (c) 2023 Andriy Gelman + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP251XFD_H_ +#define ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP251XFD_H_ + +#include + +#include +#include +#include + +#define MCP251XFD_UINT32_FLAG_TO_BYTE_MASK(flag_u32) \ + ((flag_u32) >> ROUND_DOWN(LOG2((flag_u32)), 8)) + +#define MCP251XFD_RAM_START_ADDR 0x400 +#define MCP251XFD_RAM_SIZE 2048 +#define MCP251XFD_RAM_ALIGNMENT 4 +#define MCP251XFD_PAYLOAD_SIZE CAN_MAX_DLEN + +#define MCP251XFD_FIFO_TYPE_TEF 0 +#define MCP251XFD_FIFO_TYPE_RX 1 + +#define MCP251XFD_TEF_FIFO_ITEM_SIZE 8 +#define MCP251XFD_TX_QUEUE_ITEM_SIZE (8 + MCP251XFD_PAYLOAD_SIZE) + +#if defined(CONFIG_CAN_RX_TIMESTAMP) +#define MCP251XFD_RX_FIFO_ITEM_SIZE (4 + 8 + MCP251XFD_PAYLOAD_SIZE) +#else +#define MCP251XFD_RX_FIFO_ITEM_SIZE (8 + MCP251XFD_PAYLOAD_SIZE) +#endif + +#define MCP251XFD_TEF_FIFO_START_ADDR 0 +#define MCP251XFD_TEF_FIFO_ITEMS CONFIG_CAN_MCP251XFD_MAX_TX_QUEUE +#define MCP251XFD_TEF_FIFO_SIZE (MCP251XFD_TEF_FIFO_ITEMS * MCP251XFD_TEF_FIFO_ITEM_SIZE) + +#define MCP251XFD_TX_QUEUE_START_ADDR MCP251XFD_TEF_FIFO_SIZE +#define MCP251XFD_TX_QUEUE_ITEMS CONFIG_CAN_MCP251XFD_MAX_TX_QUEUE +#define MCP251XFD_TX_QUEUE_SIZE (MCP251XFD_TX_QUEUE_ITEMS * MCP251XFD_TX_QUEUE_ITEM_SIZE) + +#define MCP251XFD_RX_FIFO_START_ADDR (MCP251XFD_TX_QUEUE_START_ADDR + MCP251XFD_TX_QUEUE_SIZE) +#define MCP251XFD_RX_FIFO_SIZE_MAX (MCP251XFD_RAM_SIZE - MCP251XFD_RX_FIFO_START_ADDR) +#define MCP251XFD_RX_FIFO_ITEMS_MAX (MCP251XFD_RX_FIFO_SIZE_MAX / MCP251XFD_RX_FIFO_ITEM_SIZE) + +#define MCP251XFD_RX_FIFO_ITEMS CONFIG_CAN_MCP251XFD_RX_FIFO_ITEMS +#define MCP251XFD_RX_FIFO_SIZE (MCP251XFD_RX_FIFO_ITEMS * MCP251XFD_RX_FIFO_ITEM_SIZE) + +#define MCP251XFD_RX_FIFO_IDX 1 +#define MCP251XFD_REG_SIZE 4 + +#define MCP251XFD_CRC_POLY 0x8005 +#define MCP251XFD_CRC_SEED 0xffff + +BUILD_ASSERT(MCP251XFD_TEF_FIFO_SIZE + MCP251XFD_TX_QUEUE_SIZE + + MCP251XFD_RX_FIFO_SIZE <= MCP251XFD_RAM_SIZE, + "Cannot fit FIFOs into RAM"); + +/* Timeout for changing mode */ +#define MCP251XFD_MODE_CHANGE_TIMEOUT_USEC 200000U +#define MCP251XFD_MODE_CHANGE_RETRIES 100 + +#define MCP251XFD_PLLRDY_TIMEOUT_USEC 100000 +#define MCP251XFD_PLLRDY_RETRIES 100 + +#define MCP251XFD_MAX_INT_HANDLER_CALLS 10 +#define MCP251XFD_INT_HANDLER_SLEEP_USEC 10000 + + +struct mcp251xfd_mailbox { + can_tx_callback_t cb; + void *cb_arg; +}; + +#define MCP251XFD_SPI_CMD_LEN 2 +#define MCP251XFD_SPI_LEN_FIELD_LEN 1 +#define MCP251XFD_SPI_CRC_LEN 2 + +/* MPC251x registers - mostly copied from Linux kernel implementation of driver */ + +/* CAN FD Controller Module SFR */ +#define MCP251XFD_REG_CON 0x00 +#define MCP251XFD_REG_CON_TXBWS_MASK GENMASK(31, 28) +#define MCP251XFD_REG_CON_ABAT BIT(27) +#define MCP251XFD_REG_CON_REQOP_MASK GENMASK(26, 24) +#define MCP251XFD_REG_CON_MODE_MIXED 0 +#define MCP251XFD_REG_CON_MODE_SLEEP 1 +#define MCP251XFD_REG_CON_MODE_INT_LOOPBACK 2 +#define MCP251XFD_REG_CON_MODE_LISTENONLY 3 +#define MCP251XFD_REG_CON_MODE_CONFIG 4 +#define MCP251XFD_REG_CON_MODE_EXT_LOOPBACK 5 +#define MCP251XFD_REG_CON_MODE_CAN2_0 6 +#define MCP251XFD_REG_CON_MODE_RESTRICTED 7 +#define MCP251XFD_REG_CON_OPMOD_MASK GENMASK(23, 21) +#define MCP251XFD_REG_CON_TXQEN BIT(20) +#define MCP251XFD_REG_CON_STEF BIT(19) +#define MCP251XFD_REG_CON_SERR2LOM BIT(18) +#define MCP251XFD_REG_CON_ESIGM BIT(17) +#define MCP251XFD_REG_CON_RTXAT BIT(16) +#define MCP251XFD_REG_CON_BRSDIS BIT(12) +#define MCP251XFD_REG_CON_BUSY BIT(11) +#define MCP251XFD_REG_CON_WFT_MASK GENMASK(10, 9) +#define MCP251XFD_REG_CON_WFT_T00FILTER 0x0 +#define MCP251XFD_REG_CON_WFT_T01FILTER 0x1 +#define MCP251XFD_REG_CON_WFT_T10FILTER 0x2 +#define MCP251XFD_REG_CON_WFT_T11FILTER 0x3 +#define MCP251XFD_REG_CON_WAKFIL BIT(8) +#define MCP251XFD_REG_CON_PXEDIS BIT(6) +#define MCP251XFD_REG_CON_ISOCRCEN BIT(5) +#define MCP251XFD_REG_CON_DNCNT_MASK GENMASK(4, 0) + +#define MCP251XFD_REG_CON_B2 (MCP251XFD_REG_CON + 2) +#define MCP251XFD_REG_CON_B3 (MCP251XFD_REG_CON + 3) + +#define MCP251XFD_REG_NBTCFG 0x04 +#define MCP251XFD_REG_NBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_NBTCFG_TSEG1_MASK GENMASK(23, 16) +#define MCP251XFD_REG_NBTCFG_TSEG2_MASK GENMASK(14, 8) +#define MCP251XFD_REG_NBTCFG_SJW_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_DBTCFG 0x08 +#define MCP251XFD_REG_DBTCFG_BRP_MASK GENMASK(31, 24) +#define MCP251XFD_REG_DBTCFG_TSEG1_MASK GENMASK(20, 16) +#define MCP251XFD_REG_DBTCFG_TSEG2_MASK GENMASK(11, 8) +#define MCP251XFD_REG_DBTCFG_SJW_MASK GENMASK(3, 0) + +#define MCP251XFD_REG_TDC 0x0c +#define MCP251XFD_REG_TDC_EDGFLTEN BIT(25) +#define MCP251XFD_REG_TDC_SID11EN BIT(24) +#define MCP251XFD_REG_TDC_TDCMOD_MASK GENMASK(17, 16) +#define MCP251XFD_REG_TDC_TDCMOD_AUTO 2 +#define MCP251XFD_REG_TDC_TDCMOD_MANUAL 1 +#define MCP251XFD_REG_TDC_TDCMOD_DISABLED 0 +#define MCP251XFD_REG_TDC_TDCO_MASK GENMASK(14, 8) +#define MCP251XFD_REG_TDC_TDCV_MASK GENMASK(5, 0) +#define MCP251XFD_REG_TDC_TDCO_MIN -64 +#define MCP251XFD_REG_TDC_TDCO_MAX 63 + +#define MCP251XFD_REG_TBC 0x10 + +#define MCP251XFD_REG_TSCON 0x14 +#define MCP251XFD_REG_TSCON_TSRES BIT(18) +#define MCP251XFD_REG_TSCON_TSEOF BIT(17) +#define MCP251XFD_REG_TSCON_TBCEN BIT(16) +#define MCP251XFD_REG_TSCON_TBCPRE_MASK GENMASK(9, 0) + +#define MCP251XFD_REG_VEC 0x18 +#define MCP251XFD_REG_VEC_RXCODE_MASK GENMASK(30, 24) +#define MCP251XFD_REG_VEC_TXCODE_MASK GENMASK(22, 16) +#define MCP251XFD_REG_VEC_FILHIT_MASK GENMASK(12, 8) +#define MCP251XFD_REG_VEC_ICODE_MASK GENMASK(6, 0) + +#define MCP251XFD_REG_INT 0x1c +#define MCP251XFD_REG_INT_IF_MASK GENMASK(15, 0) +#define MCP251XFD_REG_INT_IE_MASK GENMASK(31, 16) +#define MCP251XFD_REG_INT_IVMIE BIT(31) +#define MCP251XFD_REG_INT_WAKIE BIT(30) +#define MCP251XFD_REG_INT_CERRIE BIT(29) +#define MCP251XFD_REG_INT_SERRIE BIT(28) +#define MCP251XFD_REG_INT_RXOVIE BIT(27) +#define MCP251XFD_REG_INT_TXATIE BIT(26) +#define MCP251XFD_REG_INT_SPICRCIE BIT(25) +#define MCP251XFD_REG_INT_ECCIE BIT(24) +#define MCP251XFD_REG_INT_TEFIE BIT(20) +#define MCP251XFD_REG_INT_MODIE BIT(19) +#define MCP251XFD_REG_INT_TBCIE BIT(18) +#define MCP251XFD_REG_INT_RXIE BIT(17) +#define MCP251XFD_REG_INT_TXIE BIT(16) +#define MCP251XFD_REG_INT_IVMIF BIT(15) +#define MCP251XFD_REG_INT_WAKIF BIT(14) +#define MCP251XFD_REG_INT_CERRIF BIT(13) +#define MCP251XFD_REG_INT_SERRIF BIT(12) +#define MCP251XFD_REG_INT_RXOVIF BIT(11) +#define MCP251XFD_REG_INT_TXATIF BIT(10) +#define MCP251XFD_REG_INT_SPICRCIF BIT(9) +#define MCP251XFD_REG_INT_ECCIF BIT(8) +#define MCP251XFD_REG_INT_TEFIF BIT(4) +#define MCP251XFD_REG_INT_MODIF BIT(3) +#define MCP251XFD_REG_INT_TBCIF BIT(2) +#define MCP251XFD_REG_INT_RXIF BIT(1) +#define MCP251XFD_REG_INT_TXIF BIT(0) + +/* These IRQ flags must be cleared by SW in the CAN_INT register */ +#define MCP251XFD_REG_INT_IF_CLEARABLE_MASK \ + (MCP251XFD_REG_INT_IVMIF | MCP251XFD_REG_INT_WAKIF | MCP251XFD_REG_INT_CERRIF | \ + MCP251XFD_REG_INT_SERRIF | MCP251XFD_REG_INT_MODIF) + +#define MCP251XFD_REG_RXIF 0x20 +#define MCP251XFD_REG_TXIF 0x24 +#define MCP251XFD_REG_RXOVIF 0x28 +#define MCP251XFD_REG_TXATIF 0x2c +#define MCP251XFD_REG_TXREQ 0x30 + +#define MCP251XFD_REG_TREC 0x34 +#define MCP251XFD_REG_TREC_TXBO BIT(21) +#define MCP251XFD_REG_TREC_TXBP BIT(20) +#define MCP251XFD_REG_TREC_RXBP BIT(19) +#define MCP251XFD_REG_TREC_TXWARN BIT(18) +#define MCP251XFD_REG_TREC_RXWARN BIT(17) +#define MCP251XFD_REG_TREC_EWARN BIT(16) +#define MCP251XFD_REG_TREC_TEC_MASK GENMASK(15, 8) +#define MCP251XFD_REG_TREC_REC_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG0 0x38 +#define MCP251XFD_REG_BDIAG0_DTERRCNT_MASK GENMASK(31, 24) +#define MCP251XFD_REG_BDIAG0_DRERRCNT_MASK GENMASK(23, 16) +#define MCP251XFD_REG_BDIAG0_NTERRCNT_MASK GENMASK(15, 8) +#define MCP251XFD_REG_BDIAG0_NRERRCNT_MASK GENMASK(7, 0) + +#define MCP251XFD_REG_BDIAG1 0x3c +#define MCP251XFD_REG_BDIAG1_DLCMM BIT(31) +#define MCP251XFD_REG_BDIAG1_ESI BIT(30) +#define MCP251XFD_REG_BDIAG1_DCRCERR BIT(29) +#define MCP251XFD_REG_BDIAG1_DSTUFERR BIT(28) +#define MCP251XFD_REG_BDIAG1_DFORMERR BIT(27) +#define MCP251XFD_REG_BDIAG1_DBIT1ERR BIT(25) +#define MCP251XFD_REG_BDIAG1_DBIT0ERR BIT(24) +#define MCP251XFD_REG_BDIAG1_TXBOERR BIT(23) +#define MCP251XFD_REG_BDIAG1_NCRCERR BIT(21) +#define MCP251XFD_REG_BDIAG1_NSTUFERR BIT(20) +#define MCP251XFD_REG_BDIAG1_NFORMERR BIT(19) +#define MCP251XFD_REG_BDIAG1_NACKERR BIT(18) +#define MCP251XFD_REG_BDIAG1_NBIT1ERR BIT(17) +#define MCP251XFD_REG_BDIAG1_NBIT0ERR BIT(16) +#define MCP251XFD_REG_BDIAG1_BERR_MASK \ + (MCP251XFD_REG_BDIAG1_DLCMM | MCP251XFD_REG_BDIAG1_ESI | MCP251XFD_REG_BDIAG1_DCRCERR | \ + MCP251XFD_REG_BDIAG1_DSTUFERR | MCP251XFD_REG_BDIAG1_DFORMERR | \ + MCP251XFD_REG_BDIAG1_DBIT1ERR | MCP251XFD_REG_BDIAG1_DBIT0ERR | \ + MCP251XFD_REG_BDIAG1_TXBOERR | MCP251XFD_REG_BDIAG1_NCRCERR | \ + MCP251XFD_REG_BDIAG1_NSTUFERR | MCP251XFD_REG_BDIAG1_NFORMERR | \ + MCP251XFD_REG_BDIAG1_NACKERR | MCP251XFD_REG_BDIAG1_NBIT1ERR | \ + MCP251XFD_REG_BDIAG1_NBIT0ERR) +#define MCP251XFD_REG_BDIAG1_EFMSGCNT_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_TEFCON 0x40 +#define MCP251XFD_REG_TEFCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TEFCON_FRESET BIT(10) +#define MCP251XFD_REG_TEFCON_UINC BIT(8) +#define MCP251XFD_REG_TEFCON_TEFTSEN BIT(5) +#define MCP251XFD_REG_TEFCON_TEFOVIE BIT(3) +#define MCP251XFD_REG_TEFCON_TEFFIE BIT(2) +#define MCP251XFD_REG_TEFCON_TEFHIE BIT(1) +#define MCP251XFD_REG_TEFCON_TEFNEIE BIT(0) + +#define MCP251XFD_REG_TEFSTA 0x44 +#define MCP251XFD_REG_TEFSTA_TEFOVIF BIT(3) +#define MCP251XFD_REG_TEFSTA_TEFFIF BIT(2) +#define MCP251XFD_REG_TEFSTA_TEFHIF BIT(1) +#define MCP251XFD_REG_TEFSTA_TEFNEIF BIT(0) + +#define MCP251XFD_REG_TEFUA 0x48 + +#define MCP251XFD_REG_TXQCON 0x50 +#define MCP251XFD_REG_TXQCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_TXQCON_PLSIZE_8 0 +#define MCP251XFD_REG_TXQCON_PLSIZE_12 1 +#define MCP251XFD_REG_TXQCON_PLSIZE_16 2 +#define MCP251XFD_REG_TXQCON_PLSIZE_20 3 +#define MCP251XFD_REG_TXQCON_PLSIZE_24 4 +#define MCP251XFD_REG_TXQCON_PLSIZE_32 5 +#define MCP251XFD_REG_TXQCON_PLSIZE_48 6 +#define MCP251XFD_REG_TXQCON_PLSIZE_64 7 +#define MCP251XFD_REG_TXQCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_TXQCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_TXQCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_TXQCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_TXQCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_TXQCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_TXQCON_FRESET BIT(10) +#define MCP251XFD_REG_TXQCON_TXREQ BIT(9) +#define MCP251XFD_REG_TXQCON_UINC BIT(8) +#define MCP251XFD_REG_TXQCON_TXEN BIT(7) +#define MCP251XFD_REG_TXQCON_TXATIE BIT(4) +#define MCP251XFD_REG_TXQCON_TXQEIE BIT(2) +#define MCP251XFD_REG_TXQCON_TXQNIE BIT(0) + +#define MCP251XFD_REG_TXQSTA 0x54 +#define MCP251XFD_REG_TXQSTA_TXQCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_TXQSTA_TXABT BIT(7) +#define MCP251XFD_REG_TXQSTA_TXLARB BIT(6) +#define MCP251XFD_REG_TXQSTA_TXERR BIT(5) +#define MCP251XFD_REG_TXQSTA_TXATIF BIT(4) +#define MCP251XFD_REG_TXQSTA_TXQEIF BIT(2) +#define MCP251XFD_REG_TXQSTA_TXQNIF BIT(0) + +#define MCP251XFD_REG_TXQUA 0x58 + +#define MCP251XFD_REG_FIFOCON(x) (0x50 + 0xc * (x)) +#define MCP251XFD_REG_FIFOCON_PLSIZE_MASK GENMASK(31, 29) +#define MCP251XFD_REG_FIFOCON_PLSIZE_8 0 +#define MCP251XFD_REG_FIFOCON_PLSIZE_12 1 +#define MCP251XFD_REG_FIFOCON_PLSIZE_16 2 +#define MCP251XFD_REG_FIFOCON_PLSIZE_20 3 +#define MCP251XFD_REG_FIFOCON_PLSIZE_24 4 +#define MCP251XFD_REG_FIFOCON_PLSIZE_32 5 +#define MCP251XFD_REG_FIFOCON_PLSIZE_48 6 +#define MCP251XFD_REG_FIFOCON_PLSIZE_64 7 +#define MCP251XFD_REG_FIFOCON_FSIZE_MASK GENMASK(28, 24) +#define MCP251XFD_REG_FIFOCON_TXAT_MASK GENMASK(22, 21) +#define MCP251XFD_REG_FIFOCON_TXAT_ONE_SHOT 0 +#define MCP251XFD_REG_FIFOCON_TXAT_THREE_SHOT 1 +#define MCP251XFD_REG_FIFOCON_TXAT_UNLIMITED 3 +#define MCP251XFD_REG_FIFOCON_TXPRI_MASK GENMASK(20, 16) +#define MCP251XFD_REG_FIFOCON_FRESET BIT(10) +#define MCP251XFD_REG_FIFOCON_TXREQ BIT(9) +#define MCP251XFD_REG_FIFOCON_UINC BIT(8) +#define MCP251XFD_REG_FIFOCON_TXEN BIT(7) +#define MCP251XFD_REG_FIFOCON_RTREN BIT(6) +#define MCP251XFD_REG_FIFOCON_RXTSEN BIT(5) +#define MCP251XFD_REG_FIFOCON_TXATIE BIT(4) +#define MCP251XFD_REG_FIFOCON_RXOVIE BIT(3) +#define MCP251XFD_REG_FIFOCON_TFERFFIE BIT(2) +#define MCP251XFD_REG_FIFOCON_TFHRFHIE BIT(1) +#define MCP251XFD_REG_FIFOCON_TFNRFNIE BIT(0) + +#define MCP251XFD_REG_FIFOSTA(x) (0x54 + 0xc * (x)) +#define MCP251XFD_REG_FIFOSTA_FIFOCI_MASK GENMASK(12, 8) +#define MCP251XFD_REG_FIFOSTA_TXABT BIT(7) +#define MCP251XFD_REG_FIFOSTA_TXLARB BIT(6) +#define MCP251XFD_REG_FIFOSTA_TXERR BIT(5) +#define MCP251XFD_REG_FIFOSTA_TXATIF BIT(4) +#define MCP251XFD_REG_FIFOSTA_RXOVIF BIT(3) +#define MCP251XFD_REG_FIFOSTA_TFERFFIF BIT(2) +#define MCP251XFD_REG_FIFOSTA_TFHRFHIF BIT(1) +#define MCP251XFD_REG_FIFOSTA_TFNRFNIF BIT(0) + +#define MCP251XFD_REG_FIFOUA(x) (0x58 + 0xc * (x)) + +#define MCP251XFD_REG_BYTE_FLTCON(m) (0x1d0 + m) +#define MCP251XFD_REG_BYTE_FLTCON_FBP_MASK GENMASK(4, 0) +#define MCP251XFD_REG_BYTE_FLTCON_FLTEN BIT(7) + +#define MCP251XFD_REG_FLTOBJ(x) (0x1f0 + 0x8 * (x)) +#define MCP251XFD_REG_FLTOBJ_EXIDE BIT(30) +#define MCP251XFD_REG_FLTOBJ_SID11 BIT(29) +#define MCP251XFD_REG_FLTOBJ_EID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_FLTOBJ_SID_MASK GENMASK(10, 0) + +#define MCP251XFD_REG_FLTMASK(x) (0x1f4 + 0x8 * (x)) +#define MCP251XFD_REG_MASK_MIDE BIT(30) +#define MCP251XFD_REG_MASK_MSID11 BIT(29) +#define MCP251XFD_REG_MASK_MEID_MASK GENMASK(28, 11) +#define MCP251XFD_REG_MASK_MSID_MASK GENMASK(10, 0) + +/* Message Object */ +#define MCP251XFD_OBJ_ID_SID11 BIT(29) +#define MCP251XFD_OBJ_ID_EID_MASK GENMASK(28, 11) +#define MCP251XFD_OBJ_ID_SID_MASK GENMASK(10, 0) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK GENMASK(31, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK GENMASK(15, 9) +#define MCP251XFD_OBJ_FLAGS_SEQ_MASK MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK +#define MCP251XFD_OBJ_FLAGS_ESI BIT(8) +#define MCP251XFD_OBJ_FLAGS_FDF BIT(7) +#define MCP251XFD_OBJ_FLAGS_BRS BIT(6) +#define MCP251XFD_OBJ_FLAGS_RTR BIT(5) +#define MCP251XFD_OBJ_FLAGS_IDE BIT(4) +#define MCP251XFD_OBJ_FLAGS_DLC_MASK GENMASK(3, 0) +#define MCP251XFD_OBJ_FILHIT_MASK GENMASK(15, 11) + +#define MCP251XFD_OBJ_DATA_OFFSET 2 /* offset to the data in sizeof(uint32_t) */ +#define MCP251XFD_OBJ_HEADER_SIZE (MCP251XFD_OBJ_DATA_OFFSET * MCP251XFD_REG_SIZE) + +#define MCP251XFD_REG_FRAME_EFF_SID_MASK GENMASK(28, 18) +#define MCP251XFD_REG_FRAME_EFF_EID_MASK GENMASK(17, 0) + +/* MCP2517/18FD SFR */ +#define MCP251XFD_REG_OSC 0xe00 +#define MCP251XFD_REG_OSC_SCLKRDY BIT(12) +#define MCP251XFD_REG_OSC_OSCRDY BIT(10) +#define MCP251XFD_REG_OSC_PLLRDY BIT(8) +#define MCP251XFD_REG_OSC_CLKODIV_10 3 +#define MCP251XFD_REG_OSC_CLKODIV_4 2 +#define MCP251XFD_REG_OSC_CLKODIV_2 1 +#define MCP251XFD_REG_OSC_CLKODIV_1 0 +#define MCP251XFD_REG_OSC_CLKODIV_MASK GENMASK(6, 5) +#define MCP251XFD_REG_OSC_SCLKDIV BIT(4) +#define MCP251XFD_REG_OSC_LPMEN BIT(3) /* MCP2518FD only */ +#define MCP251XFD_REG_OSC_OSCDIS BIT(2) +#define MCP251XFD_REG_OSC_PLLEN BIT(0) + +#define MCP251XFD_REG_IOCON 0xe04 +#define MCP251XFD_REG_IOCON_INTOD BIT(30) +#define MCP251XFD_REG_IOCON_SOF BIT(29) +#define MCP251XFD_REG_IOCON_TXCANOD BIT(28) +#define MCP251XFD_REG_IOCON_PM1 BIT(25) +#define MCP251XFD_REG_IOCON_PM0 BIT(24) +#define MCP251XFD_REG_IOCON_GPIO1 BIT(17) +#define MCP251XFD_REG_IOCON_GPIO0 BIT(16) +#define MCP251XFD_REG_IOCON_LAT1 BIT(9) +#define MCP251XFD_REG_IOCON_LAT0 BIT(8) +#define MCP251XFD_REG_IOCON_XSTBYEN BIT(6) +#define MCP251XFD_REG_IOCON_TRIS1 BIT(1) +#define MCP251XFD_REG_IOCON_TRIS0 BIT(0) + +#define MCP251XFD_REG_CRC 0xe08 +#define MCP251XFD_REG_CRC_FERRIE BIT(25) +#define MCP251XFD_REG_CRC_CRCERRIE BIT(24) +#define MCP251XFD_REG_CRC_FERRIF BIT(17) +#define MCP251XFD_REG_CRC_CRCERRIF BIT(16) +#define MCP251XFD_REG_CRC_IF_MASK GENMASK(17, 16) +#define MCP251XFD_REG_CRC_MASK GENMASK(15, 0) + +#define MCP251XFD_REG_ECCCON 0xe0c +#define MCP251XFD_REG_ECCCON_PARITY_MASK GENMASK(14, 8) +#define MCP251XFD_REG_ECCCON_DEDIE BIT(2) +#define MCP251XFD_REG_ECCCON_SECIE BIT(1) +#define MCP251XFD_REG_ECCCON_ECCEN BIT(0) + +#define MCP251XFD_REG_ECCSTAT 0xe10 +#define MCP251XFD_REG_ECCSTAT_ERRADDR_MASK GENMASK(27, 16) +#define MCP251XFD_REG_ECCSTAT_IF_MASK GENMASK(2, 1) +#define MCP251XFD_REG_ECCSTAT_DEDIF BIT(2) +#define MCP251XFD_REG_ECCSTAT_SECIF BIT(1) + +#define MCP251XFD_REG_DEVID 0xe14 /* MCP2518FD only */ +#define MCP251XFD_REG_DEVID_ID_MASK GENMASK(7, 4) +#define MCP251XFD_REG_DEVID_REV_MASK GENMASK(3, 0) + +/* SPI commands */ +#define MCP251XFD_SPI_INSTRUCTION_RESET 0x0000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE 0x2000 +#define MCP251XFD_SPI_INSTRUCTION_READ 0x3000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE_CRC 0xa000 +#define MCP251XFD_SPI_INSTRUCTION_READ_CRC 0xb000 +#define MCP251XFD_SPI_INSTRUCTION_WRITE_CRC_SAFE 0xc000 +#define MCP251XFD_SPI_ADDRESS_MASK GENMASK(11, 0) + +#define MCP251XFD_REG_FIFOCON_TO_STA(addr) (addr + 0x4) + +#define MCP251XFD_REG_FLTCON(m) (0x1d0 + m) + +struct mcp251xfd_txobj { + uint32_t id; + uint32_t flags; + uint8_t data[CAN_MAX_DLEN]; +} __packed; + +struct mcp251xfd_rxobj { + uint32_t id; + uint32_t flags; +#if defined(CONFIG_CAN_RX_TIMESTAMP) + uint32_t timestamp; +#endif + uint8_t data[CAN_MAX_DLEN]; +} __packed; + +struct mcp251xfd_tefobj { + uint32_t id; + uint32_t flags; +} __packed; + +#define MCP251XFD_MAX_READ_FIFO_BUF_SIZE \ + MAX((MCP251XFD_RX_FIFO_ITEM_SIZE * MCP251XFD_RX_FIFO_ITEMS), \ + (MCP251XFD_TEF_FIFO_ITEM_SIZE * MCP251XFD_TEF_FIFO_ITEMS)) + +#define MCP251XFD_MAX_READ_CRC_BUF_SIZE \ + (MCP251XFD_SPI_CRC_LEN + 2 * MCP251XFD_REG_SIZE) + +#define MCP251XFD_SPI_BUF_SIZE \ + MAX(MCP251XFD_MAX_READ_FIFO_BUF_SIZE, MCP251XFD_MAX_READ_CRC_BUF_SIZE) +#define MCP251XFD_SPI_HEADER_LEN (MCP251XFD_SPI_CMD_LEN + MCP251XFD_SPI_LEN_FIELD_LEN) + +struct mcp251xfd_spi_data { + uint8_t _unused[4 - (MCP251XFD_SPI_HEADER_LEN % 4)]; /* so that buf is 4-byte aligned */ + uint8_t header[MCP251XFD_SPI_HEADER_LEN]; /* contains spi_cmd and length field (if used) */ + uint8_t buf[MCP251XFD_SPI_BUF_SIZE]; +} __packed __aligned(4); + +struct mcp251xfd_fifo { + uint32_t ram_start_addr; + uint16_t reg_fifocon_addr; + uint8_t capacity; + uint8_t item_size; + void (*msg_handler)(const struct device *dev, void *data); +}; + +struct mcp251xfd_data { + /* Interrupt Data */ + struct gpio_callback int_gpio_cb; + struct k_thread int_thread; + k_thread_stack_t *int_thread_stack; + struct k_sem int_sem; + + /* General */ + enum can_state state; + can_state_change_callback_t state_change_cb; + void *state_change_cb_data; + struct k_mutex mutex; + + /* TX Callback */ + struct k_sem tx_sem; + uint32_t mailbox_usage; + struct mcp251xfd_mailbox mailbox[CONFIG_CAN_MCP251XFD_MAX_TX_QUEUE]; + + /* Filter Data */ + uint64_t filter_usage; + struct can_filter filter[CONFIG_CAN_MAX_FILTER]; + can_rx_callback_t rx_cb[CONFIG_CAN_MAX_FILTER]; + void *cb_arg[CONFIG_CAN_MAX_FILTER]; + + const struct device *dev; + + bool started; + uint8_t next_mcp251xfd_mode; + uint8_t current_mcp251xfd_mode; + int tdco; + + can_mode_t mode; + + struct mcp251xfd_spi_data spi_data; + +}; + +struct mcp251xfd_timing_params { + uint8_t sjw; + uint8_t prop_seg; + uint8_t phase_seg1; + uint8_t phase_seg2; + uint32_t bus_speed; + uint16_t sample_point; +}; + +struct mcp251xfd_config { + /* spi configuration */ + struct spi_dt_spec bus; + struct gpio_dt_spec int_gpio_dt; + + uint32_t osc_freq; + + /* IO Config */ + bool sof_on_clko; + bool pll_enable; + uint8_t clko_div; + + uint16_t timestamp_prescaler; + + /* CAN Timing */ + struct mcp251xfd_timing_params timing_params; +#if defined(CONFIG_CAN_FD_MODE) + struct mcp251xfd_timing_params timing_params_data; +#endif + + /* CAN transceiver */ + const struct device *phy; + uint32_t max_bitrate; + + const struct device *clk_dev; + uint8_t clk_id; + + struct mcp251xfd_fifo rx_fifo; + struct mcp251xfd_fifo tef_fifo; +}; + +#endif /* ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP251XFD_H_ */ diff --git a/drivers/can/can_mcp25xxfd.c b/drivers/can/can_mcp25xxfd.c deleted file mode 100644 index 30b48495e62..00000000000 --- a/drivers/can/can_mcp25xxfd.c +++ /dev/null @@ -1,1088 +0,0 @@ -/* - * Copyright (c) 2020 Abram Early - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#define DT_DRV_COMPAT microchip_mcp25xxfd - -#include -#include -#include -#include - -#define LOG_LEVEL CONFIG_CAN_LOG_LEVEL -#include -LOG_MODULE_REGISTER(mcp25xxfd_can); - -#include "can_mcp25xxfd.h" - -#define SP_IS_SET(inst) DT_INST_NODE_HAS_PROP(inst, sample_point) || - -/* Macro to exclude the sample point algorithm from compilation if not used - * Without the macro, the algorithm would always waste ROM - */ -#define USE_SP_ALGO (DT_INST_FOREACH_STATUS_OKAY(SP_IS_SET) 0) - -static int mcp25xxfd_reset(const struct device *dev) -{ - uint8_t cmd_buf[] = { 0x00, 0x00 }; - const struct spi_buf tx_buf = { - .buf = cmd_buf, - .len = sizeof(cmd_buf), - }; - const struct spi_buf_set tx = { .buffers = &tx_buf, .count = 1U }; - - return spi_write(DEV_DATA(dev)->spi, &DEV_DATA(dev)->spi_cfg, &tx); -} - -static int mcp25xxfd_read(const struct device *dev, uint16_t address, void *rxd, - uint8_t rx_len) -{ - __ASSERT(address < 0x400 || address >= 0xC00 || - (address % 4 == 0 && rx_len % 4 == 0), - "Address and Length must be word aligned in RAM"); - uint8_t cmd_buf[2 + rx_len]; - - cmd_buf[0] = (MCP25XXFD_OPCODE_READ << 4) + ((address >> 8) & 0x0F); - cmd_buf[1] = address & 0xFF; - const struct spi_buf tx_buf[] = { - { .buf = cmd_buf, .len = sizeof(cmd_buf) }, - }; - const struct spi_buf rx_buf[] = { - { .buf = cmd_buf, .len = sizeof(cmd_buf) }, - }; - const struct spi_buf_set tx = { .buffers = tx_buf, - .count = ARRAY_SIZE(tx_buf) }; - const struct spi_buf_set rx = { .buffers = rx_buf, - .count = ARRAY_SIZE(rx_buf) }; - int ret; - - ret = spi_transceive(DEV_DATA(dev)->spi, &DEV_DATA(dev)->spi_cfg, - &tx, &rx); - memcpy(rxd, &cmd_buf[2], rx_len); - if (ret < 0) { - LOG_ERR("Failed to read %d bytes from 0x%03x", rx_len, address); - } - return ret; -} - -static int mcp25xxfd_write(const struct device *dev, uint16_t address, - void *txd, uint8_t tx_len) -{ - __ASSERT(address < 0x400 || address >= 0xC00 || - (address % 4 == 0 && tx_len % 4 == 0), - "Address and Length must be word aligned in RAM"); - uint8_t cmd_buf[2 + tx_len]; - - cmd_buf[0] = (MCP25XXFD_OPCODE_WRITE << 4) + - ((address >> 8) & 0xF); - cmd_buf[1] = address & 0xFF; - const struct spi_buf tx_buf[] = { - { .buf = cmd_buf, .len = sizeof(cmd_buf) }, - }; - const struct spi_buf_set tx = { .buffers = tx_buf, - .count = ARRAY_SIZE(tx_buf) }; - int ret; - - memcpy(&cmd_buf[2], txd, tx_len); - ret = spi_write(DEV_DATA(dev)->spi, &DEV_DATA(dev)->spi_cfg, &tx); - if (ret < 0) { - LOG_ERR("Failed to write %d bytes to 0x%03x", tx_len, address); - } - return ret; -} - -static inline int mcp25xxfd_readb(const struct device *dev, uint16_t address, - void *rxd) -{ - return mcp25xxfd_read(dev, address, rxd, 1); -} - -static inline int mcp25xxfd_writeb(const struct device *dev, uint16_t address, - void *txd) -{ - return mcp25xxfd_write(dev, address, txd, 1); -} - -static inline int mcp25xxfd_readw(const struct device *dev, uint16_t address, - void *rxd) -{ - return mcp25xxfd_read(dev, address, rxd, 4); -} - -static inline int mcp25xxfd_writew(const struct device *dev, uint16_t address, - void *txd) -{ - return mcp25xxfd_write(dev, address, txd, 4); -} - -static int mcp25xxfd_fifo_read(const struct device *dev, uint16_t fifo_address, - void *rxd, uint8_t rx_len) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_fifo fiforegs; - int ret; - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - ret = mcp25xxfd_read(dev, fifo_address, &fiforegs, sizeof(fiforegs)); - if (ret < 0) { - goto done; - } - - if (!fiforegs.sta.FNEIF) { - ret = -ENODATA; - goto done; - } - - ret = mcp25xxfd_read(dev, 0x400 + fiforegs.ua, rxd, rx_len); - if (ret < 0) { - goto done; - } - - fiforegs.con.UINC = 1; - ret = mcp25xxfd_writeb(dev, fifo_address + 1, &fiforegs.con.bytes[1]); - -done: - k_mutex_unlock(&dev_data->mutex); - return ret; -} - -static int mcp25xxfd_fifo_write(const struct device *dev, uint16_t fifo_address, - void *txd, uint8_t tx_len) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_fifo fiforegs; - int ret; - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - ret = mcp25xxfd_read(dev, fifo_address, &fiforegs, sizeof(fiforegs)); - if (ret < 0) { - goto done; - } - - if (!fiforegs.sta.FNEIF) { - ret = -ENOMEM; - goto done; - } - - ret = mcp25xxfd_write(dev, 0x400 + fiforegs.ua, txd, tx_len); - if (ret < 0) { - goto done; - } - - fiforegs.con.UINC = 1; - fiforegs.con.TXREQ = 1; - ret = mcp25xxfd_writeb(dev, fifo_address + 1, &fiforegs.con.bytes[1]); - -done: - k_mutex_unlock(&dev_data->mutex); - return ret; -} - -static void mcp25xxfd_zcanframe_to_txobj(const struct zcan_frame *src, - struct mcp25xxfd_txobj *dst) -{ - memset(dst, 0, offsetof(struct mcp25xxfd_txobj, DATA)); - - if (src->id_type == CAN_STANDARD_IDENTIFIER) { - dst->SID = src->id; - } else { - dst->SID = src->id >> 18; - dst->EID = src->id; - dst->IDE = 1; - } - dst->BRS = src->brs; - dst->RTR = src->rtr == CAN_REMOTEREQUEST; - dst->DLC = src->dlc; -#if defined(CONFIG_CAN_FD_MODE) - dst->FDF = src->fd; -#endif - - memcpy(dst->DATA, src->data, MIN(can_dlc_to_bytes(src->dlc), CAN_MAX_DLEN)); -} - -static void mcp25xxfd_rxobj_to_zcanframe(const struct mcp25xxfd_rxobj *src, - struct zcan_frame *dst) -{ - memset(dst, 0, offsetof(struct zcan_frame, data)); - - if (src->IDE) { - dst->id = src->EID | (src->SID << 18); - dst->id_type = CAN_EXTENDED_IDENTIFIER; - } else { - dst->id = src->SID; - dst->id_type = CAN_STANDARD_IDENTIFIER; - } - dst->brs = src->BRS; - dst->rtr = src->RTR; - dst->dlc = src->DLC; -#if defined(CONFIG_CAN_FD_MODE) - dst->fd = src->FDF; -#endif -#if defined(CONFIG_CAN_RX_TIMESTAMP) - dst->timestamp = src->RXMSGTS; -#endif - - memcpy(dst->data, src->DATA, MIN(can_dlc_to_bytes(src->DLC), CAN_MAX_DLEN)); -} - -static int mcp25xxfd_get_raw_mode(const struct device *dev, uint8_t *mode) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_con con; - int ret; - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - ret = mcp25xxfd_readb(dev, MCP25XXFD_REG_CON + 2, &con.byte[2]); - k_mutex_unlock(&dev_data->mutex); - *mode = con.OPMOD; - return ret; -} - -static int mcp25xxfd_set_raw_mode(const struct device *dev, uint8_t mode) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_con con; - int ret; - - while (true) { - k_mutex_lock(&dev_data->mutex, K_FOREVER); - ret = mcp25xxfd_readw(dev, MCP25XXFD_REG_CON, &con); - if (ret < 0 || con.OPMOD == mode) { - k_mutex_unlock(&dev_data->mutex); - break; - } - - if (con.OPMOD == MCP25XXFD_OPMODE_CONFIGURATION) { - /* Configuration mode can be switched to any other mode */ - con.REQMOD = mode; - } else if (con.OPMOD == MCP25XXFD_OPMODE_NORMAL_CANFD || - con.OPMOD == MCP25XXFD_OPMODE_NORMAL_CAN2) { - /* Normal modes can only be directly switched to Sleep, Restricted, or Listen-Only modes */ - if (mode == MCP25XXFD_OPMODE_SLEEP || - mode == MCP25XXFD_OPMODE_RESTRICTED || - mode == MCP25XXFD_OPMODE_LISTEN_ONLY) { - con.REQMOD = mode; - } else { - con.REQMOD = MCP25XXFD_OPMODE_CONFIGURATION; - } - } else if (con.OPMOD == MCP25XXFD_OPMODE_LISTEN_ONLY) { - /* Listen-Only mode can be directly switched back to normal modes */ - if (mode == MCP25XXFD_OPMODE_NORMAL_CANFD || - mode == MCP25XXFD_OPMODE_NORMAL_CAN2) { - con.REQMOD = mode; - } else { - con.REQMOD = MCP25XXFD_OPMODE_CONFIGURATION; - } - } else { - /* Otherwise, the device must be put into configuration mode first */ - con.REQMOD = MCP25XXFD_OPMODE_CONFIGURATION; - } - - LOG_DBG("OPMOD: #%d, REQMOD #%d", con.OPMOD, con.REQMOD); - ret = mcp25xxfd_writeb(dev, MCP25XXFD_REG_CON + 3, - &con.byte[3]); - k_mutex_unlock(&dev_data->mutex); - if (ret < 0) { - break; - } - - ret = k_sem_take(&dev_data->mode_sem, K_MSEC(2)); - if (ret == -EAGAIN) { - return CAN_TIMEOUT; - } - } - return ret; -} - -static int mcp25xxfd_set_mode(const struct device *dev, enum can_mode mode) -{ - switch (mode) { - case CAN_NORMAL_MODE: -#if defined(CONFIG_CAN_FD_MODE) - return mcp25xxfd_set_raw_mode(dev, - MCP25XXFD_OPMODE_NORMAL_CANFD); -#else - return mcp25xxfd_set_raw_mode(dev, - MCP25XXFD_OPMODE_NORMAL_CAN2); -#endif - case CAN_LOOPBACK_MODE: - return mcp25xxfd_set_raw_mode(dev, - MCP25XXFD_OPMODE_EXT_LOOPBACK); - default: - LOG_ERR("Unsupported CAN Mode %u", mode); - case CAN_SILENT_MODE: - return mcp25xxfd_set_raw_mode(dev, - MCP25XXFD_OPMODE_LISTEN_ONLY); - } -} - -static int mcp25xxfd_set_timing(const struct device *dev, - const struct can_timing *timing, - const struct can_timing *timing_data) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - uint8_t mode; - int ret; - -#if defined(CONFIG_CAN_FD_MODE) - if (!timing || !timing_data) { -#else - if (!timing) { -#endif - return -EINVAL; - } - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - ret = mcp25xxfd_get_raw_mode(dev, &mode); - if (ret < 0) { - goto done; - } - ret = mcp25xxfd_set_raw_mode(dev, MCP25XXFD_OPMODE_CONFIGURATION); - if (ret < 0) { - goto done; - } - - union mcp25xxfd_nbtcfg nbtcfg = { - .BRP = timing->prescaler - 1, - .TSEG1 = (timing->prop_seg + timing->phase_seg1) - 1, - .TSEG2 = timing->phase_seg2 - 1, - .SJW = timing->sjw - 1, - }; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_NBTCFG, nbtcfg.byte); - if (ret < 0) { - LOG_ERR("Failed to write device configuration [%d]", ret); - goto done; - } - -#if defined(CONFIG_CAN_FD_MODE) - union mcp25xxfd_dbtcfg ndtcfg = { - .BRP = timing_data->prescaler - 1, - .TSEG1 = (timing_data->prop_seg + timing_data->phase_seg1) - 1, - .TSEG2 = timing_data->phase_seg2 - 1, - .SJW = timing_data->sjw - 1, - }; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_DBTCFG, ndtcfg.byte); - if (ret < 0) { - LOG_ERR("Failed to write device configuration [%d]", ret); - goto done; - } -#endif - - union mcp25xxfd_tdc tdc = { - .EDGFLTEN = 0, - .SID11EN = 0, -#if defined(CONFIG_CAN_FD_MODE) - .TDCMOD = MCP25XXFD_TDCMOD_AUTO, - .TDCO = timing_data->prescaler * - (timing_data->prop_seg + timing_data->phase_seg1), -#else - .TDCMOD = MCP25XXFD_TDCMOD_DISABLED, -#endif - }; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_TDC, &tdc); - if (ret < 0) { - LOG_ERR("Failed to write device configuration [%d]", ret); - goto done; - } - -#if defined(CONFIG_CAN_RX_TIMESTAMP) - union mcp25xxfd_tscon tscon = { - .TBCEN = 1, - .TSRES = 0, - .TSEOF = 0, - .TBCPRE = timing->prescaler - 1, - }; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_TSCON, &tscon); - if (ret < 0) { - LOG_ERR("Failed to write device configuration [%d]", ret); - goto done; - } -#endif - - ret = mcp25xxfd_set_raw_mode(dev, mode); - if (ret < 0) { - goto done; - } - -done: - k_mutex_unlock(&dev_data->mutex); - - return ret; -} - -static int mcp25xxfd_send(const struct device *dev, - const struct zcan_frame *msg, k_timeout_t timeout, - can_tx_callback_t callback, void *callback_arg) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - struct mcp25xxfd_txobj tx_frame; - uint8_t mailbox_idx = 0; - int ret; - - LOG_DBG("Sending %d bytes. Id: 0x%x, ID type: %s %s %s %s", - can_dlc_to_bytes(msg->dlc), msg->id, - msg->id_type == CAN_STANDARD_IDENTIFIER ? - "standard" : "extended", - msg->rtr == CAN_DATAFRAME ? "" : "RTR", - msg->fd == CAN_DATAFRAME ? "" : "FD frame", - msg->brs == CAN_DATAFRAME ? "" : "BRS"); - - if (msg->fd != 1 && msg->dlc > CAN_MAX_DLC) { - LOG_ERR("DLC of %d without fd flag set.", msg->dlc); - return CAN_TX_EINVAL; - } - - if (k_sem_take(&dev_data->tx_sem, timeout) != 0) { - return CAN_TIMEOUT; - } - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - for (; mailbox_idx < MCP25XXFD_TXFIFOS; mailbox_idx++) { - if ((BIT(mailbox_idx) & dev_data->mailbox_usage) == 0) { - dev_data->mailbox_usage |= BIT(mailbox_idx); - break; - } - } - k_mutex_unlock(&dev_data->mutex); - - if (mailbox_idx >= MCP25XXFD_TXFIFOS) { - k_sem_give(&dev_data->tx_sem); - return CAN_TX_ERR; - } - - dev_data->mailbox[mailbox_idx].cb = callback; - dev_data->mailbox[mailbox_idx].cb_arg = callback_arg; - - mcp25xxfd_zcanframe_to_txobj(msg, &tx_frame); - tx_frame.SEQ = mailbox_idx; - ret = mcp25xxfd_fifo_write(dev, MCP25XXFD_REG_FIFOCON(mailbox_idx), &tx_frame, - offsetof(struct mcp25xxfd_txobj, DATA) + ROUND_UP(can_dlc_to_bytes(msg->dlc), 4)); - - if (ret >= 0) { - if (callback == NULL) { - k_sem_take(&dev_data->mailbox[mailbox_idx].tx_sem, - timeout); - } - } else { - k_mutex_lock(&dev_data->mutex, K_FOREVER); - dev_data->mailbox_usage &= ~BIT(mailbox_idx); - k_mutex_unlock(&dev_data->mutex); - k_sem_give(&dev_data->tx_sem); - } - - return ret; -} - -static int mcp25xxfd_attach_isr(const struct device *dev, - can_rx_callback_t rx_cb, void *cb_arg, - const struct zcan_filter *filter) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_fltcon fltcon; - union mcp25xxfd_fltobj fltobj = { .word = 0 }; - union mcp25xxfd_mask mask = { .word = 0 }; - int filter_idx = 0; - int ret; - - __ASSERT(rx_cb != NULL, "rx_cb can not be null"); - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - while ((BIT(filter_idx) & dev_data->filter_usage) && - (filter_idx < CONFIG_CAN_MAX_FILTER)) { - filter_idx++; - } - - if (filter_idx < CONFIG_CAN_MAX_FILTER) { - if (filter->id_type == CAN_STANDARD_IDENTIFIER) { - fltobj.SID = filter->id; - mask.MSID = filter->id_mask; - } else { - fltobj.SID = filter->id >> 18; - mask.MSID = filter->id_mask >> 18; - fltobj.EID = filter->id; - mask.MEID = filter->id_mask; - fltobj.EXIDE = 1; - } - mask.MIDE = 1; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_FLTOBJ(filter_idx), - &fltobj); - if (ret < 0) { - LOG_ERR("Failed to write register [%d]", ret); - goto done; - } - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_MASK(filter_idx), - &mask); - if (ret < 0) { - LOG_ERR("Failed to write register [%d]", ret); - goto done; - } - - fltcon.FLTEN = 1; - fltcon.FLTBP = MCP25XXFD_RXFIFO_IDX; - ret = mcp25xxfd_writeb(dev, MCP21518FD_REG_FLTCON(filter_idx), - fltcon.byte); - if (ret < 0) { - LOG_ERR("Failed to write register [%d]", ret); - goto done; - } - - dev_data->filter_usage |= BIT(filter_idx); - dev_data->filter[filter_idx] = *filter; - dev_data->rx_cb[filter_idx] = rx_cb; - dev_data->cb_arg[filter_idx] = cb_arg; - } else { - filter_idx = CAN_NO_FREE_FILTER; - } -done: - k_mutex_unlock(&dev_data->mutex); - - return filter_idx; -} - -static void mcp25xxfd_detach(const struct device *dev, int filter_nr) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_fltcon fltcon; - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - dev_data->filter_usage &= ~BIT(filter_nr); - fltcon.FLTEN = 0; - mcp25xxfd_writeb(dev, MCP21518FD_REG_FLTCON(filter_nr), &fltcon); - - k_mutex_unlock(&dev_data->mutex); -} - -static void mcp25xxfd_register_state_change_isr(const struct device *dev, - can_state_change_isr_t isr) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - - dev_data->state_change_isr = isr; -} - -static enum can_state mcp25xxfd_get_state(const struct device *dev, - struct can_bus_err_cnt *err_cnt) -{ - return DEV_DATA(dev)->state; -} - -static int mcp25xxfd_get_core_clock(const struct device *dev, uint32_t *rate) -{ - const struct mcp25xxfd_config *dev_cfg = DEV_CFG(dev); - - *rate = dev_cfg->osc_freq; - return 0; -} - -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY -static void mcp25xxfd_recover(const struct device *dev, k_timeout_t timeout) -{ - ARG_UNUSED(dev); - ARG_UNUSED(timeout); -} -#endif - -static void mcp25xxfd_rx(const struct device *dev, int fifo_idx) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - struct mcp25xxfd_rxobj rx_frame; - struct zcan_frame msg; - - while (mcp25xxfd_fifo_read(dev, MCP25XXFD_REG_FIFOCON(fifo_idx), &rx_frame, - sizeof(rx_frame)) >= 0) { - mcp25xxfd_rxobj_to_zcanframe(&rx_frame, &msg); - if (dev_data->filter_usage & BIT(rx_frame.FILHIT)) { - dev_data->rx_cb[rx_frame.FILHIT]( - &msg, dev_data->cb_arg[rx_frame.FILHIT]); - } - } -} - -static void mcp25xxfd_tx_done(const struct device *dev) -{ - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - struct mcp25xxfd_tefobj tefobj; - uint8_t mailbox_idx; - - while (mcp25xxfd_fifo_read(dev, MCP25XXFD_REG_TEFCON, &tefobj, - sizeof(tefobj)) >= 0) { - mailbox_idx = tefobj.SEQ; - if (dev_data->mailbox[mailbox_idx].cb == NULL) { - k_sem_give(&dev_data->mailbox[mailbox_idx].tx_sem); - } else { - dev_data->mailbox[mailbox_idx].cb( - 0, dev_data->mailbox[mailbox_idx].cb_arg); - } - k_mutex_lock(&dev_data->mutex, K_FOREVER); - dev_data->mailbox_usage &= ~BIT(mailbox_idx); - k_mutex_unlock(&dev_data->mutex); - k_sem_give(&dev_data->tx_sem); - } -} - -static void mcp25xxfd_int_thread(const struct device *dev) -{ - const struct mcp25xxfd_config *dev_cfg = DEV_CFG(dev); - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - union mcp25xxfd_intregs intregs; - union mcp25xxfd_trec trec; - uint32_t ints_before; - int ret; - - while (1) { - k_sem_take(&dev_data->int_sem, K_FOREVER); - - while (1) { - ret = mcp25xxfd_read(dev, MCP25XXFD_REG_INTREGS, - &intregs, sizeof(intregs)); - if (ret < 0) { - continue; - } - ints_before = intregs.ints.word; - - if (intregs.ints.RXIF) { - mcp25xxfd_rx(dev, MCP25XXFD_RXFIFO_IDX); - } - - if (intregs.ints.TEFIF) { - mcp25xxfd_tx_done(dev); - } - - if (intregs.ints.MODIF) { - k_sem_give(&dev_data->mode_sem); - intregs.ints.MODIF = 0; - } - - if (intregs.ints.CERRIF) { - ret = mcp25xxfd_readw(dev, MCP25XXFD_REG_TREC, - &trec); - if (ret >= 0) { - enum can_state new_state; - - if (trec.TXBO) { - new_state = CAN_BUS_OFF; - - /* Upon entering bus-off, all the fifos are reset. */ - LOG_DBG("All FIFOs Reset"); - k_mutex_lock(&dev_data->mutex, K_FOREVER); - for (int i = 0; i < MCP25XXFD_TXFIFOS; i++) { - if (!(dev_data->mailbox_usage & BIT(i))) { - continue; - } - if (dev_data->mailbox[i].cb == NULL) { - k_sem_give(&dev_data->mailbox[i].tx_sem); - } else { - dev_data->mailbox[i].cb( - CAN_TX_BUS_OFF, dev_data->mailbox[i].cb_arg); - } - dev_data->mailbox_usage &= ~BIT(i); - k_sem_give(&dev_data->tx_sem); - } - k_mutex_unlock(&dev_data->mutex); - } else if (trec.TXBP || trec.RXBP) { - new_state = CAN_ERROR_PASSIVE; - } else { - new_state = CAN_ERROR_ACTIVE; - } - if (dev_data->state != new_state) { - LOG_DBG("State %d -> %d (tx: %d, rx: %d)", dev_data->state, new_state, trec.TEC, trec.REC); - dev_data->state = new_state; - if (dev_data->state_change_isr) { - struct can_bus_err_cnt - err_cnt; - err_cnt.rx_err_cnt = - trec.REC; - err_cnt.tx_err_cnt = - trec.TEC; - dev_data->state_change_isr( - new_state, - err_cnt); - } - } - - intregs.ints.CERRIF = 0; - } - } - - if (ints_before != intregs.ints.word) { - mcp25xxfd_writew(dev, MCP25XXFD_REG_INT, &intregs.ints); - } - - /* Break from loop if INT pin is inactive */ - ret = gpio_pin_get(dev_data->int_gpio, - dev_cfg->int_pin); - if (ret <= 0) { - /* All interrupt flags handled, but we'll abort if - * an error occurs to avoid deadlock. */ - break; - } - } - - /* Re-enable pin interrupts */ - if (gpio_pin_interrupt_configure(dev_data->int_gpio, dev_cfg->int_pin, - GPIO_INT_LEVEL_ACTIVE)) { - LOG_ERR("Couldn't enable pin interrupt"); - k_oops(); - } - } -} - -static void mcp25xxfd_int_gpio_callback(const struct device *dev, - struct gpio_callback *cb, uint32_t pins) -{ - struct mcp25xxfd_data *dev_data = - CONTAINER_OF(cb, struct mcp25xxfd_data, int_gpio_cb); - - /* Disable pin interrupts */ - if (gpio_pin_interrupt_configure(dev, dev_data->int_pin, GPIO_INT_DISABLE)) { - LOG_ERR("Couldn't disable pin interrupt"); - k_oops(); - } - - k_sem_give(&dev_data->int_sem); -} - -static const struct can_driver_api can_api_funcs = { - .set_mode = mcp25xxfd_set_mode, - .set_timing = mcp25xxfd_set_timing, - .send = mcp25xxfd_send, - .attach_isr = mcp25xxfd_attach_isr, - .detach = mcp25xxfd_detach, -#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY - .recover = mcp25xxfd_recover, -#endif - .get_state = mcp25xxfd_get_state, - .register_state_change_isr = mcp25xxfd_register_state_change_isr, - .get_core_clock = mcp25xxfd_get_core_clock, - .timing_min = { .sjw = 1, - .prop_seg = 0x0, - .phase_seg1 = 2, - .phase_seg2 = 1, - .prescaler = 1 }, - .timing_max = { .sjw = 128, - .prop_seg = 0x0, - .phase_seg1 = 256, - .phase_seg2 = 128, - .prescaler = 256 }, -#if defined(CONFIG_CAN_FD_MODE) - .timing_min_data = { .sjw = 1, - .prop_seg = 0x0, - .phase_seg1 = 1, - .phase_seg2 = 1, - .prescaler = 1 }, - .timing_max_data = { .sjw = 16, - .prop_seg = 0x0, - .phase_seg1 = 32, - .phase_seg2 = 16, - .prescaler = 256 } -#endif -}; - -static int mcp25xxfd_init(const struct device *dev) -{ - const struct mcp25xxfd_config *dev_cfg = DEV_CFG(dev); - struct mcp25xxfd_data *dev_data = DEV_DATA(dev); - int ret; - struct can_timing timing; - -#if defined(CONFIG_CAN_FD_MODE) - struct can_timing timing_data; -#endif - - k_sem_init(&dev_data->int_sem, 0, 1); - k_sem_init(&dev_data->mode_sem, 0, 1); - k_sem_init(&dev_data->tx_sem, MCP25XXFD_TXFIFOS, - MCP25XXFD_TXFIFOS); - for (int i = 0; i < MCP25XXFD_TXFIFOS; i++) { - k_sem_init(&dev_data->mailbox[i].tx_sem, 0, 1); - } - k_mutex_init(&dev_data->mutex); - - /* SPI Init */ - dev_data->spi_cfg.operation = SPI_WORD_SET(8); - dev_data->spi_cfg.frequency = dev_cfg->spi_freq; - dev_data->spi_cfg.slave = dev_cfg->spi_slave; - dev_data->spi = device_get_binding(dev_cfg->spi_port); - if (!dev_data->spi) { - LOG_ERR("SPI master port %s not found", dev_cfg->spi_port); - return -EINVAL; - } - -#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) - dev_data->spi_cs_ctrl.gpio_dev = - device_get_binding(dev_cfg->spi_cs_port); - if (!dev_data->spi_cs_ctrl.gpio_dev) { - LOG_ERR("Unable to get GPIO SPI CS device"); - return -ENODEV; - } - - dev_data->spi_cs_ctrl.gpio_pin = dev_cfg->spi_cs_pin; - dev_data->spi_cs_ctrl.gpio_dt_flags = dev_cfg->spi_cs_flags; - dev_data->spi_cs_ctrl.delay = 0U; - - dev_data->spi_cfg.cs = &dev_data->spi_cs_ctrl; -#else - dev_data->spi_cfg.cs = NULL; -#endif /* DT_INST_SPI_DEV_HAS_CS_GPIOS(0) */ - - ret = mcp25xxfd_reset(dev); - if (ret < 0) { - LOG_ERR("Failed to reset the device [%d]", ret); - return -EIO; - } - - dev_data->int_gpio = device_get_binding(dev_cfg->int_port); - if (dev_data->int_gpio == NULL) { - LOG_ERR("GPIO port %s not found", dev_cfg->int_port); - return -EINVAL; - } - - if (gpio_pin_configure(dev_data->int_gpio, dev_cfg->int_pin, - (GPIO_INPUT | - DT_INST_GPIO_FLAGS(0, int_gpios)))) { - LOG_ERR("Unable to configure GPIO pin %u", dev_cfg->int_pin); - return -EINVAL; - } - - gpio_init_callback(&(dev_data->int_gpio_cb), - mcp25xxfd_int_gpio_callback, BIT(dev_cfg->int_pin)); - dev_data->int_pin = dev_cfg->int_pin; - - if (gpio_add_callback(dev_data->int_gpio, &(dev_data->int_gpio_cb))) { - return -EINVAL; - } - - if (gpio_pin_interrupt_configure(dev_data->int_gpio, dev_cfg->int_pin, - GPIO_INT_LEVEL_ACTIVE)) { - return -EINVAL; - } - - k_thread_create(&dev_data->int_thread, dev_data->int_thread_stack, - dev_cfg->int_thread_stack_size, - (k_thread_entry_t)mcp25xxfd_int_thread, (void *)dev, - NULL, NULL, K_PRIO_COOP(dev_cfg->int_thread_priority), - 0, K_NO_WAIT); - - timing.sjw = dev_cfg->tq_sjw; - if (dev_cfg->sample_point && USE_SP_ALGO) { - ret = can_calc_timing(dev, &timing, dev_cfg->bus_speed, - dev_cfg->sample_point); - if (ret == -EINVAL) { - LOG_ERR("Can't find timing for given param"); - return -EIO; - } - LOG_DBG("Presc: %d, BS1: %d, BS2: %d", timing.prescaler, - timing.phase_seg1, timing.phase_seg2); - LOG_DBG("Sample-point err : %d", ret); - } else { - timing.prop_seg = dev_cfg->tq_prop; - timing.phase_seg1 = dev_cfg->tq_bs1; - timing.phase_seg2 = dev_cfg->tq_bs2; - ret = can_calc_prescaler(dev, &timing, dev_cfg->bus_speed); - if (ret) { - LOG_WRN("Bitrate error: %d", ret); - } - } - -#if defined(CONFIG_CAN_FD_MODE) - timing_data.sjw = dev_cfg->tq_sjw_data; - if (dev_cfg->sample_point && USE_SP_ALGO) { - ret = can_calc_timing(dev, &timing_data, - dev_cfg->bus_speed_data, - dev_cfg->sample_point_data); - if (ret == -EINVAL) { - LOG_ERR("Can't find timing for given param"); - return -EIO; - } - LOG_DBG("Presc: %d, BS1: %d, BS2: %d", timing.prescaler, - timing.phase_seg1, timing.phase_seg2); - LOG_DBG("Sample-point err : %d", ret); - } else { - timing_data.prop_seg = dev_cfg->tq_prop_data; - timing_data.phase_seg1 = dev_cfg->tq_bs1_data; - timing_data.phase_seg2 = dev_cfg->tq_bs2_data; - ret = can_calc_prescaler(dev, &timing_data, - dev_cfg->bus_speed_data); - if (ret) { - LOG_WRN("Bitrate error: %d", ret); - } - } -#endif - - k_mutex_lock(&dev_data->mutex, K_FOREVER); - - union mcp25xxfd_con con; - union mcp25xxfd_int regint = { .word = 0x00000000 }; - union mcp25xxfd_iocon iocon; - union mcp25xxfd_osc osc; - union mcp25xxfd_fifocon tefcon = { .word = 0x00000400 }; - union mcp25xxfd_fifocon txfifocon = { .word = 0x00600400 }; - union mcp25xxfd_fifocon fifocon = { .word = 0x00600400 }; - - ret = mcp25xxfd_readw(dev, MCP25XXFD_REG_CON, &con); - if (ret < 0) { - goto done; - } else if (con.OPMOD != MCP25XXFD_OPMODE_CONFIGURATION) { - LOG_ERR("Device did not reset into configuration mode [%d]", - con.OPMOD); - ret = -EIO; - goto done; - } - con.TXBWS = 0; - con.ABAT = 0; - con.REQMOD = MCP25XXFD_OPMODE_CONFIGURATION; - con.TXQEN = 1; - con.STEF = 1; - con.SERR2LOM = 0; - con.ESIGM = 0; - con.RTXAT = 0; - con.BRSDIS = 0; - con.BUSY = 0; - con.WFT = MCP25XXFD_WFT_T11FILTER; - con.WAKFIL = 1; - con.PXEDIS = 0; - con.ISOCRCEN = 1; - con.DNCNT = 0; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_CON, con.byte); - if (ret < 0) { - goto done; - } - - osc.PLLEN = 0; - osc.OSCDIS = 0; - osc.LPMEN = 0; - osc.SCLKDIV = 0; - osc.CLKODIV = dev_cfg->clko_div; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_OSC, &osc.word); - if (ret < 0) { - goto done; - } - - iocon.TRIS0 = 1; - iocon.TRIS1 = 1; - iocon.XSTBYEN = 0; - iocon.LAT0 = 0; - iocon.LAT1 = 0; - iocon.PM0 = 1; - iocon.PM1 = 1; - iocon.TXCANOD = 0; - iocon.SOF = dev_cfg->sof_on_clko ? 1 : 0; - iocon.INTOD = 0; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_IOCON, &iocon.word); - if (ret < 0) { - goto done; - } - - regint.RXIE = 1; - regint.MODIE = 1; - regint.TEFIE = 1; - regint.CERRIE = 1; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_INT, ®int); - if (ret < 0) { - goto done; - } - - tefcon.FSIZE = MCP25XXFD_TXFIFOS - 1; - tefcon.FNEIE = 1; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_TEFCON, &tefcon); - if (ret < 0) { - goto done; - } - - txfifocon.PLSIZE = can_bytes_to_dlc(MCP25XXFD_PAYLOAD_SIZE) - 8; - txfifocon.FSIZE = 0; - txfifocon.TXPRI = 0; - txfifocon.TXEN = 1; - for (int i = 0; i < MCP25XXFD_TXFIFOS; i++) { - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_FIFOCON(i), &txfifocon); - if (ret < 0) { - goto done; - } - } - - fifocon.PLSIZE = can_bytes_to_dlc(MCP25XXFD_PAYLOAD_SIZE) - 8; - fifocon.FSIZE = MCP25XXFD_RXFIFO_LENGTH - 1; -#if defined(CONFIG_CAN_RX_TIMESTAMP) - fifocon.TSEN = 1; -#endif - fifocon.FNEIE = 1; - ret = mcp25xxfd_writew(dev, MCP25XXFD_REG_FIFOCON(MCP25XXFD_RXFIFO_IDX), &fifocon); - if (ret < 0) { - goto done; - } - - LOG_DBG("%d TX FIFOS: 1 element", MCP25XXFD_TXFIFOS); - LOG_DBG("1 RX FIFO: %ld elements", MCP25XXFD_RXFIFO_LENGTH); - LOG_DBG("%ldb of %db RAM Allocated", MCP25XXFD_TEF_SIZE + MCP25XXFD_TXFIFOS_SIZE + MCP25XXFD_RXFIFO_SIZE, MCP25XXFD_RAM_SIZE); - -done: - k_mutex_unlock(&dev_data->mutex); - -#if defined(CONFIG_CAN_FD_MODE) - ret = can_set_timing(dev, &timing, &timing_data); -#else - ret = can_set_timing(dev, &timing, NULL); -#endif - if (ret < 0) { - return ret; - } - - ret = can_set_mode(dev, CAN_NORMAL_MODE); - - return ret; -} - -#if DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) - -static K_KERNEL_STACK_DEFINE(mcp25xxfd_int_stack_0, - CONFIG_CAN_MCP25XXFD_INT_THREAD_STACK_SIZE); -static struct mcp25xxfd_data mcp25xxfd_data_0 = { - .int_thread_stack = mcp25xxfd_int_stack_0 -}; -static const struct mcp25xxfd_config mcp25xxfd_config_0 = { - .spi_port = DT_INST_BUS_LABEL(0), - .spi_freq = DT_INST_PROP(0, spi_max_frequency), - .spi_slave = DT_INST_REG_ADDR(0), -#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) - .spi_cs_pin = DT_INST_SPI_DEV_CS_GPIOS_PIN(0), - .spi_cs_port = DT_INST_SPI_DEV_CS_GPIOS_LABEL(0), - .spi_cs_flags = DT_INST_SPI_DEV_CS_GPIOS_FLAGS(0), -#endif /* DT_INST_SPI_DEV_HAS_CS_GPIOS(0) */ - - .int_pin = DT_INST_GPIO_PIN(0, int_gpios), - .int_port = DT_INST_GPIO_LABEL(0, int_gpios), - .int_thread_stack_size = CONFIG_CAN_MCP25XXFD_INT_THREAD_STACK_SIZE, - .int_thread_priority = CONFIG_CAN_MCP25XXFD_INT_THREAD_PRIO, - - .sof_on_clko = DT_INST_PROP(0, sof_on_clko), - .clko_div = DT_ENUM_IDX(DT_DRV_INST(0), clko_div), - - .osc_freq = DT_INST_PROP(0, osc_freq), - .tq_sjw = DT_INST_PROP(0, sjw), - .tq_prop = DT_INST_PROP_OR(0, prop_seg, 0), - .tq_bs1 = DT_INST_PROP_OR(0, phase_seg1, 0), - .tq_bs2 = DT_INST_PROP_OR(0, phase_seg2, 0), - .bus_speed = DT_INST_PROP(0, bus_speed), - .sample_point = DT_INST_PROP_OR(0, sample_point, 0), - -#if defined(CONFIG_CAN_FD_MODE) - .tq_sjw_data = DT_INST_PROP(0, sjw_data), - .tq_prop_data = DT_INST_PROP_OR(0, prop_seg_data, 0), - .tq_bs1_data = DT_INST_PROP_OR(0, phase_seg1_data, 0), - .tq_bs2_data = DT_INST_PROP_OR(0, phase_seg2_data, 0), - .bus_speed_data = DT_INST_PROP(0, bus_speed_data), - .sample_point_data = DT_INST_PROP_OR(0, sample_point_data, 0) -#endif -}; - -DEVICE_DT_INST_DEFINE(0, &mcp25xxfd_init, NULL, - &mcp25xxfd_data_0, &mcp25xxfd_config_0, POST_KERNEL, - CONFIG_CAN_MCP25XXFD_INIT_PRIORITY, &can_api_funcs); - -#endif /* DT_NODE_HAS_STATUS(DT_DRV_INST(0), okay) */ diff --git a/drivers/can/can_mcp25xxfd.h b/drivers/can/can_mcp25xxfd.h deleted file mode 100644 index 4d9cb448f97..00000000000 --- a/drivers/can/can_mcp25xxfd.h +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Copyright (c) 2020 Abram Early - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP25XXFD_H_ -#define ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP25XXFD_H_ - -#include - -#define DEV_CFG(dev) ((const struct mcp25xxfd_config *const)(dev)->config) -#define DEV_DATA(dev) ((struct mcp25xxfd_data *const)(dev)->data) - -#define MCP25XXFD_RAM_SIZE 2048 -#define MCP25XXFD_PAYLOAD_SIZE CLAMP(ROUND_UP(CAN_MAX_DLEN, 4), 8, 64) -#if defined(CONFIG_CAN_TX_TIMESTAMP) -/* Note: This will be implemented with a future can_send overhaul */ -#define MCP25XXFD_TEF_SIZE (CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE * (4 + 8)) -#else -#define MCP25XXFD_TEF_SIZE (CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE * (0 + 8)) -#endif -#define MCP25XXFD_TXFIFOS_SIZE (CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE * (8 + MCP25XXFD_PAYLOAD_SIZE)) -#define MCP25XXFD_RXFIFO_MAX \ - ((MCP25XXFD_RAM_SIZE - (MCP25XXFD_TEF_SIZE + MCP25XXFD_TXFIFOS_SIZE))) -#if defined(CONFIG_CAN_RX_TIMESTAMP) -#define MCP25XXFD_RXFIFO_ELEMENT_SIZE (4 + 8 + MCP25XXFD_PAYLOAD_SIZE) -#else -#define MCP25XXFD_RXFIFO_ELEMENT_SIZE (0 + 8 + MCP25XXFD_PAYLOAD_SIZE) -#endif -#define MCP25XXFD_RXFIFO_LENGTH \ - MIN(MCP25XXFD_RXFIFO_MAX / MCP25XXFD_RXFIFO_ELEMENT_SIZE, 32) -#define MCP25XXFD_RXFIFO_SIZE (MCP25XXFD_RXFIFO_LENGTH * MCP25XXFD_RXFIFO_ELEMENT_SIZE) -#define MCP25XXFD_TXFIFOS CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE -#define MCP25XXFD_RXFIFO_IDX CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE -BUILD_ASSERT(MCP25XXFD_RXFIFO_LENGTH >= 1, - "Cannot fit RX FIFO into MCP25xxFD RAM"); - -struct mcp25xxfd_mailbox { - can_tx_callback_t cb; - void *cb_arg; - struct k_sem tx_sem; -}; - -struct mcp25xxfd_data { - /* SPI Data */ - const struct device *spi; - struct spi_config spi_cfg; -#if DT_INST_SPI_DEV_HAS_CS_GPIOS(0) - struct spi_cs_control spi_cs_ctrl; -#endif /* DT_INST_SPI_DEV_HAS_CS_GPIOS(0) */ - - /* Interrupt Data */ - const struct device *int_gpio; - struct gpio_callback int_gpio_cb; - struct k_thread int_thread; - k_thread_stack_t *int_thread_stack; - struct k_sem int_sem; - uint8_t int_pin; - - /* General */ - enum can_state state; - can_state_change_isr_t state_change_isr; - struct k_mutex mutex; - struct k_sem mode_sem; - - /* TX Callback */ - struct k_sem tx_sem; - uint32_t mailbox_usage; - struct mcp25xxfd_mailbox mailbox[CONFIG_CAN_MCP25XXFD_MAX_TX_QUEUE]; - - /* Filter Data */ - uint64_t filter_usage; - struct zcan_filter filter[CONFIG_CAN_MAX_FILTER]; - can_rx_callback_t rx_cb[CONFIG_CAN_MAX_FILTER]; - void *cb_arg[CONFIG_CAN_MAX_FILTER]; -}; - -struct mcp25xxfd_config { - /* SPI Config */ - const char *spi_port; - uint32_t spi_freq; - uint8_t spi_slave; - uint8_t spi_cs_pin; - uint8_t spi_cs_flags; - const char *spi_cs_port; - - /* Interrupt Config */ - uint8_t int_pin; - const char *int_port; - size_t int_thread_stack_size; - int int_thread_priority; - uint32_t osc_freq; - - /* CAN Timing */ - uint8_t tq_sjw; - uint8_t tq_prop; - uint8_t tq_bs1; - uint8_t tq_bs2; - uint32_t bus_speed; - uint16_t sample_point; - - /* IO Config */ - bool sof_on_clko; - uint8_t clko_div; - -#if defined(CONFIG_CAN_FD_MODE) - /* CAN-FD Timing */ - uint8_t tq_sjw_data; - uint8_t tq_prop_data; - uint8_t tq_bs1_data; - uint8_t tq_bs2_data; - uint32_t bus_speed_data; - uint16_t sample_point_data; -#endif -}; - -/* MCP25XXFD Opcodes */ -#define MCP25XXFD_OPCODE_RESET 0x00 -#define MCP25XXFD_OPCODE_WRITE 0x02 -#define MCP25XXFD_OPCODE_READ 0x03 - -/* MCP25XXFD Operation Modes */ -#define MCP25XXFD_OPMODE_NORMAL_CANFD 0b000 -#define MCP25XXFD_OPMODE_SLEEP 0b001 -#define MCP25XXFD_OPMODE_INT_LOOPBACK 0b010 -#define MCP25XXFD_OPMODE_LISTEN_ONLY 0b011 -#define MCP25XXFD_OPMODE_CONFIGURATION 0b100 -#define MCP25XXFD_OPMODE_EXT_LOOPBACK 0b101 -#define MCP25XXFD_OPMODE_NORMAL_CAN2 0b110 -#define MCP25XXFD_OPMODE_RESTRICTED 0b110 - -#define MCP25XXFD_WFT_T00FILTER 0b00 -#define MCP25XXFD_WFT_T01FILTER 0b01 -#define MCP25XXFD_WFT_T10FILTER 0b10 -#define MCP25XXFD_WFT_T11FILTER 0b11 - -#define MCP25XXFD_TDCMOD_AUTO 0b10 -#define MCP25XXFD_TDCMOD_MANUAL 0b01 -#define MCP25XXFD_TDCMOD_DISABLED 0b00 - -/* MCP25XXFD Registers */ - -#define MCP25XXFD_REG_CON 0x000 -union mcp25xxfd_con { - struct { - uint32_t DNCNT : 5; /* Device Net Filter Bit Number */ - uint32_t ISOCRCEN : 1; /* Enable ISO CRC in CAN FD Frames */ - uint32_t PXEDIS : 1; /* Protocol Exception Event Detection Disabled */ - uint32_t res0 : 1; - uint32_t WAKFIL : 1; /* Enable CAN Bus Line Wake-up Filter */ - uint32_t WFT : 2; /* Selectable Wake-up Filter Time */ - uint32_t BUSY : 1; /* CAN Module is Busy */ - uint32_t BRSDIS : 1; /* Bit Rate Switching Disable */ - uint32_t res1 : 3; - uint32_t RTXAT : 1; /* Restrict Retransmission Attempts */ - uint32_t ESIGM : 1; /* Transmit ESI in Gateway Mode */ - uint32_t SERR2LOM : 1; /* Transition to Listen Only Mode on System Error */ - uint32_t STEF : 1; /* Store in Transmit Event FIFO */ - uint32_t TXQEN : 1; /* Enable Transmit Queue */ - uint32_t OPMOD : 3; /* Operation Mode Status */ - uint32_t REQMOD : 3; /* Request Operation Mode */ - uint32_t ABAT : 1; /* Abort All Pending Transmissions */ - uint32_t TXBWS : 4; /* Transmit Bandwidth Sharing */ - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_NBTCFG 0x004 -union mcp25xxfd_nbtcfg { - struct { - uint32_t SJW : 7; /* Synchronization Jump Width */ - uint32_t res0 : 1; - uint32_t TSEG2 : 7; /* Time Segment 2 (Phase Segment 2) */ - uint32_t res1 : 1; - uint32_t TSEG1 : 8; /* Time Segment 1 (Propagation Segment + Phase Segment 1) */ - uint32_t BRP : 8; /* Baud Rate Prescaler */ - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_DBTCFG 0x008 -union mcp25xxfd_dbtcfg { - struct { - uint32_t SJW : 4; /* Synchronization Jump Width */ - uint32_t res0 : 4; - uint32_t TSEG2 : 4; /* Time Segment 2 (Phase Segment 2) */ - uint32_t res1 : 4; - uint32_t TSEG1 : 5; /* Time Segment 1 (Propagation Segment + Phase Segment 1) */ - uint32_t res2 : 3; - uint32_t BRP : 8; /* Baud Rate Prescaler */ - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_TDC 0x00C -union mcp25xxfd_tdc { - struct { - uint32_t TDCV : 6; /* Transmitter Delay Compensation Value */ - uint32_t res0 : 2; - uint32_t TDCO : 7; /* Transmitter Delay Compensation Offset */ - uint32_t res1 : 1; - uint32_t TDCMOD : 2; /* Transmitter Delay Compensation Mode */ - uint32_t res2 : 6; - uint32_t SID11EN : 1; /* Enable 12-Bit SID in CAN FD Base Format Messages */ - uint32_t EDGFLTEN : 1; /* Enable Edge Filtering during Bus Integration state */ - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_TSCON 0x014 -union mcp25xxfd_tscon { - struct { - uint32_t TBCPRE : 10; /* Time Base Counter Prescaler */ - uint32_t res0 : 6; - uint32_t TBCEN : 1; /* Time Base Counter Enable */ - uint32_t TSEOF : 1; /* 0: Beginning (See TSREF) / 1: Frame is considered valid */ - uint32_t TSRES : 1; /* Timestamp Sample Point Bit (0: SP of SOF / 1: SP of bit following FDF on FD frames) */ - uint32_t res1 : 13; - }; - uint32_t word; - uint8_t bytes[4]; -}; - -#define MCP25XXFD_REG_VEC 0x018 -union mcp25xxfd_vec { - struct { - uint32_t ICODE : 7; /* Interrupt Flag Code */ - uint32_t res0 : 1; - uint32_t FILHIT : 5; /* Filter Hit Number */ - uint32_t res1 : 3; - uint32_t TXCODE : 7; /* Transmit Interrupt Flag Code */ - uint32_t res2 : 1; - uint32_t RXCODE : 7; /* Receive Interrupt Flag Code */ - uint32_t res3 : 1; - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_INT 0x01C -union mcp25xxfd_int { - struct { - uint32_t TXIF : 1; /* Transmit FIFO Interrupt Flag */ - uint32_t RXIF : 1; /* Receive FIFO Interrupt Flag */ - uint32_t TCBIF : 1; /* Time Base Counter Interrupt Flag */ - uint32_t MODIF : 1; /* Mode Change Interrupt Flag */ - uint32_t TEFIF : 1; /* Transmit Event FIFO Interrupt Flag */ - uint32_t res0 : 3; - uint32_t ECCIF : 1; /* ECC Error Interrupt Flag */ - uint32_t SPICRCIF : 1; /* SPI CRC Error Interrupt Flag */ - uint32_t TXATIF : 1; /* Transmit Attempt Interrupt Flag */ - uint32_t RXOVIF : 1; /* Receive FIFO Overflow Interrupt Flag */ - uint32_t SERRIF : 1; /* System Error Interrupt Flag */ - uint32_t CERRIF : 1; /* CAN Bus Error Interrupt Flag */ - uint32_t WAKIF : 1; /* Bus Wake Up Interrupt Flag */ - uint32_t IVMIF : 1; /* Invalid Message Interrupt Flag */ - uint32_t TXIE : 1; /* Transmit FIFO Interrupt Enable */ - uint32_t RXIE : 1; /* Receive FIFO Interrupt Enable */ - uint32_t TBCIE : 1; /* Time Base Counter Interrupt Enable */ - uint32_t MODIE : 1; /* Mode Change Interrupt Enable */ - uint32_t TEFIE : 1; /* Transmit Event FIFO Interrupt Enable */ - uint32_t res1 : 3; - uint32_t ECCIE : 1; /* ECC Error Interrupt Enable */ - uint32_t SPICRCIE : 1; /* SPI CRC Error Interrupt Enable */ - uint32_t TXATIE : 1; /* Transmit Attempt Interrupt Enable */ - uint32_t RXOVIE : 1; /* Receive FIFO Overflow Interrupt Enable */ - uint32_t SERRIE : 1; /* System Error Interrupt Enable */ - uint32_t CERRIE : 1; /* CAN Bus Error Interrupt Enable */ - uint32_t WAKIE : 1; /* Bus Wake Up Interrupt Enable */ - uint32_t IVMIE : 1; /* Invalid Message Interrupt Enable */ - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_INTREGS MCP25XXFD_REG_VEC -union mcp25xxfd_intregs { - struct { - union mcp25xxfd_vec vec; /* Interrupt Vector Codes */ - union mcp25xxfd_int ints; /* Interrupt Enables/Flags */ - uint32_t rxif; /* FIFO RXIF Interrupt Flags */ - uint32_t txif; /* FIFO TXIF Interrupt Flags */ - uint32_t rxovif; /* FIFO RXOVIF Interrupt Flags */ - uint32_t txatif; /* FIFO TXATIF Interrupt Flags */ - }; - uint32_t words[6]; -}; - -#define MCP25XXFD_REG_TREC 0x034 - -union mcp25xxfd_trec { - struct { - uint32_t REC : 8; /* Receive Error Counter */ - uint32_t TEC : 8; /* Transmit Error Counter */ - uint32_t EWARN : 1; /* Transmitter or Receiver is in Error Warning State */ - uint32_t RXWARN : 1; /* Receiver is in Error Warning State */ - uint32_t TXWARN : 1; /* Transmitter is in Error Warning State */ - uint32_t RXBP : 1; /* Receiver in Error Passive State */ - uint32_t TXBP : 1; /* Transmitter in Error Passive State bit */ - uint32_t TXBO : 1; /* Transmitter in Bus Off State */ - uint32_t res0 : 10; - }; - uint32_t word; - uint8_t bytes[4]; -}; - -#define MCP25XXFD_REG_BDIAG1 0x3C -union mcp25xxfd_bdiag1 { - struct { - uint32_t EFMSGCNT : 16; - uint32_t NBIT0ERR : 1; - uint32_t NBIT1ERR : 1; - uint32_t NACKERR : 1; - uint32_t NFORMERR : 1; - uint32_t NSTUFERR : 1; - uint32_t NCRCERR : 1; - uint32_t res0 : 1; - uint32_t TXBOERR : 1; - uint32_t DBIT0ERR : 1; - uint32_t DBIT1ERR : 1; - uint32_t res1 : 1; - uint32_t DFORMERR : 1; - uint32_t DSTUFERR : 1; - uint32_t DCRCERR : 1; - uint32_t ESI : 1; - uint32_t DLCMM : 1; - }; - uint32_t word; - uint8_t byte[4]; -}; - -/* The FIFOCON, FIFOSTA, and FIFOUA registers are almost identical to their TEF and TXQ counterparts. */ - -#define MCP25XXFD_REG_TEFCON 0x040 -#define MCP25XXFD_REG_TXQCON 0x050 -#define MCP25XXFD_REG_FIFOCON(m) (MCP25XXFD_REG_TXQCON + (m) * 0xC) - -union mcp25xxfd_fifocon { - struct { - uint32_t FNEIE : 1; /* FIFO Not Full/Not Empty Interrupt Enable */ - uint32_t FHIE : 1; /* FIFO Half Empty/Full Interrupt Enable */ - uint32_t FFIE : 1; /* FIFO Empty/Full Interrupt Enable */ - uint32_t OVIE : 1; /* FIFO Overflow Interrupt Enable */ - uint32_t TXATIE : 1; /* FIFO TX Attempts Exhuasted Interrupt Enable */ - uint32_t TSEN : 1; /* FIFO Timestamp Enable */ - uint32_t RTREN : 1; /* FIFO Auto RTR Enable */ - uint32_t TXEN : 1; /* FIFO Transmit Enable */ - uint32_t UINC : 1; /* FIFO Increment Head */ - uint32_t TXREQ : 1; /* FIFO Message Send Request */ - uint32_t FRESET : 1; /* FIFO Reset */ - uint32_t res0 : 5; - uint32_t TXPRI : 5; /* Transmit Priority */ - uint32_t TXAT : 2; /* Retransmission Attempts */ - uint32_t res1 : 1; - uint32_t FSIZE : 5; /* FIFO Size */ - uint32_t PLSIZE : 3; /* Payload Size */ - }; - uint32_t word; - uint8_t bytes[4]; -}; - -#define MCP25XXFD_REG_TEFSTA 0x044 -#define MCP25XXFD_REG_TXQSTA 0x054 -#define MCP25XXFD_REG_FIFOSTA(m) (MCP25XXFD_REG_TXQSTA + (m) * 0xC) - -union mcp25xxfd_fifosta { - struct { - uint32_t FNEIF : 1; /* FIFO Not Full/Not Empty Interrupt Flag */ - uint32_t FHIF : 1; /* FIFO Half Empty/Full Interrupt Flag */ - uint32_t FFIF : 1; /* FIFO Empty/Full Interrupt Flag */ - uint32_t OVIF : 1; /* FIFO Overflow Interrupt Flag */ - uint32_t TXATIF : 1; /* FIFO TX Attempts Exhuasted Interrupt Flag */ - uint32_t TXERR : 1; /* Transmission Error Status */ - uint32_t TXLARB : 1; /* Message Lost Arbitration Status */ - uint32_t TXABT : 1; /* Message Aborted Status */ - uint32_t FIFOCI : 5; /* FIFO Message Index */ - }; - uint32_t word; - uint8_t bytes[4]; -}; - -#define MCP25XXFD_REG_TEFUA 0x048 -#define MCP25XXFD_REG_TXQUA 0x058 -#define MCP25XXFD_REG_FIFOUA(m) (MCP25XXFD_REG_TXQUA + (m) * 0xC) - -union mcp25xxfd_fifo { - struct { - union mcp25xxfd_fifocon con; /* FIFO Control Register */ - union mcp25xxfd_fifosta sta; /* FIFO Status Register */ - uint32_t ua; /* FIFO User Address Register */ - }; - uint32_t words[3]; -}; - -#define MCP21518FD_REG_FLTCON(m) (0x1D0 + m) -union mcp25xxfd_fltcon { - struct { - uint32_t FLTBP : 5; - uint32_t res : 2; - uint32_t FLTEN : 1; - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_FLTOBJ(m) (0x1F0 + ((m) * 8)) -union mcp25xxfd_fltobj { - struct { - uint32_t SID : 11; - uint32_t EID : 18; - uint32_t SID11 : 1; - uint32_t EXIDE : 1; - uint32_t res0 : 1; - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_MASK(m) (0x1F4 + ((m) * 8)) -union mcp25xxfd_mask { - struct { - uint32_t MSID : 11; - uint32_t MEID : 18; - uint32_t MSID11 : 1; - uint32_t MIDE : 1; - uint32_t res0 : 1; - }; - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_OSC 0xE00 -union mcp25xxfd_osc { - struct { - uint32_t PLLEN : 1; /* PLL Enable (0: Clock from XTAL, 1: Clock from 10x PLL) */ - uint32_t res0 : 1; - uint32_t OSCDIS : 1; /* Clock (Oscillator) Disable */ - uint32_t LPMEN : 1; /* Low Power Mode (LPM) Enable */ - uint32_t SCLKDIV : 1; /* System Clock Divisor (0: 1/1, 1: 1/2) */ - uint32_t CLKODIV : 2; /* Clock Output Divisor (0: 1/1, 1: 1/2, 2: 1/4, 3: 1/10) */ - uint32_t res1 : 1; - uint32_t PLLRDY : 1; /* PLL Ready (0: Not Ready, 1: Locked) */ - uint32_t res2 : 1; - uint32_t OSCRDY : 1; /* Clock Ready (0: Not Ready/Off, 1: Running/Stable) */ - uint32_t res3 : 1; - uint32_t SCLKRDY : 1; /* Synchronized SCLKDIV Bit (0: SCLKDIV 0, 1: SCLKDIV 1) */ - uint32_t res4 : 19; - }; - - uint32_t word; - uint8_t byte[4]; -}; - -#define MCP25XXFD_REG_IOCON 0xE04 -union mcp25xxfd_iocon { - struct { - uint32_t TRIS0 : 1; /* GPIO0 Data Direction (0: Output, 1: Input) */ - uint32_t TRIS1 : 1; /* GPIO1 Data Direction (0: Output, 1: Input) */ - uint32_t res0 : 4; - uint32_t XSTBYEN : 1; /* Enable Transiever Standby Pin Control */ - uint32_t res1 : 1; - uint32_t LAT0 : 1; /* GPIO0 Latch (0: Low, 1: High) */ - uint32_t LAT1 : 1; /* GPIO1 Latch (0: Low, 1: High) */ - uint32_t res2 : 6; - uint32_t GPIO0 : 1; /* GPIO0 Status (0: < VIL, 1: > VIH) */ - uint32_t GPIO1 : 1; /* GPIO1 Status (0: < VIL, 1: > VIH) */ - uint32_t res3 : 6; - uint32_t PM0 : 1; /* GPIO0 Pin Mode (0: INT0, 1: GPIO0) */ - uint32_t PM1 : 1; /* GPIO1 Pin Mode (0: INT1, 1: GPIO1) */ - uint32_t res4 : 2; - uint32_t TXCANOD : 1; /* TXCAN Drive Mode (0: Push/Pull, 1: Open Drain) */ - uint32_t SOF : 1; /* Start-Of-Frame Signal (0: Clock on CLKO, 1: SOF on CLKO) */ - uint32_t INTOD : 1; /* Interrupt Pins Drive Mode (0: Push/Pull, 1: Open Drain) */ - uint32_t res5 : 1; - }; - - uint32_t word; - uint8_t byte[4]; -}; - -/* MCP25XXFD Objects */ - -struct mcp25xxfd_txobj { - uint32_t SID : 11; - uint32_t EID : 18; - uint32_t SID11 : 1; - uint32_t res0 : 2; - uint32_t DLC : 4; /* Data Length Code */ - uint32_t IDE : 1; /* Indentifier Extension Flag */ - uint32_t RTR : 1; /* Remote Transmission Request */ - uint32_t BRS : 1; /* Bit Rate Switch Enable */ - uint32_t FDF : 1; /* FD Frame */ - uint32_t ESI : 1; /* Error Status Indicator */ - uint32_t SEQ : 23; - uint8_t DATA[CAN_MAX_DLEN]; -}; - -struct mcp25xxfd_rxobj { - uint32_t SID : 11; - uint32_t EID : 18; - uint32_t SID11 : 1; - uint32_t res0 : 2; - uint32_t DLC : 4; /* Data Length Code */ - uint32_t IDE : 1; /* Indentifier Extension Flag */ - uint32_t RTR : 1; /* Remote Transmission Request */ - uint32_t BRS : 1; /* Bit Rate Switch Enable */ - uint32_t FDF : 1; /* FD Frame */ - uint32_t ESI : 1; /* Error Status Indicator */ - uint32_t res1 : 2; - uint32_t FILHIT : 5; - uint32_t res2 : 16; -#if defined(CONFIG_CAN_RX_TIMESTAMP) - uint32_t RXMSGTS : 32; -#endif - uint8_t DATA[CAN_MAX_DLEN]; -}; - -struct mcp25xxfd_tefobj { - uint32_t SID : 11; - uint32_t EID : 18; - uint32_t SID11 : 1; - uint32_t res0 : 2; - uint32_t DLC : 4; /* Data Length Code */ - uint32_t IDE : 1; /* Indentifier Extension Flag */ - uint32_t RTR : 1; /* Remote Transmission Request */ - uint32_t BRS : 1; /* Bit Rate Switch Enable */ - uint32_t FDF : 1; /* FD Frame */ - uint32_t ESI : 1; /* Error Status Indicator */ - uint32_t SEQ : 23; -}; - -#endif /* ZEPHYR_DRIVERS_CAN_MICROCHIP_MCP25XXFD_H_ */ diff --git a/dts/bindings/can/microchip,mcp251xfd.yaml b/dts/bindings/can/microchip,mcp251xfd.yaml new file mode 100644 index 00000000000..3338f82e02a --- /dev/null +++ b/dts/bindings/can/microchip,mcp251xfd.yaml @@ -0,0 +1,80 @@ +# Copyright (c) 2020 Abram Early +# SPDX-License-Identifier: Apache-2.0 + +description: | + Microchip MCP251XFD SPI CAN-FD controller + + The MCP251XFD node is defined on an SPI bus. An example + configuration is: + + &mikrobus_spi { + cs-gpios = <&mikrobus_header 2 GPIO_ACTIVE_LOW>; + + mcp2518fd_mikroe_mcp2518fd_click: mcp2518fd@0 { + compatible = "microchip,mcp251xfd"; + status = "okay"; + + spi-max-frequency = <18000000>; + int-gpios = <&mikrobus_header 7 GPIO_ACTIVE_LOW>; + reg = <0x0>; + osc-freq = <40000000>; + + bus-speed = <125000>; + sample-point = <875>; + bus-speed-data = <1000000>; + sample-point-data = <875>; + }; + }; + +compatible: "microchip,mcp251xfd" + +include: [spi-device.yaml, can-fd-controller.yaml] + +properties: + osc-freq: + type: int + required: true + description: Frequency of the external oscillator in Hz. + + int-gpios: + type: phandle-array + required: true + description: | + The interrupt signal from the controller is active low in push-pull mode. + The property value should ensure the flags properly describe the signal + that is presented to the driver. + + pll-enable: + type: boolean + description: | + Enables controller PLL, which multiples input clock frequency x10. + This parameter also implicity sets whether the clock is from the PLL + output or directly from the oscillator. + If this option is enabled the clock source is the PLL, otherwise its + the oscillator. + + timestamp-prescaler: + type: int + default: 1 + description: | + Prescaler value for computing the timestamps of received messages. + The timestamp counter is derived from the internal clock divided by this value. + Valid range is [1, 1024]. + + sof-on-clko: + type: boolean + description: | + Output start-of-frame (SOF) signal on the CLKO pin every time + a Start bit of a CAN message is transmitted or received. If this option + is not set, then an internal clock (typically 40MHz or 20MHz) will be + output on CLKO pin instead. + + clko-div: + type: int + description: The factor to divide the system clock for CLKO pin. + default: 10 + enum: + - 1 + - 2 + - 4 + - 10 diff --git a/dts/bindings/can/microchip,mcp25xxfd.yaml b/dts/bindings/can/microchip,mcp25xxfd.yaml deleted file mode 100644 index 037bf32d405..00000000000 --- a/dts/bindings/can/microchip,mcp25xxfd.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright (c) 2020 Abram Early -# SPDX-License-Identifier: Apache-2.0 - -description: MCP25XXFD SPI CAN-FD controller - -compatible: "microchip,mcp25xxfd" - -include: [spi-device.yaml, can-fd-controller.yaml] - -properties: - osc-freq: - type: int - required: true - description: Frequency of the external oscillator - int-gpios: - type: phandle-array - required: true - description: > - Interrupt pin. - - This pin signals active low when produced by the controller. The - property value should ensure the flags properly describe the signal - that is presented to the driver. - reg: - type: array - required: true - sof-on-clko: - type: boolean - required: false - description: Output SOF signal on CLKO pin - clko-div: - type: int - required: false - description: The factor to divide the system clock for CLKO - default: 10 - enum: - - 1 - - 2 - - 4 - - 10 \ No newline at end of file