From d9b773d5d1b7bea9c0d598e5563507b60ed556b1 Mon Sep 17 00:00:00 2001 From: Kamil Serwus Date: Wed, 16 Nov 2022 20:27:33 +0100 Subject: [PATCH 1/4] sam: can: CAN driver for SAM0 socs Driver was based on can_sam. SAMC21 has only 1 interrupt for one can "output", so can interrupt has to executes two lines of interrupts. CAN is configured to use OSC48M clock via GLCK7. GLCK7 is set by divider configured from dts. Signed-off-by: Kamil Serwus --- drivers/can/CMakeLists.txt | 1 + drivers/can/Kconfig | 1 + drivers/can/Kconfig.sam0 | 9 ++ drivers/can/can_sam0.c | 227 +++++++++++++++++++++++++++ dts/bindings/can/atmel,sam0-can.yaml | 25 +++ soc/arm/atmel_sam0/samc20/soc.h | 1 + soc/arm/atmel_sam0/samc21/soc.h | 1 + 7 files changed, 265 insertions(+) create mode 100644 drivers/can/Kconfig.sam0 create mode 100644 drivers/can/can_sam0.c create mode 100644 dts/bindings/can/atmel,sam0-can.yaml diff --git a/drivers/can/CMakeLists.txt b/drivers/can/CMakeLists.txt index 630ace7a6581d..c5a39471ea9c6 100644 --- a/drivers/can/CMakeLists.txt +++ b/drivers/can/CMakeLists.txt @@ -10,6 +10,7 @@ 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_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) zephyr_library_sources_ifdef(CONFIG_CAN_STM32 can_stm32.c) zephyr_library_sources_ifdef(CONFIG_CAN_STM32FD can_stm32fd.c) zephyr_library_sources_ifdef(CONFIG_CAN_STM32H7 can_stm32h7.c) diff --git a/drivers/can/Kconfig b/drivers/can/Kconfig index 7c37744a3c895..c006d468ec0d6 100644 --- a/drivers/can/Kconfig +++ b/drivers/can/Kconfig @@ -86,6 +86,7 @@ config CAN_QEMU_IFACE_NAME configured before starting QEMU. source "drivers/can/Kconfig.sam" +source "drivers/can/Kconfig.sam0" source "drivers/can/Kconfig.stm32" source "drivers/can/Kconfig.stm32fd" source "drivers/can/Kconfig.stm32h7" diff --git a/drivers/can/Kconfig.sam0 b/drivers/can/Kconfig.sam0 new file mode 100644 index 0000000000000..aca2befcca814 --- /dev/null +++ b/drivers/can/Kconfig.sam0 @@ -0,0 +1,9 @@ +# SAM CAN configuration options +# Copyright (c) 2022 Kamil Serwus +# SPDX-License-Identifier: Apache-2.0 + +config CAN_SAM0 + bool "Atmel SAM0 CAN driver" + default y + depends on DT_HAS_ATMEL_SAM0_CAN_ENABLED + select CAN_MCAN diff --git a/drivers/can/can_sam0.c b/drivers/can/can_sam0.c new file mode 100644 index 0000000000000..2f9a390ecc8f3 --- /dev/null +++ b/drivers/can/can_sam0.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2022 Vestas Wind Systems A/S + * Copyright (c) 2021 Alexander Wachter + * Copyright (c) 2022 Kamil Serwus + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include + +#include "can_mcan.h" + +LOG_MODULE_REGISTER(can_sam0, CONFIG_CAN_LOG_LEVEL); + +#define DT_DRV_COMPAT atmel_sam0_can + +struct can_sam0_config { + mm_reg_t base; + void (*config_irq)(void); + const struct pinctrl_dev_config *pcfg; + volatile uint32_t *mclk; + uint32_t mclk_mask; + uint16_t gclk_core_id; + int divider; +}; + +struct can_sam0_data { + struct can_mcan_msg_sram msg_ram; +}; + +static int can_sam0_read_reg(const struct device *dev, uint16_t reg, uint32_t *val) +{ + const struct can_mcan_config *mcan_config = dev->config; + const struct can_sam0_config *sam_config = mcan_config->custom; + + return can_mcan_sys_read_reg(sam_config->base, reg, val); +} + +static int can_sam0_write_reg(const struct device *dev, uint16_t reg, uint32_t val) +{ + const struct can_mcan_config *mcan_config = dev->config; + const struct can_sam0_config *sam_config = mcan_config->custom; + uint32_t bits = 0U; + + switch (reg) { + case CAN_MCAN_ILS: + /* All interrupts are assigned to MCAN_INT0 */ + val = 0; + break; + case CAN_MCAN_ILE: + /* SAM0 has only one line to handle interrupts */ + val = CAN_MCAN_ILE_EINT0; + break; + default: + /* No field remap needed */ + bits = val; + break; + }; + + return can_mcan_sys_write_reg(sam_config->base, reg, val); +} + +void can_sam0_line_x_isr(const struct device *dev) +{ + can_mcan_line_0_isr(dev); + can_mcan_line_1_isr(dev); +} + +static int can_sam0_get_core_clock(const struct device *dev, uint32_t *rate) +{ + const struct can_mcan_config *mcan_cfg = dev->config; + const struct can_sam0_config *sam_cfg = mcan_cfg->custom; + + *rate = SOC_ATMEL_SAM0_OSC48M_FREQ_HZ / (sam_cfg->divider); + + return 0; +} + +static void can_sam0_clock_enable(const struct can_sam0_config *cfg) +{ + /* Enable the GLCK7 with DIV*/ + GCLK->GENCTRL[7].reg = GCLK_GENCTRL_SRC(GCLK_GENCTRL_SRC_OSC48M) + | GCLK_GENCTRL_DIV(cfg->divider) + | GCLK_GENCTRL_GENEN; + + /* Route channel */ + GCLK->PCHCTRL[cfg->gclk_core_id].reg = GCLK_PCHCTRL_GEN_GCLK7 + | GCLK_PCHCTRL_CHEN; + + /* Enable CAN clock in MCLK */ + *cfg->mclk |= cfg->mclk_mask; +} + +static int can_sam0_init(const struct device *dev) +{ + const struct can_mcan_config *mcan_cfg = dev->config; + const struct can_sam0_config *sam_cfg = mcan_cfg->custom; + int ret; + + can_sam0_clock_enable(sam_cfg); + + ret = pinctrl_apply_state(sam_cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("failed to apply pinctrl"); + return ret; + } + + ret = can_mcan_configure_message_ram(dev, 0U); + if (ret != 0) { + LOG_ERR("failed to configure message ram"); + return ret; + } + + ret = can_mcan_init(dev); + if (ret != 0) { + LOG_ERR("failed to mcan init"); + return ret; + } + + sam_cfg->config_irq(); + + return ret; +} + +static const struct can_driver_api can_sam0_driver_api = { + .get_capabilities = can_mcan_get_capabilities, + .start = can_mcan_start, + .stop = can_mcan_stop, + .set_mode = can_mcan_set_mode, + .set_timing = can_mcan_set_timing, + .send = can_mcan_send, + .add_rx_filter = can_mcan_add_rx_filter, + .remove_rx_filter = can_mcan_remove_rx_filter, + .get_state = can_mcan_get_state, +#ifndef CONFIG_CAN_AUTO_BUS_OFF_RECOVERY + .recover = can_mcan_recover, +#endif /* CONFIG_CAN_AUTO_BUS_OFF_RECOVERY */ + .get_core_clock = can_sam0_get_core_clock, + .get_max_filters = can_mcan_get_max_filters, + .get_max_bitrate = can_mcan_get_max_bitrate, + .set_state_change_callback = can_mcan_set_state_change_callback, + .timing_min = { + .sjw = 0x1, + .prop_seg = 0x00, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_max = { + .sjw = 0x7f, + .prop_seg = 0x00, + .phase_seg1 = 0x100, + .phase_seg2 = 0x80, + .prescaler = 0x200 + }, +#ifdef CONFIG_CAN_FD_MODE + .set_timing_data = can_mcan_set_timing_data, + .timing_data_min = { + .sjw = 0x01, + .prop_seg = 0x00, + .phase_seg1 = 0x01, + .phase_seg2 = 0x01, + .prescaler = 0x01 + }, + .timing_data_max = { + .sjw = 0x10, + .prop_seg = 0x00, + .phase_seg1 = 0x20, + .phase_seg2 = 0x10, + .prescaler = 0x20 + } +#endif /* CONFIG_CAN_FD_MODE */ +}; + +#define CAN_SAM0_IRQ_CFG_FUNCTION(inst) \ +static void config_can_##inst##_irq(void) \ +{ \ + LOG_DBG("Enable CAN##inst## IRQ"); \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(inst, line_0, irq), \ + DT_INST_IRQ_BY_NAME(inst, line_0, priority), can_sam0_line_x_isr, \ + DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ_BY_NAME(inst, line_0, irq)); \ +} + +#define CAN_SAM0_CFG_INST(inst) \ + static const struct can_sam0_config can_sam0_cfg_##inst = { \ + .base = (mm_reg_t)DT_INST_REG_ADDR(inst), \ + .mclk = (volatile uint32_t *)MCLK_MASK_DT_INT_REG_ADDR(inst), \ + .mclk_mask = BIT(DT_INST_CLOCKS_CELL_BY_NAME(inst, mclk, bit)), \ + .gclk_core_id = DT_INST_CLOCKS_CELL_BY_NAME(inst, gclk, periph_ch), \ + .divider = DT_INST_PROP(inst, divider), \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \ + .config_irq = config_can_##inst##_irq, \ + }; \ + \ + static const struct can_mcan_config can_mcan_cfg_##inst = \ + CAN_MCAN_DT_CONFIG_INST_GET(inst, &can_sam0_cfg_##inst, \ + can_sam0_read_reg, \ + can_sam0_write_reg); + +#define CAN_SAM0_DATA_INST(inst) \ + static struct can_sam0_data can_sam0_data_##inst; \ + \ + static struct can_mcan_data can_mcan_data_##inst = \ + CAN_MCAN_DATA_INITIALIZER(&can_sam0_data_##inst.msg_ram, \ + &can_sam0_data_##inst); \ + +#define CAN_SAM0_DEVICE_INST(inst) \ + DEVICE_DT_INST_DEFINE(inst, &can_sam0_init, NULL, \ + &can_mcan_data_##inst, \ + &can_mcan_cfg_##inst, \ + POST_KERNEL, CONFIG_CAN_INIT_PRIORITY, \ + &can_sam0_driver_api); + +#define CAN_SAM0_INST(inst) \ + PINCTRL_DT_INST_DEFINE(inst); \ + CAN_SAM0_IRQ_CFG_FUNCTION(inst) \ + CAN_SAM0_CFG_INST(inst) \ + CAN_SAM0_DATA_INST(inst) \ + CAN_SAM0_DEVICE_INST(inst) + +DT_INST_FOREACH_STATUS_OKAY(CAN_SAM0_INST) diff --git a/dts/bindings/can/atmel,sam0-can.yaml b/dts/bindings/can/atmel,sam0-can.yaml new file mode 100644 index 0000000000000..a555d8bf485ae --- /dev/null +++ b/dts/bindings/can/atmel,sam0-can.yaml @@ -0,0 +1,25 @@ +description: Specialization of Bosch m_can CAN-FD controller for Atmel SAM0 + +compatible: "atmel,sam0-can" + +include: + - name: can-fd-controller.yaml + - name: pinctrl-device.yaml + +properties: + reg: + required: true + + interrupts: + required: true + + clocks: + required: true + + clock-names: + required: true + + divider: + type: int + required: true + description: Clock divider of GLCK7 used by CAN as clock source diff --git a/soc/arm/atmel_sam0/samc20/soc.h b/soc/arm/atmel_sam0/samc20/soc.h index cca05ab6521ad..270f7557521c1 100644 --- a/soc/arm/atmel_sam0/samc20/soc.h +++ b/soc/arm/atmel_sam0/samc20/soc.h @@ -57,6 +57,7 @@ #include "../common/atmel_sam0_dt.h" #define SOC_ATMEL_SAM0_OSC32K_FREQ_HZ 32768 +#define SOC_ATMEL_SAM0_OSC48M_FREQ_HZ 48000000 /** Processor Clock (HCLK) Frequency */ #define SOC_ATMEL_SAM0_HCLK_FREQ_HZ CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC diff --git a/soc/arm/atmel_sam0/samc21/soc.h b/soc/arm/atmel_sam0/samc21/soc.h index 3f258b2eace7b..9f245e8eb0795 100644 --- a/soc/arm/atmel_sam0/samc21/soc.h +++ b/soc/arm/atmel_sam0/samc21/soc.h @@ -57,6 +57,7 @@ #include "../common/atmel_sam0_dt.h" #define SOC_ATMEL_SAM0_OSC32K_FREQ_HZ 32768 +#define SOC_ATMEL_SAM0_OSC48M_FREQ_HZ 48000000 /** Processor Clock (HCLK) Frequency */ #define SOC_ATMEL_SAM0_HCLK_FREQ_HZ CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC From ccbd4fbe051f1bacdc2177408706928be6f6a4bb Mon Sep 17 00:00:00 2001 From: Kamil Serwus Date: Wed, 16 Nov 2022 20:29:15 +0100 Subject: [PATCH 2/4] sam: atsamc21: enable CAN driver for SAMC21 Enable CAN driver sam0 in SAMC21 socs. CAN module exists only in C21 socs. Signed-off-by: Kamil Serwus --- dts/arm/atmel/samc21.dtsi | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dts/arm/atmel/samc21.dtsi b/dts/arm/atmel/samc21.dtsi index f7874487d0dc2..0fe0d9565a0e1 100644 --- a/dts/arm/atmel/samc21.dtsi +++ b/dts/arm/atmel/samc21.dtsi @@ -43,5 +43,47 @@ clock-names = "GCLK", "MCLK"; status = "disabled"; }; + + can { + compatible = "bosch,m_can-base"; + #address-cells = <1>; + #size-cells = <1>; + std-filter-elements = <28>; + ext-filter-elements = <8>; + rx-fifo0-elements = <3>; + rx-fifo1-elements = <3>; + rx-buffer-elements = <0>; + tx-buffer-elements = <1>; + + can0: can@42001c00 { + compatible = "atmel,sam0-can"; + reg = <0x42001c00 0x100>; + interrupts = <15 0>; + interrupt-names = "LINE_0"; + clocks = <&gclk 26>, <&mclk 0x10 8>; + clock-names = "GCLK", "MCLK"; + divider = <12>; + sjw = <1>; + sample-point = <875>; + sjw-data = <1>; + sample-point-data = <875>; + status = "disabled"; + }; + + can1: can@42002000 { + compatible = "atmel,sam0-can"; + reg = <0x42002000 0x100>; + interrupts = <16 0>; + interrupt-names = "LINE_0"; + clocks = <&gclk 27>, <&mclk 0x10 9>; + clock-names = "GCLK", "MCLK"; + divider = <12>; + sjw = <1>; + sample-point = <875>; + sjw-data = <1>; + sample-point-data = <875>; + status = "disabled"; + }; + }; }; }; From 5382faedaa04e1a02d6d8eff12afd48254c53818 Mon Sep 17 00:00:00 2001 From: Kamil Serwus Date: Wed, 16 Nov 2022 20:31:32 +0100 Subject: [PATCH 3/4] sam: atsamc21: enable CAN module in c21 board Configure pinmux pins at samc21 xpro board. Boards has hard-configured pins for CAN0 and CAN1 module. Signed-off-by: Kamil Serwus --- .../atsamc21n_xpro-pinctrl.dtsi | 14 ++++++++++ boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts | 27 +++++++++++++++++++ boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml | 1 + boards/arm/atsamc21n_xpro/doc/index.rst | 7 +++++ 4 files changed, 49 insertions(+) diff --git a/boards/arm/atsamc21n_xpro/atsamc21n_xpro-pinctrl.dtsi b/boards/arm/atsamc21n_xpro/atsamc21n_xpro-pinctrl.dtsi index 735b1b0ddd450..bc79bf08f8b7e 100644 --- a/boards/arm/atsamc21n_xpro/atsamc21n_xpro-pinctrl.dtsi +++ b/boards/arm/atsamc21n_xpro/atsamc21n_xpro-pinctrl.dtsi @@ -25,6 +25,20 @@ }; }; + can0_default: can0_default { + group1 { + pinmux = , + ; + }; + }; + + can1_default: can1_default { + group1 { + pinmux = , + ; + }; + }; + sercom0_uart_default: sercom0_uart_default { group1 { pinmux = , diff --git a/boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts b/boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts index 14d0dd22d5fbe..98ab04162a04b 100644 --- a/boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts +++ b/boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts @@ -18,6 +18,7 @@ zephyr,shell-uart = &sercom4; zephyr,sram = &sram0; zephyr,flash = &flash0; + zephyr,canbus = &can0; }; /* These aliases are provided for compatibility with samples */ @@ -151,3 +152,29 @@ }; }; }; + +&can0 { + status = "okay"; + + pinctrl-0 = <&can0_default>; + pinctrl-names = "default"; + + bus-speed = <125000>; + bus-speed-data = <1000000>; + + can-transceiver { + max-bitrate = <5000000>; + }; +}; + +&can1 { + pinctrl-0 = <&can1_default>; + pinctrl-names = "default"; + + bus-speed = <125000>; + bus-speed-data = <1000000>; + + can-transceiver { + max-bitrate = <5000000>; + }; +}; diff --git a/boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml b/boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml index 0d8fbeb75e110..554804a6f94dc 100644 --- a/boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml +++ b/boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml @@ -17,3 +17,4 @@ supported: - i2c - pwm - spi + - can diff --git a/boards/arm/atsamc21n_xpro/doc/index.rst b/boards/arm/atsamc21n_xpro/doc/index.rst index 07d12d6299e62..bb060fe32a46c 100644 --- a/boards/arm/atsamc21n_xpro/doc/index.rst +++ b/boards/arm/atsamc21n_xpro/doc/index.rst @@ -71,6 +71,9 @@ features: * - SPI - on-chip - Serial Peripheral Interface ports + * - CAN + - on-chip + - CAN ports Other hardware features are not currently supported by Zephyr. @@ -90,6 +93,10 @@ Default Zephyr Peripheral Mapping: ---------------------------------- - ADC0 : PB09 - ADC1 : PA08 +- CAN0 TX : PA24 +- CAN0 RX : PA25 +- CAN1 TX : PB14 +- CAN1 RX : PB15 - SERCOM0 USART TX : PB24 - SERCOM0 USART RX : PB25 - SERCOM1 I2C SDA : PA16 From 277c0af6033ec1af162bd8c0011455735a622dfd Mon Sep 17 00:00:00 2001 From: Kamil Serwus Date: Thu, 18 May 2023 12:22:37 +0200 Subject: [PATCH 4/4] can: mcan: fix setup configuration ram SAM0 required can module was in init state before configure pointers to ram which handle can frames. Signed-off-by: Kamil Serwus --- drivers/can/can_mcan.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/can/can_mcan.c b/drivers/can/can_mcan.c index f8091b3224c7d..ca252e914ec41 100644 --- a/drivers/can/can_mcan.c +++ b/drivers/can/can_mcan.c @@ -1207,6 +1207,18 @@ int can_mcan_configure_message_ram(const struct device *dev, uintptr_t mrba) uint32_t reg; int err; + err = can_mcan_exit_sleep_mode(dev); + if (err != 0) { + LOG_ERR("Failed to exit sleep mode"); + return -EIO; + } + + err = can_mcan_enter_init_mode(dev, K_MSEC(CAN_INIT_TIMEOUT_MS)); + if (err != 0) { + LOG_ERR("Failed to enter init mode"); + return -EIO; + } + can_mcan_enable_configuration_change(dev); reg = ((POINTER_TO_UINT(msg_ram->std_filt) - mrba) & CAN_MCAN_SIDFC_FLSSA) |