drivers: can: mcan: let front-end drivers supply register r/w functions

Let the Bosch M_CAN front-end drivers supply their own register read/write
functions.

This is preparation for handling non-standard Bosch M_CAN register layouts
directly in the front-end and for accessing Bosch M_CAN IP cores over
peripheral busses.

Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
This commit is contained in:
Henrik Brix Andersen 2023-05-04 11:14:31 +02:00 committed by Carles Cufí
commit bbfc1f905c
10 changed files with 197 additions and 35 deletions

View file

@ -47,26 +47,34 @@ static void memset32_volatile(volatile void *dst_, uint32_t val, size_t len)
}
}
static inline int can_mcan_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *config = dev->config;
mm_reg_t base = config->base;
LOG_DBG("write reg 0x%03x = 0x%08x", reg, val);
sys_write32(val, base + reg);
return 0;
}
static inline int can_mcan_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
{
const struct can_mcan_config *config = dev->config;
mm_reg_t base = config->base;
int err;
*val = sys_read32(base + reg);
LOG_DBG("read reg 0x%03x = 0x%08x", reg, *val);
err = config->read_reg(dev, reg, val);
if (err != 0) {
LOG_ERR("failed to read reg 0x%03x (err %d)", reg, err);
} else {
LOG_DBG("read reg 0x%03x = 0x%08x", reg, *val);
}
return 0;
return err;
}
static inline int can_mcan_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *config = dev->config;
int err;
err = config->write_reg(dev, reg, val);
if (err != 0) {
LOG_ERR("failed to write reg 0x%03x (err %d)", reg, err);
} else {
LOG_DBG("write reg 0x%03x = 0x%08x", reg, val);
}
return err;
}
static int can_mcan_exit_sleep_mode(const struct device *dev)

View file

