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 <kserwus@gmail.com>
This commit is contained in:
parent
d01780fc94
commit
632704e04b
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
9
drivers/can/Kconfig.sam0
Normal file
9
drivers/can/Kconfig.sam0
Normal file
|
@ -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
|
227
drivers/can/can_sam0.c
Normal file
227
drivers/can/can_sam0.c
Normal file
|
@ -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)
|
25
dts/bindings/can/atmel,sam0-can.yaml
Normal file
25
dts/bindings/can/atmel,sam0-can.yaml
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue