From bbfc1f905c2d19c70b6138d8e5851870d01812e1 Mon Sep 17 00:00:00 2001 From: Henrik Brix Andersen Date: Thu, 4 May 2023 11:14:31 +0200 Subject: [PATCH] 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 --- drivers/can/can_mcan.c | 38 ++++++---- drivers/can/can_mcan.h | 101 ++++++++++++++++++++++++--- drivers/can/can_mcux_mcan.c | 22 +++++- drivers/can/can_sam.c | 22 +++++- drivers/can/can_stm32fd.c | 22 +++++- drivers/can/can_stm32h7.c | 22 +++++- dts/arm/atmel/same70.dtsi | 2 - dts/arm/nxp/nxp_lpc55S0x_common.dtsi | 1 - dts/arm/nxp/nxp_lpc55S1x_common.dtsi | 1 - dts/arm/nxp/nxp_lpc55S3x_common.dtsi | 1 - 10 files changed, 197 insertions(+), 35 deletions(-) diff --git a/drivers/can/can_mcan.c b/drivers/can/can_mcan.c index a75f0e9fe01..98c9d5c18a4 100644 --- a/drivers/can/can_mcan.c +++ b/drivers/can/can_mcan.c @@ -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) diff --git a/drivers/can/can_mcan.h b/drivers/can/can_mcan.h index 2fce306e28e..0bff3c93fed 100644 --- a/drivers/can/can_mcan.h +++ b/drivers/can/can_mcan.h @@ -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); diff --git a/drivers/can/can_mcux_mcan.c b/drivers/can/can_mcux_mcan.c index c5b9ca277e8..6996a8fecef 100644 --- a/drivers/can/can_mcux_mcan.c +++ b/drivers/can/can_mcux_mcan.c @@ -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; \ \ diff --git a/drivers/can/can_sam.c b/drivers/can/can_sam.c index 40ce17c38cc..85f7cf66b63 100644 --- a/drivers/can/can_sam.c +++ b/drivers/can/can_sam.c @@ -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; \ diff --git a/drivers/can/can_stm32fd.c b/drivers/can/can_stm32fd.c index 2c34fb85623..e8c8a80186f 100644 --- a/drivers/can/can_stm32fd.c +++ b/drivers/can/can_stm32fd.c @@ -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 = \ diff --git a/drivers/can/can_stm32h7.c b/drivers/can/can_stm32h7.c index 55ee06bb641..2f4c0b98fef 100644 --- a/drivers/can/can_stm32h7.c +++ b/drivers/can/can_stm32h7.c @@ -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 *) \ diff --git a/dts/arm/atmel/same70.dtsi b/dts/arm/atmel/same70.dtsi index af4f49a4d1a..8ba71d4d252 100644 --- a/dts/arm/atmel/same70.dtsi +++ b/dts/arm/atmel/same70.dtsi @@ -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>; diff --git a/dts/arm/nxp/nxp_lpc55S0x_common.dtsi b/dts/arm/nxp/nxp_lpc55S0x_common.dtsi index 187ee2c52bd..d79383d7100 100644 --- a/dts/arm/nxp/nxp_lpc55S0x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S0x_common.dtsi @@ -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>; diff --git a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi index d4343d5d8d2..0c8d75683fd 100644 --- a/dts/arm/nxp/nxp_lpc55S1x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S1x_common.dtsi @@ -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>; diff --git a/dts/arm/nxp/nxp_lpc55S3x_common.dtsi b/dts/arm/nxp/nxp_lpc55S3x_common.dtsi index 858cc72ecb8..c2a362fbfde 100644 --- a/dts/arm/nxp/nxp_lpc55S3x_common.dtsi +++ b/dts/arm/nxp/nxp_lpc55S3x_common.dtsi @@ -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>;