@ -190,8 +190,33 @@ struct can_mcan_data {
void *custom;
} __aligned(4);
/**
* @brief Bosch M_CAN driver front-end callback for reading a register value
*
* @param dev Pointer to the device structure for the driver instance.
* @param reg Register offset
* @param[out] val Register value
*
* @retval 0 If successful.
* @retval -EIO General input/output error.
*/
typedef int (*can_mcan_read_reg_t)(const struct device *dev, uint16_t reg, uint32_t *val);
/**
* @brief Bosch M_CAN driver front-end callback for writing a register value
*
* @param dev Pointer to the device structure for the driver instance.
* @param reg Register offset
* @param val Register value
*
* @retval 0 If successful.
* @retval -EIO General input/output error.
*/
typedef int (*can_mcan_write_reg_t)(const struct device *dev, uint16_t reg, uint32_t val);
struct can_mcan_config {
mm_reg_t base;
can_mcan_read_reg_t read_reg;
can_mcan_write_reg_t write_reg;
uint32_t bus_speed;
uint32_t bus_speed_data;
uint16_t sjw;
@ -210,10 +235,18 @@ struct can_mcan_config {
const void *custom;
};
/**
* @brief Static initializer for @p can_mcan_config struct
*
* @param node_id Devicetree node identifier
* @param _custom Pointer to custom driver frontend configuration structure
* @param _read_reg Driver frontend Bosch M_CAN register read function
* @param _write_reg Driver frontend Bosch M_CAN register write function
*/
#ifdef CONFIG_CAN_FD_MODE
#define CAN_MCAN_DT_CONFIG_GET(node_id, _custom_config) \
#define CAN_MCAN_DT_CONFIG_GET(node_id, _custom, _read_reg, _write_reg) \
{ \
.base = (mm_reg_t)DT_REG_ADDR_BY_NAME(node_id, m_can), \
.read_reg = _read_reg, .write_reg = _write_reg, \
.bus_speed = DT_PROP(node_id, bus_speed), .sjw = DT_PROP(node_id, sjw), \
.sample_point = DT_PROP_OR(node_id, sample_point, 0), \
.prop_ts1 = DT_PROP_OR(node_id, prop_seg, 0) + DT_PROP_OR(node_id, phase_seg1, 0), \
@ -227,30 +260,76 @@ struct can_mcan_config {
.tx_delay_comp_offset = DT_PROP(node_id, tx_delay_comp_offset), \
.phy = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, phys)), \
.max_bitrate = DT_CAN_TRANSCEIVER_MAX_BITRATE(node_id, 8000000), \
.custom = _custom_config, \
.custom = _custom, \
}
#else /* CONFIG_CAN_FD_MODE */
#define CAN_MCAN_DT_CONFIG_GET(node_id, _custom_config) \
#define CAN_MCAN_DT_CONFIG_GET(node_id, _custom, _read_reg, _write_reg) \
{ \
.base = (mm_reg_t)DT_REG_ADDR_BY_NAME(node_id, m_can), \
.read_reg = _read_reg, .write_reg = _write_reg, \
.bus_speed = DT_PROP(node_id, bus_speed), .sjw = DT_PROP(node_id, sjw), \
.sample_point = DT_PROP_OR(node_id, sample_point, 0), \
.prop_ts1 = DT_PROP_OR(node_id, prop_seg, 0) + DT_PROP_OR(node_id, phase_seg1, 0), \
.ts2 = DT_PROP_OR(node_id, phase_seg2, 0), \
.phy = DEVICE_DT_GET_OR_NULL(DT_PHANDLE(node_id, phys)), \
.max_bitrate = DT_CAN_TRANSCEIVER_MAX_BITRATE(node_id, 1000000), \
.custom = _custom_config, \
.custom = _custom, \
}
#endif /* !CONFIG_CAN_FD_MODE */
#define CAN_MCAN_DT_CONFIG_INST_GET(inst, _custom_config) \
CAN_MCAN_DT_CONFIG_GET(DT_DRV_INST(inst), _custom_config)
/**
* @brief Static initializer for @p can_mcan_config struct from DT_DRV_COMPAT instance
*
* @param inst DT_DRV_COMPAT instance number
* @param _custom Pointer to custom driver frontend configuration structure
* @param _read_reg Driver frontend Bosch M_CAN register read function
* @param _write_reg Driver frontend Bosch M_CAN register write function
* @see CAN_MCAN_DT_CONFIG_GET()
*/
#define CAN_MCAN_DT_CONFIG_INST_GET(inst, _custom, _read_reg, _write_reg) \
CAN_MCAN_DT_CONFIG_GET(DT_DRV_INST(inst), _custom, _read_reg, _write_reg)
#define CAN_MCAN_DATA_INITIALIZER(_msg_ram, _custom_data) \
/**
* @brief Initializer for a @a can_mcan_data struct
* @param _msg_ram Pointer to message RAM structure
* @param _custom Pointer to custom driver frontend data structure
*/
#define CAN_MCAN_DATA_INITIALIZER(_msg_ram, _custom) \
{ \
.msg_ram = _msg_ram, .custom = _custom_data, \
.msg_ram = _msg_ram, .custom = _custom, \
}
/**
* @brief Bosch M_CAN driver front-end callback helper for reading a memory mapped register
*
* @param base Register base address
* @param reg Register offset
* @param[out] val Register value
*
* @retval 0 Memory mapped register read always succeeds.
*/
static inline int can_mcan_sys_read_reg(mm_reg_t base, uint16_t reg, uint32_t *val)
{
*val = sys_read32(base + reg);
return 0;
}
/**
* @brief Bosch M_CAN driver front-end callback helper for writing a memory mapped register
*
* @param base Register base address
* @param reg Register offset
* @param val Register value
*
* @retval 0 Memory mapped register write always succeeds.
*/
static inline int can_mcan_sys_write_reg(mm_reg_t base, uint16_t reg, uint32_t val)
{
sys_write32(val, base + reg);
return 0;
}
int can_mcan_get_capabilities(const struct device *dev, can_mode_t *cap);
int can_mcan_start(const struct device *dev);

View file

