Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions boards/arm/atsamc21n_xpro/atsamc21n_xpro-pinctrl.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@
};
};

can0_default: can0_default {
group1 {
pinmux = <PA25G_CAN0_RX>,
<PA24G_CAN0_TX>;
};
};

can1_default: can1_default {
group1 {
pinmux = <PB15G_CAN1_RX>,
<PB14G_CAN1_TX>;
};
};

sercom0_uart_default: sercom0_uart_default {
group1 {
pinmux = <PB25C_SERCOM0_PAD1>,
Expand Down
27 changes: 27 additions & 0 deletions boards/arm/atsamc21n_xpro/atsamc21n_xpro.dts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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>;
};
};
1 change: 1 addition & 0 deletions boards/arm/atsamc21n_xpro/atsamc21n_xpro.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ supported:
- i2c
- pwm
- spi
- can
7 changes: 7 additions & 0 deletions boards/arm/atsamc21n_xpro/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions drivers/can/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions drivers/can/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
9 changes: 9 additions & 0 deletions drivers/can/Kconfig.sam0
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions drivers/can/can_mcan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
227 changes: 227 additions & 0 deletions drivers/can/can_sam0.c
Original file line number Diff line number Diff line change
@@ -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 <zephyr/drivers/can.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <soc.h>

#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)
Loading