drivers: can: esp32_twai: add support for newer MCUs

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 <martin@libre.solar>
This commit is contained in:
Martin Jäger 2022-10-06 13:46:54 +02:00 committed by Carles Cufí
commit 2a035f775b
2 changed files with 167 additions and 12 deletions

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (c) 2022 Henrik Brix Andersen <henrik@brixandersen.dk> * Copyright (c) 2022 Henrik Brix Andersen <henrik@brixandersen.dk>
* Copyright (c) 2022 Martin Jäger <martin@libre.solar>
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -18,12 +19,62 @@
LOG_MODULE_REGISTER(can_esp32_twai, CONFIG_CAN_LOG_LEVEL); 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 { struct can_esp32_twai_config {
mm_reg_t base; mm_reg_t base;
const struct pinctrl_dev_config *pcfg; const struct pinctrl_dev_config *pcfg;
const struct device *clock_dev; const struct device *clock_dev;
const clock_control_subsys_t clock_subsys; const clock_control_subsys_t clock_subsys;
int irq_source; 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) 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); 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) static int can_esp32_twai_get_core_clock(const struct device *dev, uint32_t *rate)
{ {
ARG_UNUSED(dev); ARG_UNUSED(dev);
@ -90,6 +205,18 @@ static int can_esp32_twai_init(const struct device *dev)
return err; 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); esp_intr_alloc(twai_config->irq_source, 0, can_esp32_twai_isr, (void *)dev, NULL);
return 0; return 0;
@ -100,7 +227,11 @@ const struct can_driver_api can_esp32_twai_driver_api = {
.start = can_sja1000_start, .start = can_sja1000_start,
.stop = can_sja1000_stop, .stop = can_sja1000_stop,
.set_mode = can_sja1000_set_mode, .set_mode = can_sja1000_set_mode,
#ifdef CONFIG_SOC_ESP32
.set_timing = can_sja1000_set_timing, .set_timing = can_sja1000_set_timing,
#else
.set_timing = can_esp32_twai_set_timing,
#endif /* CONFIG_SOC_ESP32 */
.send = can_sja1000_send, .send = can_sja1000_send,
.add_rx_filter = can_sja1000_add_rx_filter, .add_rx_filter = can_sja1000_add_rx_filter,
.remove_rx_filter = can_sja1000_remove_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, .recover = can_sja1000_recover,
#endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ #endif /* !CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */
.timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER, .timing_min = CAN_SJA1000_TIMING_MIN_INITIALIZER,
#ifdef CONFIG_SOC_ESP32
.timing_max = CAN_SJA1000_TIMING_MAX_INITIALIZER, .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) \ #define CAN_ESP32_TWAI_DT_CDR_INST_GET(inst) \
COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clkout_divider), \ COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, clkout_divider), \
(UTIL_CAT(CAN_SJA1000_CDR_CD_DIV, DT_INST_PROP(inst, clkout_divider))), \ COND_CODE_1(DT_INST_PROP(inst, clkout_divider) == 1, (TWAI_CD_MASK), \
(CAN_SJA1000_CDR_CLOCK_OFF)) ((DT_INST_PROP(inst, clkout_divider)) / 2 - 1)), \
(TWAI_CLOCK_OFF))
#define CAN_ESP32_TWAI_INIT(inst) \ #define CAN_ESP32_TWAI_INIT(inst) \
PINCTRL_DT_INST_DEFINE(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), \ .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(inst, offset), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
.irq_source = DT_INST_IRQN(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 = \ static const struct can_sja1000_config can_sja1000_config_##inst = \
CAN_SJA1000_DT_CONFIG_INST_GET(inst, &can_esp32_twai_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_esp32_twai_read_reg, can_esp32_twai_write_reg, \
CAN_SJA1000_OCR_OCMODE_BIPHASE, \ 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 = \ static struct can_sja1000_data can_sja1000_data_##inst = \
CAN_SJA1000_DATA_INITIALIZER(NULL); \ CAN_SJA1000_DATA_INITIALIZER(NULL); \

View file

@ -26,14 +26,8 @@ properties:
clkout-divider: clkout-divider:
type: int type: int
required: false required: false
enum:
- 1
- 2
- 4
- 6
- 8
- 10
- 12
- 14
description: | description: |
Clock divider for the CLKOUT signal. If not set, the CLKOUT signal is turned off. 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.