From 2a035f775ba1ac6399e2fc3ac05829f571f76a65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20J=C3=A4ger?= Date: Thu, 6 Oct 2022 13:46:54 +0200 Subject: [PATCH] drivers: can: esp32_twai: add support for newer MCUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer ESP32 series MCUs like the ESP32-C3 contain some register changes incompatible to the original ESP32 and the SJA1000. The additions in this commit consider these changes and fix the incompatibilities in the TWAI front-end for the SJA1000 driver. Signed-off-by: Martin Jäger --- drivers/can/can_esp32_twai.c | 167 ++++++++++++++++++++- dts/bindings/can/espressif,esp32-twai.yaml | 12 +- 2 files changed, 167 insertions(+), 12 deletions(-) diff --git a/drivers/can/can_esp32_twai.c b/drivers/can/can_esp32_twai.c index 6648750f2c..06595e57e8 100644 --- a/drivers/can/can_esp32_twai.c +++ b/drivers/can/can_esp32_twai.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2022 Henrik Brix Andersen + * Copyright (c) 2022 Martin Jäger * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,12 +19,62 @@ LOG_MODULE_REGISTER(can_esp32_twai, CONFIG_CAN_LOG_LEVEL); +/* + * Newer ESP32-series MCUs like ESP32-C3 and ESP32-S2 have some slightly different registers + * compared to the original ESP32, which is fully compatible with the SJA1000 controller. + * + * The names with TWAI_ prefixes from Espressif reference manuals are used for these incompatible + * registers. + */ +#ifndef CONFIG_SOC_ESP32 + +/* TWAI_BUS_TIMING_0_REG is incompatible with CAN_SJA1000_BTR0 */ +#define TWAI_BUS_TIMING_0_REG (6U) +#define TWAI_BAUD_PRESC_MASK GENMASK(12, 0) +#define TWAI_SYNC_JUMP_WIDTH_MASK GENMASK(15, 14) +#define TWAI_BAUD_PRESC_PREP(brp) FIELD_PREP(TWAI_BAUD_PRESC_MASK, brp) +#define TWAI_SYNC_JUMP_WIDTH_PREP(sjw) FIELD_PREP(TWAI_SYNC_JUMP_WIDTH_MASK, sjw) + +/* + * TWAI_BUS_TIMING_1_REG is compatible with CAN_SJA1000_BTR1, but needed here for the custom + * set_timing() function. + */ +#define TWAI_BUS_TIMING_1_REG (7U) +#define TWAI_TIME_SEG1_MASK GENMASK(3, 0) +#define TWAI_TIME_SEG2_MASK GENMASK(6, 4) +#define TWAI_TIME_SAMP BIT(7) +#define TWAI_TIME_SEG1_PREP(seg1) FIELD_PREP(TWAI_TIME_SEG1_MASK, seg1) +#define TWAI_TIME_SEG2_PREP(seg2) FIELD_PREP(TWAI_TIME_SEG2_MASK, seg2) + +/* TWAI_CLOCK_DIVIDER_REG is incompatible with CAN_SJA1000_CDR */ +#define TWAI_CLOCK_DIVIDER_REG (31U) +#define TWAI_CD_MASK GENMASK(7, 0) +#define TWAI_CLOCK_OFF BIT(8) + +/* + * Further incompatible registers currently not used by the driver: + * - TWAI_STATUS_REG has new bit 8: TWAI_MISS_ST + * - TWAI_INT_RAW_REG has new bit 8: TWAI_BUS_STATE_INT_ST + * - TWAI_INT_ENA_REG has new bit 8: TWAI_BUS_STATE_INT_ENA + */ +#else + +/* Redefinitions of the SJA1000 CDR bits to simplify driver config */ +#define TWAI_CD_MASK GENMASK(2, 0) +#define TWAI_CLOCK_OFF BIT(3) + +#endif /* !CONFIG_SOC_ESP32 */ + 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; +#ifndef CONFIG_SOC_ESP32 + /* 32-bit variant of output clock divider register required for non-ESP32 MCUs */ + uint32_t cdr32; +#endif /* !CONFIG_SOC_ESP32 */ }; static uint8_t can_esp32_twai_read_reg(const struct device *dev, uint8_t reg) @@ -44,6 +95,70 @@ static void can_esp32_twai_write_reg(const struct device *dev, uint8_t reg, uint sys_write32(val & 0xFF, addr); } +#ifndef CONFIG_SOC_ESP32 + +/* + * Required for newer ESP32-series MCUs which violate the original SJA1000 8-bit register size. + */ +static void can_esp32_twai_write_reg32(const struct device *dev, uint8_t reg, uint32_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, addr); +} + +/* + * Custom implementation instead of can_sja1000_set_timing required because TWAI_BUS_TIMING_0_REG + * is incompatible with CAN_SJA1000_BTR0. + */ +static int can_esp32_twai_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; + + __ASSERT_NO_MSG(timing->sjw == CAN_SJW_NO_CHANGE || + (timing->sjw >= 0x1 && timing->sjw <= 0x4)); + __ASSERT_NO_MSG(timing->prop_seg == 0); + __ASSERT_NO_MSG(timing->phase_seg1 >= 0x1 && timing->phase_seg1 <= 0x10); + __ASSERT_NO_MSG(timing->phase_seg2 >= 0x1 && timing->phase_seg2 <= 0x8); + __ASSERT_NO_MSG(timing->prescaler >= 0x1 && timing->prescaler <= 0x2000); + + if (data->started) { + return -EBUSY; + } + + k_mutex_lock(&data->mod_lock, K_FOREVER); + + if (timing->sjw == CAN_SJW_NO_CHANGE) { + sjw = data->sjw; + } else { + sjw = timing->sjw; + data->sjw = timing->sjw; + } + + btr0 = TWAI_BAUD_PRESC_PREP(timing->prescaler - 1) | + TWAI_SYNC_JUMP_WIDTH_PREP(sjw - 1); + btr1 = TWAI_TIME_SEG1_PREP(timing->phase_seg1 - 1) | + TWAI_TIME_SEG2_PREP(timing->phase_seg2 - 1); + + if ((data->mode & CAN_MODE_3_SAMPLES) != 0) { + btr1 |= TWAI_TIME_SAMP; + } + + can_esp32_twai_write_reg32(dev, TWAI_BUS_TIMING_0_REG, btr0); + can_esp32_twai_write_reg32(dev, TWAI_BUS_TIMING_1_REG, btr1); + + k_mutex_unlock(&data->mod_lock); + + return 0; +} + +#endif /* !CONFIG_SOC_ESP32 */ + static int can_esp32_twai_get_core_clock(const struct device *dev, uint32_t *rate) { ARG_UNUSED(dev); @@ -90,6 +205,18 @@ static int can_esp32_twai_init(const struct device *dev) return err; } +#ifndef CONFIG_SOC_ESP32 + /* + * TWAI_CLOCK_DIVIDER_REG is incompatible with CAN_SJA1000_CDR for non-ESP32 MCUs + * - TWAI_CD has length of 8 bits instead of 3 bits + * - TWAI_CLOCK_OFF at BIT(8) instead of BIT(3) + * - TWAI_EXT_MODE bit missing (always "extended" = PeliCAN mode) + * + * Overwrite with 32-bit register variant configured via devicetree. + */ + can_esp32_twai_write_reg32(dev, TWAI_CLOCK_DIVIDER_REG, twai_config->cdr32); +#endif /* !CONFIG_SOC_ESP32 */ + esp_intr_alloc(twai_config->irq_source, 0, can_esp32_twai_isr, (void *)dev, NULL); return 0; @@ -100,7 +227,11 @@ const struct can_driver_api can_esp32_twai_driver_api = { .start = can_sja1000_start, .stop = can_sja1000_stop, .set_mode = can_sja1000_set_mode, +#ifdef CONFIG_SOC_ESP32 .set_timing = can_sja1000_set_timing, +#else + .set_timing = can_esp32_twai_set_timing, +#endif /* CONFIG_SOC_ESP32 */ .send = can_sja1000_send, .add_rx_filter = can_sja1000_add_rx_filter, .remove_rx_filter = can_sja1000_remove_rx_filter, @@ -113,13 +244,40 @@ const struct can_driver_api can_esp32_twai_driver_api = { .recover = can_sja1000_recover, #endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, +#ifdef CONFIG_SOC_ESP32 .timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, +#else + /* larger prescaler allowed for newer ESP32-series MCUs */ + .timing_max = { + .sjw = 0x4, + .prop_seg = 0x0, + .phase_seg1 = 0x10, + .phase_seg2 = 0x8, + .prescaler = 0x2000, + } +#endif /* CONFIG_SOC_ESP32 */ }; +#ifdef CONFIG_SOC_ESP32 +#define TWAI_CLKOUT_DIVIDER_MAX (14) +#define TWAI_CDR32_INIT(inst) +#else +#define TWAI_CLKOUT_DIVIDER_MAX (490) +#define TWAI_CDR32_INIT(inst) .cdr32 = CAN_ESP32_TWAI_DT_CDR_INST_GET(inst) +#endif /* CONFIG_SOC_ESP32 */ + +#define CAN_ESP32_TWAI_ASSERT_CLKOUT_DIVIDER(inst) \ + BUILD_ASSERT(COND_CODE_0(DT_INST_NODE_HAS_PROP(inst, clkout_divider), (1), \ + (DT_INST_PROP(inst, clkout_divider) == 1 || \ + (DT_INST_PROP(inst, clkout_divider) % 2 == 0 && \ + DT_INST_PROP(inst, clkout_divider) / 2 <= TWAI_CLKOUT_DIVIDER_MAX))), \ + "TWAI clkout-divider from dts invalid") + #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)) + COND_CODE_1(DT_INST_PROP(inst, clkout_divider) == 1, (TWAI_CD_MASK), \ + ((DT_INST_PROP(inst, clkout_divider)) / 2 - 1)), \ + (TWAI_CLOCK_OFF)) #define CAN_ESP32_TWAI_INIT(inst) \ PINCTRL_DT_INST_DEFINE(inst); \ @@ -130,12 +288,15 @@ const struct can_driver_api can_esp32_twai_driver_api = { .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), \ + TWAI_CDR32_INIT(inst) \ }; \ + CAN_ESP32_TWAI_ASSERT_CLKOUT_DIVIDER(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)); \ + COND_CODE_0(IS_ENABLED(CONFIG_SOC_ESP32), (0), \ + (CAN_ESP32_TWAI_DT_CDR_INST_GET(inst)))); \ \ static struct can_sja1000_data can_sja1000_data_##inst = \ CAN_SJA1000_DATA_INITIALIZER(NULL); \ diff --git a/dts/bindings/can/espressif,esp32-twai.yaml b/dts/bindings/can/espressif,esp32-twai.yaml index 2d37098bb2..36ddceb0e8 100644 --- a/dts/bindings/can/espressif,esp32-twai.yaml +++ b/dts/bindings/can/espressif,esp32-twai.yaml @@ -26,14 +26,8 @@ properties: clkout-divider: type: int required: false - enum: - - 1 - - 2 - - 4 - - 6 - - 8 - - 10 - - 12 - - 14 description: | Clock divider for the CLKOUT signal. If not set, the CLKOUT signal is turned off. + + Valid values are 1 or any even number from 2 to 14 for ESP32 and 2 to 490 for newer + Espressif MCUs like ESP32-C3.