@ -18,6 +18,7 @@ LOG_MODULE_REGISTER(can_mcux_mcan, CONFIG_CAN_LOG_LEVEL);
#define DT_DRV_COMPAT nxp_lpc_mcan
struct mcux_mcan_config {
mm_reg_t base;
const struct device *clock_dev;
clock_control_subsys_t clock_subsys;
void (*irq_config_func)(const struct device *dev);
@ -28,6 +29,22 @@ struct mcux_mcan_data {
struct can_mcan_msg_sram msg_ram __nocache;
};
static int mcux_mcan_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct mcux_mcan_config *mcux_config = mcan_config->custom;
return can_mcan_sys_read_reg(mcux_config->base, reg, val);
}
static int mcux_mcan_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct mcux_mcan_config *mcux_config = mcan_config->custom;
return can_mcan_sys_write_reg(mcux_config->base, reg, val);
}
static int mcux_mcan_get_core_clock(const struct device *dev, uint32_t *rate)
{
const struct can_mcan_config *mcan_config = dev->config;
@ -142,6 +159,7 @@ static const struct can_driver_api mcux_mcan_driver_api = {
static void mcux_mcan_irq_config_##n(const struct device *dev); \
\
static const struct mcux_mcan_config mcux_mcan_config_##n = { \
.base = (mm_reg_t)DT_INST_REG_ADDR(n), \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_subsys = (clock_control_subsys_t) \
DT_INST_CLOCKS_CELL(n, name), \
@ -150,7 +168,9 @@ static const struct can_driver_api mcux_mcan_driver_api = {
}; \
\
static const struct can_mcan_config can_mcan_config_##n = \
CAN_MCAN_DT_CONFIG_INST_GET(n, &mcux_mcan_config_##n); \
CAN_MCAN_DT_CONFIG_INST_GET(n, &mcux_mcan_config_##n, \
mcux_mcan_read_reg, \
mcux_mcan_write_reg); \
\
static struct mcux_mcan_data mcux_mcan_data_##n; \
\

View file

@ -20,6 +20,7 @@ LOG_MODULE_REGISTER(can_sam, CONFIG_CAN_LOG_LEVEL);
#define DT_DRV_COMPAT atmel_sam_can
struct can_sam_config {
mm_reg_t base;
void (*config_irq)(void);
const struct atmel_sam_pmc_config clock_cfg;
const struct pinctrl_dev_config *pcfg;
@ -30,6 +31,22 @@ struct can_sam_data {
struct can_mcan_msg_sram msg_ram;
};
static int can_sam_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_sam_config *sam_config = mcan_config->custom;
return can_mcan_sys_read_reg(sam_config->base, reg, val);
}
static int can_sam_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_sam_config *sam_config = mcan_config->custom;
return can_mcan_sys_write_reg(sam_config->base, reg, val);
}
static int can_sam_get_core_clock(const struct device *dev, uint32_t *rate)
{
const struct can_mcan_config *mcan_cfg = dev->config;
@ -139,6 +156,7 @@ static void config_can_##inst##_irq(void)
#define CAN_SAM_CFG_INST(inst) \
static const struct can_sam_config can_sam_cfg_##inst = { \
.base = (mm_reg_t)DT_INST_REG_ADDR(inst), \
.clock_cfg = SAM_DT_INST_CLOCK_PMC_CFG(inst), \
.divider = DT_INST_PROP(inst, divider), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
@ -146,7 +164,9 @@ static void config_can_##inst##_irq(void)
}; \
\
static const struct can_mcan_config can_mcan_cfg_##inst = \
CAN_MCAN_DT_CONFIG_INST_GET(inst, &can_sam_cfg_##inst);
CAN_MCAN_DT_CONFIG_INST_GET(inst, &can_sam_cfg_##inst, \
can_sam_read_reg, \
can_sam_write_reg);
#define CAN_SAM_DATA_INST(inst) \
static struct can_sam_data can_sam_data_##inst; \

View file

@ -30,6 +30,7 @@ LOG_MODULE_REGISTER(can_stm32fd, CONFIG_CAN_LOG_LEVEL);
#endif
struct can_stm32fd_config {
mm_reg_t base;
size_t pclk_len;
const struct stm32_pclken *pclken;
void (*config_irq)(void);
@ -37,6 +38,22 @@ struct can_stm32fd_config {
uint8_t clock_divider;
};
static int can_stm32fd_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_stm32fd_config *stm32fd_config = mcan_config->custom;
return can_mcan_sys_read_reg(stm32fd_config->base, reg, val);
}
static int can_stm32fd_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_stm32fd_config *stm32fd_config = mcan_config->custom;
return can_mcan_sys_write_reg(stm32fd_config->base, reg, val);
}
static int can_stm32fd_get_core_clock(const struct device *dev, uint32_t *rate)
{
const uint32_t rate_tmp = LL_RCC_GetFDCANClockFreq(LL_RCC_FDCAN_CLKSOURCE);
@ -190,6 +207,7 @@ static void config_can_##inst##_irq(void) \
STM32_DT_INST_CLOCKS(inst); \
\
static const struct can_stm32fd_config can_stm32fd_cfg_##inst = { \
.base = (mm_reg_t)DT_INST_REG_ADDR_BY_NAME(inst, m_can), \
.pclken = can_stm32fd_pclken_##inst, \
.pclk_len = DT_INST_NUM_CLOCKS(inst), \
.config_irq = config_can_##inst##_irq, \
@ -198,7 +216,9 @@ static void config_can_##inst##_irq(void) \
}; \
\
static const struct can_mcan_config can_mcan_cfg_##inst = \
CAN_MCAN_DT_CONFIG_INST_GET(inst, &can_stm32fd_cfg_##inst);
CAN_MCAN_DT_CONFIG_INST_GET(inst, &can_stm32fd_cfg_##inst, \
can_stm32fd_read_reg, \
can_stm32fd_write_reg);
#define CAN_STM32FD_DATA_INST(inst) \
static struct can_mcan_data can_mcan_data_##inst = \

View file

@ -21,11 +21,28 @@ LOG_MODULE_REGISTER(can_stm32h7, CONFIG_CAN_LOG_LEVEL);
#define DT_DRV_COMPAT st_stm32h7_fdcan
struct can_stm32h7_config {
mm_reg_t base;
void (*config_irq)(void);
const struct pinctrl_dev_config *pcfg;
struct stm32_pclken pclken;
};
static int can_stm32h7_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_stm32h7_config *stm32h7_config = mcan_config->custom;
return can_mcan_sys_read_reg(stm32h7_config->base, reg, val);
}
static int can_stm32h7_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
{
const struct can_mcan_config *mcan_config = dev->config;
const struct can_stm32h7_config *stm32h7_config = mcan_config->custom;
return can_mcan_sys_write_reg(stm32h7_config->base, reg, val);
}
static int can_stm32h7_get_core_clock(const struct device *dev, uint32_t *rate)
{
const uint32_t rate_tmp = LL_RCC_GetFDCANClockFreq(LL_RCC_FDCAN_CLKSOURCE);
@ -164,6 +181,7 @@ static const struct can_driver_api can_stm32h7_driver_api = {
PINCTRL_DT_INST_DEFINE(n); \
\
static const struct can_stm32h7_config can_stm32h7_cfg_##n = { \
.base = (mm_reg_t)DT_INST_REG_ADDR_BY_NAME(n, m_can), \
.config_irq = stm32h7_mcan_irq_config_##n, \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.pclken = { \
@ -173,7 +191,9 @@ static const struct can_driver_api can_stm32h7_driver_api = {
}; \
\
static const struct can_mcan_config can_mcan_cfg_##n = \
CAN_MCAN_DT_CONFIG_INST_GET(n, &can_stm32h7_cfg_##n); \
CAN_MCAN_DT_CONFIG_INST_GET(n, &can_stm32h7_cfg_##n, \
can_stm32h7_read_reg, \
can_stm32h7_write_reg); \
\
static struct can_mcan_data can_mcan_data_##n = \
CAN_MCAN_DATA_INITIALIZER((struct can_mcan_msg_sram *) \

View file

@ -422,7 +422,6 @@
can0: can@40030000 {
compatible = "atmel,sam-can";
reg = <0x40030000 0x100>;
reg-names = "m_can";
interrupts = <35 0>, <36 0>;
interrupt-names = "LINE_0", "LINE_1";
clocks = <&pmc PMC_TYPE_PERIPHERAL 35>;
@ -437,7 +436,6 @@
can1: can@40034000 {
compatible = "atmel,sam-can";
reg = <0x40034000 0x100>;
reg-names = "m_can";
interrupts = <37 0>, <38 0>;
interrupt-names = "LINE_0", "LINE_1";
clocks = <&pmc PMC_TYPE_PERIPHERAL 37>;

View file

@ -220,7 +220,6 @@
can0: can@9d000 {
compatible = "nxp,lpc-mcan";
reg = <0x9d000 0x1000>;
reg-names = "m_can";
interrupts = <43 0>, <44 0>;
clocks = <&syscon MCUX_MCAN_CLK>;
std-filter-elements = <15>;

View file

@ -214,7 +214,6 @@
can0: can@9d000 {
compatible = "nxp,lpc-mcan";
reg = <0x9d000 0x1000>;
reg-names = "m_can";
interrupts = <43 0>, <44 0>;
clocks = <&syscon MCUX_MCAN_CLK>;
std-filter-elements = <15>;

View file

@ -257,7 +257,6 @@
can0: can@4009d000 {
compatible = "nxp,lpc-mcan";
reg = <0x4009d000 0x1000>;
reg-names = "m_can";
interrupts = <43 0>, <44 0>;
clocks = <&syscon MCUX_MCAN_CLK>;
std-filter-elements = <15>;