diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 23a21bcfa43..3d41b6585de 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -15,6 +15,7 @@ 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_CAN_ESP32_TWAI can_esp32_twai.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 3aa1286b9a4..494faa805eb 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -94,6 +94,7 @@ source "drivers/can/Kconfig.rcar" source "drivers/can/Kconfig.loopback" source "drivers/can/Kconfig.sja1000" +source "drivers/can/Kconfig.esp32" source "drivers/can/transceiver/Kconfig" diff --git a/drivers/can/Kconfig.esp32 b/drivers/can/Kconfig.esp32 new file mode 100644 index 00000000000..e1b30109657 --- /dev/null +++ b/drivers/can/Kconfig.esp32 @@ -0,0 +1,13 @@ +# Espressif ESP32 TWAI configuration options + +# Copyright (c) 2022 Henrik Brix Andersen +# SPDX-License-Identifier: Apache-2.0 + +config CAN_ESP32_TWAI + bool "Espressif TWAI CAN driver" + default y + depends on DT_HAS_ESPRESSIF_ESP32_TWAI_ENABLED + select CAN_SJA1000 + help + This enables support for the Espressif Two-Wire Automotive Interface + (TWAI) CAN driver. diff --git a/drivers/can/can_esp32_twai.c b/drivers/can/can_esp32_twai.c new file mode 100644 index 00000000000..f9385059328 --- /dev/null +++ b/drivers/can/can_esp32_twai.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2022 Henrik Brix Andersen + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT espressif_esp32_twai + +#include "can_sja1000.h" + +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_REGISTER(can_esp32_twai, CONFIG_CAN_LOG_LEVEL); + +struct can_esp32_twai_config { + mm_reg_t base; + const struct pinctrl_dev_config *pcfg; + const struct device *clock_dev; + const clock_control_subsys_t clock_subsys; + int irq_source; +}; + +static uint8_t can_esp32_twai_read_reg(const struct device *dev, uint8_t reg) +{ + const struct can_sja1000_config *sja1000_config = dev->config; + const struct can_esp32_twai_config *twai_config = sja1000_config->custom; + mm_reg_t addr = twai_config->base + reg * sizeof(uint32_t); + + return sys_read32(addr) & 0xFF; +} + +static void can_esp32_twai_write_reg(const struct device *dev, uint8_t reg, uint8_t val) +{ + const struct can_sja1000_config *sja1000_config = dev->config; + const struct can_esp32_twai_config *twai_config = sja1000_config->custom; + mm_reg_t addr = twai_config->base + reg * sizeof(uint32_t); + + sys_write32(val & 0xFF, addr); +} + +static int can_esp32_twai_get_core_clock(const struct device *dev, uint32_t *rate) +{ + ARG_UNUSED(dev); + + /* The internal clock operates at half of the oscillator frequency */ + *rate = APB_CLK_FREQ / 2; + + return 0; +} + +static void IRAM_ATTR can_esp32_twai_isr(void *arg) +{ + const struct device *dev = (const struct device *)arg; + + can_sja1000_isr(dev); +} + +static int can_esp32_twai_init(const struct device *dev) +{ + const struct can_sja1000_config *sja1000_config = dev->config; + const struct can_esp32_twai_config *twai_config = sja1000_config->custom; + int err; + + err = pinctrl_apply_state(twai_config->pcfg, PINCTRL_STATE_DEFAULT); + if (err != 0) { + LOG_ERR("failed to configure TWAI pins (err %d)", err); + return err; + } + + clock_control_on(twai_config->clock_dev, twai_config->clock_subsys); + + err = can_sja1000_init(dev); + if (err != 0) { + LOG_ERR("failed to initialize controller (err %d)", err); + return err; + } + + esp_intr_alloc(twai_config->irq_source, 0, can_esp32_twai_isr, (void *)dev, NULL); + + return 0; +} + +const struct can_driver_api can_esp32_twai_driver_api = { + .get_capabilities = can_sja1000_get_capabilities, + .set_mode = can_sja1000_set_mode, + .set_timing = can_sja1000_set_timing, + .send = can_sja1000_send, + .add_rx_filter = can_sja1000_add_rx_filter, + .remove_rx_filter = can_sja1000_remove_rx_filter, + .get_state = can_sja1000_get_state, + .set_state_change_callback = can_sja1000_set_state_change_callback, + .get_core_clock = can_esp32_twai_get_core_clock, + .get_max_filters = can_sja1000_get_max_filters, + .get_max_bitrate = can_sja1000_get_max_bitrate, +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + .recover = can_sja1000_recover, +#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, + .timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, +}; + +#define CAN_ESP32_TWAI_DT_CDR_INST_GET(inst) \ + COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clkout_divider), \ + (UTIL_CAT(CAN_SJA1000_CDR_CD_DIV, DT_INST_PROP(inst, clkout_divider))), \ + (CAN_SJA1000_CDR_CLOCK_OFF)) + +#define CAN_ESP32_TWAI_INIT(inst) \ + PINCTRL_DT_INST_DEFINE(inst); \ + \ + static const struct can_esp32_twai_config can_esp32_twai_config_##inst = { \ + .base = DT_INST_REG_ADDR(inst), \ + .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(inst)), \ + .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(inst, offset), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .irq_source = DT_INST_IRQN(inst), \ + }; \ + static const struct can_sja1000_config can_sja1000_config_##inst = \ + CAN_SJA1000_DT_CONFIG_INST_GET(inst, &can_esp32_twai_config_##inst, \ + can_esp32_twai_read_reg, can_esp32_twai_write_reg, \ + CAN_SJA1000_OCR_OCMODE_BIPHASE, \ + CAN_ESP32_TWAI_DT_CDR_INST_GET(inst)); \ + \ + static struct can_sja1000_data can_sja1000_data_##inst = \ + CAN_SJA1000_DATA_INITIALIZER(NULL); \ + \ + DEVICE_DT_INST_DEFINE(inst, can_esp32_twai_init, NULL, &can_sja1000_data_##inst, \ + &can_sja1000_config_##inst, POST_KERNEL, \ + CONFIG_CAN_INIT_PRIORITY, &can_esp32_twai_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(CAN_ESP32_TWAI_INIT)