From 491e831436ac8f03caa0688e3e6fecc85bdbf6e4 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Wed, 22 Jun 2022 16:11:44 +0200 Subject: [PATCH] drivers: can: add NXP SJA1000 common driver backend Add a common driver backend for NXP SJA1000 compatible CAN controllers. Signed-off-by: Henrik Brix Andersen --- drivers/can/CMakeLists.txt | 2 + drivers/can/Kconfig | 2 + drivers/can/Kconfig.sja1000 | 18 + drivers/can/can_sja1000.c | 709 +++++++++++++++++++++++++++++++++ drivers/can/can_sja1000.h | 153 +++++++ drivers/can/can_sja1000_priv.h | 127 ++++++ 6 files changed, 1011 insertions(+) create mode 100644 drivers/can/Kconfig.sja1000 create mode 100644 drivers/can/can_sja1000.c create mode 100644 drivers/can/can_sja1000.h create mode 100644 drivers/can/can_sja1000_priv.h diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index ce0dddf4dcb..23a21bcfa43 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -14,6 +14,8 @@ zephyr_library_sources_ifdef(CONFIG_CAN_STM32FD can_stm32fd.c) zephyr_library_sources_ifdef(CONFIG_CAN_STM32H7 can_stm32h7.c) zephyr_library_sources_ifdef(CONFIG_CAN_RCAR can_rcar.c) +zephyr_library_sources_ifdef(CONFIG_CAN_SJA1000 can_sja1000.c) + zephyr_library_sources_ifdef(CONFIG_USERSPACE can_handlers.c) zephyr_library_sources_ifdef(CONFIG_CAN_SHELL can_shell.c) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 503717c46ce..3aa1286b9a4 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -93,6 +93,8 @@ source "drivers/can/Kconfig.mcan" source "drivers/can/Kconfig.rcar" source "drivers/can/Kconfig.loopback" +source "drivers/can/Kconfig.sja1000" + source "drivers/can/transceiver/Kconfig" endif # CAN diff --git a/drivers/can/Kconfig.sja1000 b/drivers/can/Kconfig.sja1000 new file mode 100644 index 00000000000..97ca0ba2099 --- /dev/null +++ b/drivers/can/Kconfig.sja1000 @@ -0,0 +1,18 @@ +# NXP SJA1000 configuration options + +# Copyright (c) 2022 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +config CAN_SJA1000 + bool + help + This enables support for the shared NXP SJA1000 CAN driver. + +config CAN_MAX_FILTER + int "Maximum number of concurrent active RX filters" + depends on CAN_SJA1000 + default 5 + range 1 32 + help + As the NXP SJA1000 only supports one full-width RX filter, filtering of received CAN + frames are done in software. diff --git a/drivers/can/can_sja1000.c b/drivers/can/can_sja1000.c new file mode 100644 index 00000000000..c8d9a906a4e --- /dev/null +++ b/drivers/can/can_sja1000.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 2022 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "can_sja1000.h" +#include "can_sja1000_priv.h" +#include "can_utils.h" + +#include +#include +#include + +LOG_MODULE_REGISTER(can_sja1000, CONFIG_CAN_LOG_LEVEL); + +/* Timeout for entering/leaving reset mode */ +#define CAN_SJA1000_RESET_MODE_TIMEOUT_USEC 1000 +#define CAN_SJA1000_RESET_MODE_RETRIES 100 +#define CAN_SJA1000_RESET_MODE_DELAY \ + K_USEC(CAN_SJA1000_RESET_MODE_TIMEOUT_USEC / CAN_SJA1000_RESET_MODE_RETRIES) + +static inline void can_sja1000_write_reg(const struct device *dev, uint8_t reg, uint8_t val) +{ + const struct can_sja1000_config *config = dev->config; + + LOG_DBG("write reg %d = 0x%02x", reg, val); + return config->write_reg(dev, reg, val); +} + +static inline uint8_t can_sja1000_read_reg(const struct device *dev, uint8_t reg) +{ + const struct can_sja1000_config *config = dev->config; + uint8_t val; + + val = config->read_reg(dev, reg); + LOG_DBG("read reg %d = 0x%02x", reg, val); + + return val; +} + +static inline int can_sja1000_enter_reset_mode(const struct device *dev) +{ + int retries = CAN_SJA1000_RESET_MODE_RETRIES; + uint8_t mod; + + mod = can_sja1000_read_reg(dev, CAN_SJA1000_MOD); + + while ((mod & CAN_SJA1000_MOD_RM) == 0) { + if (--retries < 0) { + return -EIO; + } + + can_sja1000_write_reg(dev, CAN_SJA1000_MOD, mod | CAN_SJA1000_MOD_RM); + k_sleep(CAN_SJA1000_RESET_MODE_DELAY); + mod = can_sja1000_read_reg(dev, CAN_SJA1000_MOD); + }; + + return 0; +} + +static inline int can_sja1000_leave_reset_mode(const struct device *dev) +{ + int retries = CAN_SJA1000_RESET_MODE_RETRIES; + uint8_t mod; + + mod = can_sja1000_read_reg(dev, CAN_SJA1000_MOD); + + while ((mod & CAN_SJA1000_MOD_RM) == 1) { + if (--retries < 0) { + return -EIO; + } + + can_sja1000_write_reg(dev, CAN_SJA1000_MOD, mod & ~(CAN_SJA1000_MOD_RM)); + k_sleep(CAN_SJA1000_RESET_MODE_DELAY); + mod = can_sja1000_read_reg(dev, CAN_SJA1000_MOD); + }; + + return 0; +} + +int can_sja1000_set_timing(const struct device *dev, const struct can_timing *timing) +{ + struct can_sja1000_data *data = dev->data; + uint8_t btr0; + uint8_t btr1; + uint8_t sjw; + int err; + + __ASSERT_NO_MSG(timing->sjw == CAN_SJW_NO_CHANGE || (timing->sjw >= 1 && timing->sjw <= 4)); + __ASSERT_NO_MSG(timing->prop_seg == 0); + __ASSERT_NO_MSG(timing->phase_seg1 >= 1 && timing->phase_seg1 <= 16); + __ASSERT_NO_MSG(timing->phase_seg2 >= 1 && timing->phase_seg2 <= 8); + __ASSERT_NO_MSG(timing->prescaler >= 1 && timing->prescaler <= 64); + + k_mutex_lock(&data->mod_lock, K_FOREVER); + + err = can_sja1000_enter_reset_mode(dev); + if (err != 0) { + goto unlock; + } + + if (timing->sjw == CAN_SJW_NO_CHANGE) { + sjw = data->sjw; + } else { + sjw = timing->sjw; + data->sjw = timing->sjw; + } + + btr0 = CAN_SJA1000_BTR0_BRP_PREP(timing->prescaler - 1) | + CAN_SJA1000_BTR0_SJW_PREP(sjw - 1); + btr1 = CAN_SJA1000_BTR1_TSEG1_PREP(timing->phase_seg1 - 1) | + CAN_SJA1000_BTR1_TSEG2_PREP(timing->phase_seg2 - 1); + + if ((data->mode & CAN_MODE_3_SAMPLES) != 0) { + btr1 |= CAN_SJA1000_BTR1_SAM; + } + + can_sja1000_write_reg(dev, CAN_SJA1000_BTR0, btr0); + can_sja1000_write_reg(dev, CAN_SJA1000_BTR1, btr1); + + err = can_sja1000_leave_reset_mode(dev); + if (err != 0) { + goto unlock; + } + +unlock: + k_mutex_unlock(&data->mod_lock); + + return err; +} + +int can_sja1000_get_capabilities(const struct device *dev, can_mode_t *cap) +{ + ARG_UNUSED(dev); + + *cap = CAN_MODE_NORMAL | CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | + CAN_MODE_ONE_SHOT | CAN_MODE_3_SAMPLES; + + return 0; +} + +int can_sja1000_set_mode(const struct device *dev, can_mode_t mode) +{ + const struct can_sja1000_config *config = dev->config; + struct can_sja1000_data *data = dev->data; + uint8_t btr1; + uint8_t mod; + int err; + + if ((mode & ~(CAN_MODE_LOOPBACK | CAN_MODE_LISTENONLY | CAN_MODE_ONE_SHOT | + CAN_MODE_3_SAMPLES)) != 0) { + LOG_ERR("unsupported mode: 0x%08x", mode); + return -ENOTSUP; + } + + if (config->phy != NULL) { + err = can_transceiver_enable(config->phy); + if (err != 0) { + LOG_ERR("failed to enable CAN transceiver (err %d)", err); + return err; + } + } + + k_mutex_lock(&data->mod_lock, K_FOREVER); + + err = can_sja1000_enter_reset_mode(dev); + if (err != 0) { + goto unlock; + } + + mod = can_sja1000_read_reg(dev, CAN_SJA1000_MOD); + mod |= CAN_SJA1000_MOD_AFM; + + if ((mode & CAN_MODE_LOOPBACK) != 0) { + /* (Local) self test mode */ + mod |= CAN_SJA1000_MOD_STM; + } else { + mod &= ~(CAN_SJA1000_MOD_STM); + } + + if ((mode & CAN_MODE_LISTENONLY) != 0) { + mod |= CAN_SJA1000_MOD_LOM; + } else { + mod &= ~(CAN_SJA1000_MOD_LOM); + } + + btr1 = can_sja1000_read_reg(dev, CAN_SJA1000_BTR1); + if ((mode & CAN_MODE_3_SAMPLES) != 0) { + btr1 |= CAN_SJA1000_BTR1_SAM; + } else { + btr1 &= ~(CAN_SJA1000_BTR1_SAM); + } + + can_sja1000_write_reg(dev, CAN_SJA1000_MOD, mod); + can_sja1000_write_reg(dev, CAN_SJA1000_BTR1, btr1); + + err = can_sja1000_leave_reset_mode(dev); + if (err != 0) { + goto unlock; + } + + data->mode = mode; +unlock: + k_mutex_unlock(&data->mod_lock); + + return err; +} + +static void can_sja1000_read_frame(const struct device *dev, struct zcan_frame *frame) +{ + uint8_t info; + int i; + + memset(frame, 0, sizeof(*frame)); + + info = can_sja1000_read_reg(dev, CAN_SJA1000_FRAME_INFO); + + if ((info & CAN_SJA1000_FRAME_INFO_RTR) != 0) { + frame->rtr = CAN_REMOTEREQUEST; + } else { + frame->rtr = CAN_DATAFRAME; + } + + frame->dlc = CAN_SJA1000_FRAME_INFO_DLC_GET(info); + if (frame->dlc > CAN_MAX_DLC) { + LOG_ERR("RX frame DLC %u exceeds maximum (%d)", frame->dlc, CAN_MAX_DLC); + return; + } + + if ((info & CAN_SJA1000_FRAME_INFO_FF) != 0) { + frame->id_type = CAN_EXTENDED_IDENTIFIER; + + frame->id = FIELD_PREP(GENMASK(28, 21), + can_sja1000_read_reg(dev, CAN_SJA1000_XFF_ID1)); + frame->id |= FIELD_PREP(GENMASK(20, 13), + can_sja1000_read_reg(dev, CAN_SJA1000_XFF_ID2)); + frame->id |= FIELD_PREP(GENMASK(12, 5), + can_sja1000_read_reg(dev, CAN_SJA1000_EFF_ID3)); + frame->id |= FIELD_PREP(GENMASK(4, 0), + can_sja1000_read_reg(dev, CAN_SJA1000_EFF_ID4) >> 3); + + for (i = 0; i < frame->dlc; i++) { + frame->data[i] = can_sja1000_read_reg(dev, CAN_SJA1000_EFF_DATA + i); + } + } else { + frame->id_type = CAN_STANDARD_IDENTIFIER; + + frame->id = FIELD_PREP(GENMASK(10, 3), + can_sja1000_read_reg(dev, CAN_SJA1000_XFF_ID1)); + frame->id |= FIELD_PREP(GENMASK(2, 0), + can_sja1000_read_reg(dev, CAN_SJA1000_XFF_ID2) >> 5); + + for (i = 0; i < frame->dlc; i++) { + frame->data[i] = can_sja1000_read_reg(dev, CAN_SJA1000_SFF_DATA + i); + } + } +} + +void can_sja1000_write_frame(const struct device *dev, const struct zcan_frame *frame) +{ + uint8_t info; + int i; + + info = CAN_SJA1000_FRAME_INFO_DLC_PREP(frame->dlc); + + if (frame->rtr == CAN_REMOTEREQUEST) { + info |= CAN_SJA1000_FRAME_INFO_RTR; + } + + if (frame->id_type == CAN_EXTENDED_IDENTIFIER) { + info |= CAN_SJA1000_FRAME_INFO_FF; + } + + can_sja1000_write_reg(dev, CAN_SJA1000_FRAME_INFO, info); + + if (frame->id_type == CAN_EXTENDED_IDENTIFIER) { + can_sja1000_write_reg(dev, CAN_SJA1000_XFF_ID1, + FIELD_GET(GENMASK(28, 21), frame->id)); + can_sja1000_write_reg(dev, CAN_SJA1000_XFF_ID2, + FIELD_GET(GENMASK(20, 13), frame->id)); + can_sja1000_write_reg(dev, CAN_SJA1000_EFF_ID3, + FIELD_GET(GENMASK(12, 5), frame->id)); + can_sja1000_write_reg(dev, CAN_SJA1000_EFF_ID4, + FIELD_GET(GENMASK(4, 0), frame->id) << 3); + + for (i = 0; i < frame->dlc; i++) { + can_sja1000_write_reg(dev, CAN_SJA1000_EFF_DATA + i, frame->data[i]); + } + } else { + can_sja1000_write_reg(dev, CAN_SJA1000_XFF_ID1, + FIELD_GET(GENMASK(10, 3), frame->id)); + can_sja1000_write_reg(dev, CAN_SJA1000_XFF_ID2, + FIELD_GET(GENMASK(2, 0), frame->id) << 5); + + for (i = 0; i < frame->dlc; i++) { + can_sja1000_write_reg(dev, CAN_SJA1000_SFF_DATA + i, frame->data[i]); + } + } +} + +int can_sja1000_send(const struct device *dev, const struct zcan_frame *frame, k_timeout_t timeout, + can_tx_callback_t callback, void *user_data) +{ + struct can_sja1000_data *data = dev->data; + uint8_t cmr; + uint8_t sr; + + if (frame->dlc > CAN_MAX_DLC) { + LOG_ERR("TX frame DLC %u exceeds maximum (%d)", frame->dlc, CAN_MAX_DLC); + return -EINVAL; + } + + if (data->state == CAN_BUS_OFF) { + LOG_DBG("transmit failed, bus-off"); + return -ENETDOWN; + } + + if (k_sem_take(&data->tx_idle, timeout) != 0) { + return -EAGAIN; + } + + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + if ((sr & CAN_SJA1000_SR_TBS) == 0) { + LOG_ERR("transmit buffer locked, sr = 0x%02x", sr); + return -EIO; + } + + data->tx_callback = callback; + data->tx_user_data = user_data; + + can_sja1000_write_frame(dev, frame); + + if ((data->mode & CAN_MODE_LOOPBACK) != 0) { + cmr = CAN_SJA1000_CMR_SRR; + } else { + cmr = CAN_SJA1000_CMR_TR; + } + + if ((data->mode & CAN_MODE_ONE_SHOT) != 0) { + cmr |= CAN_SJA1000_CMR_AT; + } + + can_sja1000_write_reg(dev, CAN_SJA1000_CMR, cmr); + + if (callback == NULL) { + k_sem_take(&data->tx_done, K_FOREVER); + return data->tx_status; + } + + return 0; +} + +int can_sja1000_add_rx_filter(const struct device *dev, can_rx_callback_t callback, void *user_data, + const struct zcan_filter *filter) +{ + struct can_sja1000_data *data = dev->data; + int filter_id = -ENOSPC; + int i; + + for (i = 0; i < ARRAY_SIZE(data->filters); i++) { + if (!atomic_test_and_set_bit(data->rx_allocs, i)) { + filter_id = i; + break; + } + } + + if (filter_id >= 0) { + data->filters[filter_id].filter = *filter; + data->filters[filter_id].user_data = user_data; + data->filters[filter_id].callback = callback; + } + + return filter_id; +} + +void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id) +{ + struct can_sja1000_data *data = dev->data; + + if (filter_id < 0 || filter_id >= ARRAY_SIZE(data->filters)) { + LOG_ERR("filter ID %d out of bounds", filter_id); + return; + } + + if (atomic_test_and_clear_bit(data->rx_allocs, filter_id)) { + data->filters[filter_id].callback = NULL; + data->filters[filter_id].user_data = NULL; + data->filters[filter_id].filter = (struct zcan_filter){0}; + } +} + +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +int can_sja1000_recover(const struct device *dev, k_timeout_t timeout) +{ + struct can_sja1000_data *data = dev->data; + int64_t start_ticks; + uint8_t sr; + int err; + + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + if ((sr & CAN_SJA1000_SR_BS) == 0) { + return 0; + } + + start_ticks = k_uptime_ticks(); + + err = k_mutex_lock(&data->mod_lock, timeout); + if (err != 0) { + LOG_WRN("failed to acquire MOD lock"); + return err; + } + + err = can_sja1000_leave_reset_mode(dev); + if (err != 0) { + LOG_ERR("failed to initiate bus recovery"); + k_mutex_unlock(&data->mod_lock); + return err; + } + + k_mutex_unlock(&data->mod_lock); + + while ((sr & CAN_SJA1000_SR_BS) != 0) { + if (k_uptime_ticks() - start_ticks > timeout.ticks) { + LOG_WRN("bus recovery timed out"); + return -EAGAIN; + } + + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + } + + return 0; +} +#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + +int can_sja1000_get_state(const struct device *dev, enum can_state *state, + struct can_bus_err_cnt *err_cnt) +{ + struct can_sja1000_data *data = dev->data; + + if (state != NULL) { + *state = data->state; + } + + if (err_cnt != NULL) { + err_cnt->rx_err_cnt = can_sja1000_read_reg(dev, CAN_SJA1000_RXERR); + err_cnt->tx_err_cnt = can_sja1000_read_reg(dev, CAN_SJA1000_TXERR); + } + + return 0; +} + +void can_sja1000_set_state_change_callback(const struct device *dev, + can_state_change_callback_t callback, void *user_data) +{ + struct can_sja1000_data *data = dev->data; + + data->state_change_cb = callback; + data->state_change_cb_data = user_data; +} + +int can_sja1000_get_max_filters(const struct device *dev, enum can_ide id_type) +{ + ARG_UNUSED(dev); + ARG_UNUSED(id_type); + + return CONFIG_CAN_MAX_FILTER; +} + +int can_sja1000_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate) +{ + const struct can_sja1000_config *config = dev->config; + + *max_bitrate = config->max_bitrate; + + return 0; +} + +static void can_sja1000_handle_receive_irq(const struct device *dev) +{ + struct can_sja1000_data *data = dev->data; + struct zcan_frame frame; + can_rx_callback_t callback; + uint8_t sr; + int i; + + do { + can_sja1000_read_frame(dev, &frame); + + for (i = 0; i < ARRAY_SIZE(data->filters); i++) { + if (!atomic_test_bit(data->rx_allocs, i)) { + continue; + } + + if (can_utils_filter_match(&frame, &data->filters[i].filter)) { + callback = data->filters[i].callback; + if (callback != NULL) { + callback(dev, &frame, data->filters[i].user_data); + } + } + } + + can_sja1000_write_reg(dev, CAN_SJA1000_CMR, CAN_SJA1000_CMR_RRB); + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + } while ((sr & CAN_SJA1000_SR_RBS) != 0); +} + +static void can_sja1000_tx_done(const struct device *dev, int status) +{ + struct can_sja1000_data *data = dev->data; + can_tx_callback_t callback = data->tx_callback; + void *user_data = data->tx_user_data; + + if (callback != NULL) { + data->tx_callback = NULL; + callback(dev, status, user_data); + } else { + data->tx_status = status; + k_sem_give(&data->tx_done); + } + + k_sem_give(&data->tx_idle); +} + +static void can_sja1000_handle_transmit_irq(const struct device *dev) +{ + int status = 0; + uint8_t sr; + + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + if ((sr & CAN_SJA1000_SR_TCS) == 0) { + status = -EIO; + } + + can_sja1000_tx_done(dev, status); +} + +static void can_sja1000_handle_error_warning_irq(const struct device *dev) +{ + struct can_sja1000_data *data = dev->data; + uint8_t sr; + int err; + + sr = can_sja1000_read_reg(dev, CAN_SJA1000_SR); + if ((sr & CAN_SJA1000_SR_BS) != 0) { + data->state = CAN_BUS_OFF; + can_sja1000_tx_done(dev, -ENETDOWN); +#ifdef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + /* Recover bus now unless interrupted in the middle of a MOD register change. */ + err = k_mutex_lock(&data->mod_lock, K_NO_WAIT); + if (err == 0) { + (void)can_sja1000_leave_reset_mode(dev); + k_mutex_unlock(&data->mod_lock); + } +#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + } else if ((sr & CAN_SJA1000_SR_ES) != 0) { + data->state = CAN_ERROR_WARNING; + } else { + data->state = CAN_ERROR_ACTIVE; + } +} + +static void can_sja1000_handle_error_passive_irq(const struct device *dev) +{ + struct can_sja1000_data *data = dev->data; + + if (data->state == CAN_ERROR_PASSIVE) { + data->state = CAN_ERROR_WARNING; + } else { + data->state = CAN_ERROR_PASSIVE; + } +} + +void can_sja1000_isr(const struct device *dev) +{ + struct can_sja1000_data *data = dev->data; + const can_state_change_callback_t cb = data->state_change_cb; + void *cb_data = data->state_change_cb_data; + enum can_state prev_state = data->state; + struct can_bus_err_cnt err_cnt; + uint8_t ir; + + ir = can_sja1000_read_reg(dev, CAN_SJA1000_IR); + + if ((ir & CAN_SJA1000_IR_TI) != 0) { + can_sja1000_handle_transmit_irq(dev); + } + + if ((ir & CAN_SJA1000_IR_RI) != 0) { + can_sja1000_handle_receive_irq(dev); + } + + if ((ir & CAN_SJA1000_IR_EI) != 0) { + can_sja1000_handle_error_warning_irq(dev); + } + + if ((ir & CAN_SJA1000_IR_EPI) != 0) { + can_sja1000_handle_error_passive_irq(dev); + } + + if (prev_state != data->state && cb != NULL) { + err_cnt.rx_err_cnt = can_sja1000_read_reg(dev, CAN_SJA1000_RXERR); + err_cnt.tx_err_cnt = can_sja1000_read_reg(dev, CAN_SJA1000_TXERR); + cb(dev, data->state, err_cnt, cb_data); + } +} + +int can_sja1000_init(const struct device *dev) +{ + const struct can_sja1000_config *config = dev->config; + struct can_sja1000_data *data = dev->data; + struct can_timing timing; + int err; + + __ASSERT_NO_MSG(config->read_reg != NULL); + __ASSERT_NO_MSG(config->write_reg != NULL); + + if (config->phy != NULL) { + if (!device_is_ready(config->phy)) { + LOG_ERR("CAN transceiver not ready"); + return -ENODEV; + } + } + + k_mutex_init(&data->mod_lock); + k_sem_init(&data->tx_idle, 1, 1); + k_sem_init(&data->tx_done, 0, 1); + + data->state = CAN_ERROR_ACTIVE; + + /* See NXP SJA1000 Application Note AN97076 (figure 12) for initialization sequence */ + + /* Enter reset mode */ + err = can_sja1000_enter_reset_mode(dev); + if (err != 0) { + return err; + } + + /* Set PeliCAN mode */ + can_sja1000_write_reg(dev, CAN_SJA1000_CDR, config->cdr | CAN_SJA1000_CDR_CAN_MODE); + + /* Set up acceptance code and mask to match any frame (software filtering) */ + can_sja1000_write_reg(dev, CAN_SJA1000_ACR0, 0x00); + can_sja1000_write_reg(dev, CAN_SJA1000_ACR1, 0x00); + can_sja1000_write_reg(dev, CAN_SJA1000_ACR2, 0x00); + can_sja1000_write_reg(dev, CAN_SJA1000_ACR3, 0x00); + + can_sja1000_write_reg(dev, CAN_SJA1000_AMR0, 0xFF); + can_sja1000_write_reg(dev, CAN_SJA1000_AMR1, 0xFF); + can_sja1000_write_reg(dev, CAN_SJA1000_AMR2, 0xFF); + can_sja1000_write_reg(dev, CAN_SJA1000_AMR3, 0xFF); + + /* Calculate initial timing parameters */ + data->sjw = config->sjw; + timing.sjw = CAN_SJW_NO_CHANGE; + + if (config->sample_point != 0) { + err = can_calc_timing(dev, &timing, config->bitrate, config->sample_point); + if (err == -EINVAL) { + LOG_ERR("bitrate/sample point cannot be met (err %d)", err); + return err; + } + + LOG_DBG("initial sample point error: %d", err); + } else { + timing.prop_seg = 0; + timing.phase_seg1 = config->phase_seg1; + timing.phase_seg2 = config->phase_seg2; + + err = can_calc_prescaler(dev, &timing, config->bitrate); + if (err != 0) { + LOG_WRN("initial bitrate error: %d", err); + } + } + + /* Configure timing */ + err = can_sja1000_set_timing(dev, &timing); + if (err != 0) { + LOG_ERR("timing parameters cannot be met (err %d)", err); + return err; + } + + /* Set output control */ + can_sja1000_write_reg(dev, CAN_SJA1000_OCR, config->ocr); + + /* Clear error counters */ + can_sja1000_write_reg(dev, CAN_SJA1000_RXERR, 0); + can_sja1000_write_reg(dev, CAN_SJA1000_TXERR, 0); + + /* Clear error capture */ + (void)can_sja1000_read_reg(dev, CAN_SJA1000_ECC); + + /* Set error warning limit */ + can_sja1000_write_reg(dev, CAN_SJA1000_EWLR, 96); + + /* Enter normal mode */ + data->mode = CAN_MODE_NORMAL; + err = can_sja1000_set_mode(dev, CAN_MODE_NORMAL); + if (err != 0) { + return err; + } + + /* Enable interrupts */ + can_sja1000_write_reg(dev, CAN_SJA1000_IER, + CAN_SJA1000_IER_RIE | CAN_SJA1000_IER_TIE | + CAN_SJA1000_IER_EIE | CAN_SJA1000_IER_EPIE); + + return 0; +} diff --git a/drivers/can/can_sja1000.h b/drivers/can/can_sja1000.h new file mode 100644 index 00000000000..8c07c1b0d3d --- /dev/null +++ b/drivers/can/can_sja1000.h @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2022 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_CAN_SJA1000_H_ +#define ZEPHYR_DRIVERS_CAN_SJA1000_H_ + +#include + +/* Output Control Register (OCR) bits */ +#define CAN_SJA1000_OCR_OCMODE_MASK GENMASK(1, 0) +#define CAN_SJA1000_OCR_OCPOL0 BIT(2) +#define CAN_SJA1000_OCR_OCTN0 BIT(3) +#define CAN_SJA1000_OCR_OCTP0 BIT(4) +#define CAN_SJA1000_OCR_OCPOL1 BIT(5) +#define CAN_SJA1000_OCR_OCTN1 BIT(6) +#define CAN_SJA1000_OCR_OCTP1 BIT(7) + +#define CAN_SJA1000_OCR_OCMODE_BIPHASE FIELD_PREP(CAN_SJA1000_OCR_OCMODE_MASK, 0U) +#define CAN_SJA1000_OCR_OCMODE_TEST FIELD_PREP(CAN_SJA1000_OCR_OCMODE_MASK, 1U) +#define CAN_SJA1000_OCR_OCMODE_NORMAL FIELD_PREP(CAN_SJA1000_OCR_OCMODE_MASK, 2U) +#define CAN_SJA1000_OCR_OCMODE_CLOCK FIELD_PREP(CAN_SJA1000_OCR_OCMODE_MASK, 3U) + +/* Clock Divider Register (CDR) bits */ +#define CAN_SJA1000_CDR_CD_MASK GENMASK(2, 0) +#define CAN_SJA1000_CDR_CLOCK_OFF BIT(3) +#define CAN_SJA1000_CDR_RXINTEN BIT(5) +#define CAN_SJA1000_CDR_CBP BIT(6) +#define CAN_SJA1000_CDR_CAN_MODE BIT(7) + +#define CAN_SJA1000_CDR_CD_DIV1 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 7U) +#define CAN_SJA1000_CDR_CD_DIV2 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 0U) +#define CAN_SJA1000_CDR_CD_DIV4 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 1U) +#define CAN_SJA1000_CDR_CD_DIV6 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 2U) +#define CAN_SJA1000_CDR_CD_DIV8 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 3U) +#define CAN_SJA1000_CDR_CD_DIV10 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 4U) +#define CAN_SJA1000_CDR_CD_DIV12 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 5U) +#define CAN_SJA1000_CDR_CD_DIV14 FIELD_PREP(CAN_SJA1000_CDR_CD_MASK, 6U) + +#define CAN_SJA1000_TIMING_MIN_INITIALIZER \ + { \ + .sjw = 1, \ + .prop_seg = 0, \ + .phase_seg1 = 1, \ + .phase_seg2 = 1, \ + .prescaler = 1 \ + } + +#define CAN_SJA1000_TIMING_MAX_INITIALIZER \ + { \ + .sjw = 4, \ + .prop_seg = 0, \ + .phase_seg1 = 16, \ + .phase_seg2 = 8, \ + .prescaler = 64 \ + } + +typedef void (*can_sja1000_write_reg_t)(const struct device *dev, uint8_t reg, uint8_t val); + +typedef uint8_t (*can_sja1000_read_reg_t)(const struct device *dev, uint8_t reg); + +struct can_sja1000_config { + can_sja1000_read_reg_t read_reg; + can_sja1000_write_reg_t write_reg; + uint32_t bitrate; + uint32_t sample_point; + uint32_t sjw; + uint32_t phase_seg1; + uint32_t phase_seg2; + const struct device *phy; + uint32_t max_bitrate; + uint8_t ocr; + uint8_t cdr; + const void *custom; +}; + +#define CAN_SJA1000_DT_CONFIG_GET(node_id, _custom, _read_reg, _write_reg, _ocr, _cdr) \ + { \ + .read_reg = _read_reg, .write_reg = _write_reg, \ + .bitrate = DT_PROP(node_id, bus_speed), .sjw = DT_PROP(node_id, sjw), \ + .phase_seg1 = DT_PROP_OR(node_id, phase_seg1, 0), \ + .phase_seg2 = DT_PROP_OR(node_id, phase_seg2, 0), \ + .sample_point = DT_PROP_OR(node_id, sample_point, 0), \ + .max_bitrate = DT_CAN_TRANSCEIVER_MAX_BITRATE(node_id, 1000000), .ocr = _ocr, \ + .cdr = _cdr, .custom = _custom, \ + } + +#define CAN_SJA1000_DT_CONFIG_INST_GET(inst, _custom, _read_reg, _write_reg, _ocr, _cdr) \ + CAN_SJA1000_DT_CONFIG_GET(DT_DRV_INST(inst), _custom, _read_reg, _write_reg, _ocr, _cdr) + +struct can_sja1000_rx_filter { + struct zcan_filter filter; + can_rx_callback_t callback; + void *user_data; +}; + +struct can_sja1000_data { + ATOMIC_DEFINE(rx_allocs, CONFIG_CAN_MAX_FILTER); + struct can_sja1000_rx_filter filters[CONFIG_CAN_MAX_FILTER]; + struct k_mutex mod_lock; + can_mode_t mode; + enum can_state state; + can_state_change_callback_t state_change_cb; + void *state_change_cb_data; + struct k_sem tx_idle; + struct k_sem tx_done; + can_tx_callback_t tx_callback; + void *tx_user_data; + int tx_status; + uint32_t sjw; + void *custom; +}; + +#define CAN_SJA1000_DATA_INITIALIZER(_custom) \ + { \ + .custom = _custom, \ + } + +int can_sja1000_set_timing(const struct device *dev, const struct can_timing *timing); + +int can_sja1000_get_capabilities(const struct device *dev, can_mode_t *cap); + +int can_sja1000_set_mode(const struct device *dev, can_mode_t mode); + +int can_sja1000_send(const struct device *dev, const struct zcan_frame *frame, k_timeout_t timeout, + can_tx_callback_t callback, void *user_data); + +int can_sja1000_add_rx_filter(const struct device *dev, can_rx_callback_t callback, void *user_data, + const struct zcan_filter *filter); + +void can_sja1000_remove_rx_filter(const struct device *dev, int filter_id); + +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY +int can_sja1000_recover(const struct device *dev, k_timeout_t timeout); +#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + +int can_sja1000_get_state(const struct device *dev, enum can_state *state, + struct can_bus_err_cnt *err_cnt); + +void can_sja1000_set_state_change_callback(const struct device *dev, + can_state_change_callback_t callback, void *user_data); + +int can_sja1000_get_max_filters(const struct device *dev, enum can_ide id_type); + +int can_sja1000_get_max_bitrate(const struct device *dev, uint32_t *max_bitrate); + +void can_sja1000_isr(const struct device *dev); + +int can_sja1000_init(const struct device *dev); + +#endif /* ZEPHYR_DRIVERS_CAN_SJA1000_H_ */ diff --git a/drivers/can/can_sja1000_priv.h b/drivers/can/can_sja1000_priv.h new file mode 100644 index 00000000000..03d5dbf0f0d --- /dev/null +++ b/drivers/can/can_sja1000_priv.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2022 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_CAN_SJA1000_PRIV_H_ +#define ZEPHYR_DRIVERS_CAN_SJA1000_PRIV_H_ + +#include + +/* SJA1000 register "CAN addresses", PeliCAN mode */ +#define CAN_SJA1000_MOD (0U) +#define CAN_SJA1000_CMR (1U) +#define CAN_SJA1000_SR (2U) +#define CAN_SJA1000_IR (3U) +#define CAN_SJA1000_IER (4U) +#define CAN_SJA1000_BTR0 (6U) +#define CAN_SJA1000_BTR1 (7U) +#define CAN_SJA1000_OCR (8U) +#define CAN_SJA1000_ALC (11U) +#define CAN_SJA1000_ECC (12U) +#define CAN_SJA1000_EWLR (13U) +#define CAN_SJA1000_RXERR (14U) +#define CAN_SJA1000_TXERR (15U) + +/* Reset Mode access (acceptance codes/masks) */ +#define CAN_SJA1000_ACR0 (16U) +#define CAN_SJA1000_ACR1 (17U) +#define CAN_SJA1000_ACR2 (18U) +#define CAN_SJA1000_ACR3 (19U) +#define CAN_SJA1000_AMR0 (20U) +#define CAN_SJA1000_AMR1 (21U) +#define CAN_SJA1000_AMR2 (22U) +#define CAN_SJA1000_AMR3 (23U) + +/* Operation Mode access (RX/TX SFF/EFF frame) */ +#define CAN_SJA1000_FRAME_INFO (16U) +#define CAN_SJA1000_XFF_ID1 (17U) +#define CAN_SJA1000_XFF_ID2 (18U) +#define CAN_SJA1000_EFF_ID3 (19U) +#define CAN_SJA1000_EFF_ID4 (20U) +#define CAN_SJA1000_SFF_DATA (19U) +#define CAN_SJA1000_EFF_DATA (21U) + +#define CAN_SJA1000_RMC (29U) +#define CAN_SJA1000_RBSA (30U) +#define CAN_SJA1000_CDR (31U) + +/* Mode register (MOD) bits */ +#define CAN_SJA1000_MOD_RM BIT(0) +#define CAN_SJA1000_MOD_LOM BIT(1) +#define CAN_SJA1000_MOD_STM BIT(2) +#define CAN_SJA1000_MOD_AFM BIT(3) +#define CAN_SJA1000_MOD_SM BIT(4) + +/* Command Register (CMR) bits */ +#define CAN_SJA1000_CMR_TR BIT(0) +#define CAN_SJA1000_CMR_AT BIT(1) +#define CAN_SJA1000_CMR_RRB BIT(2) +#define CAN_SJA1000_CMR_CDO BIT(3) +#define CAN_SJA1000_CMR_SRR BIT(4) + +/* Status Register (SR) bits */ +#define CAN_SJA1000_SR_RBS BIT(0) +#define CAN_SJA1000_SR_DOS BIT(1) +#define CAN_SJA1000_SR_TBS BIT(2) +#define CAN_SJA1000_SR_TCS BIT(3) +#define CAN_SJA1000_SR_RS BIT(4) +#define CAN_SJA1000_SR_TS BIT(5) +#define CAN_SJA1000_SR_ES BIT(6) +#define CAN_SJA1000_SR_BS BIT(7) + +/* Interrupt Register (IR) bits */ +#define CAN_SJA1000_IR_RI BIT(0) +#define CAN_SJA1000_IR_TI BIT(1) +#define CAN_SJA1000_IR_EI BIT(2) +#define CAN_SJA1000_IR_DOI BIT(3) +#define CAN_SJA1000_IR_WUI BIT(4) +#define CAN_SJA1000_IR_EPI BIT(5) +#define CAN_SJA1000_IR_ALI BIT(6) +#define CAN_SJA1000_IR_BEI BIT(7) + +/* Interrupt Enable Register (IER) bits */ +#define CAN_SJA1000_IER_RIE BIT(0) +#define CAN_SJA1000_IER_TIE BIT(1) +#define CAN_SJA1000_IER_EIE BIT(2) +#define CAN_SJA1000_IER_DOIE BIT(3) +#define CAN_SJA1000_IER_WUIE BIT(4) +#define CAN_SJA1000_IER_EPIE BIT(5) +#define CAN_SJA1000_IER_ALIE BIT(6) +#define CAN_SJA1000_IER_BEIE BIT(7) + +/* Bus Timing Register 0 (BTR0) bits */ +#define CAN_SJA1000_BTR0_BRP_MASK GENMASK(5, 0) +#define CAN_SJA1000_BTR0_SJW_MASK GENMASK(7, 6) + +#define CAN_SJA1000_BTR0_BRP_PREP(brp) FIELD_PREP(CAN_SJA1000_BTR0_BRP_MASK, brp) +#define CAN_SJA1000_BTR0_SJW_PREP(sjw) FIELD_PREP(CAN_SJA1000_BTR0_SJW_MASK, sjw) + +/* Bus Timing Register 1 (BTR1) bits */ +#define CAN_SJA1000_BTR1_TSEG1_MASK GENMASK(3, 0) +#define CAN_SJA1000_BTR1_TSEG2_MASK GENMASK(6, 4) +#define CAN_SJA1000_BTR1_SAM BIT(7) + +#define CAN_SJA1000_BTR1_TSEG1_PREP(tseg1) FIELD_PREP(CAN_SJA1000_BTR1_TSEG1_MASK, tseg1) +#define CAN_SJA1000_BTR1_TSEG2_PREP(tseg2) FIELD_PREP(CAN_SJA1000_BTR1_TSEG2_MASK, tseg2) + +/* Error Code Capture register (ECC) bits */ +#define CAN_SJA1000_ECC_SEG_MASK GENMASK(4, 0) +#define CAN_SJA1000_ECC_DIR BIT(5) +#define CAN_SJA1000_ECC_ERRC_MASK GENMASK(7, 6) + +#define CAN_SJA1000_ECC_ERRC_BIT_ERROR FIELD_PREP(CAN_SJA1000_ECC_ERRC_MASK, 0U) +#define CAN_SJA1000_ECC_ERRC_FORM_ERROR FIELD_PREP(CAN_SJA1000_ECC_ERRC_MASK, 1U) +#define CAN_SJA1000_ECC_ERRC_STUFF_ERROR FIELD_PREP(CAN_SJA1000_ECC_ERRC_MASK, 2U) +#define CAN_SJA1000_ECC_ERRC_OTHER_ERROR FIELD_PREP(CAN_SJA1000_ECC_ERRC_MASK, 3U) + +/* RX/TX SFF/EFF Frame Information bits */ +#define CAN_SJA1000_FRAME_INFO_DLC_MASK GENMASK(3, 0) +#define CAN_SJA1000_FRAME_INFO_RTR BIT(6) +#define CAN_SJA1000_FRAME_INFO_FF BIT(7) + +#define CAN_SJA1000_FRAME_INFO_DLC_PREP(dlc) FIELD_PREP(CAN_SJA1000_FRAME_INFO_DLC_MASK, dlc) +#define CAN_SJA1000_FRAME_INFO_DLC_GET(info) FIELD_GET(CAN_SJA1000_FRAME_INFO_DLC_MASK, info) + +#endif /* ZEPHYR_DRIVERS_CAN_SJA1000_PRIV_H_ */