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:
parent
1c0a43ec45
commit
bbfc1f905c
10 changed files with 197 additions and 35 deletions
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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; \
|
||||
\
|
||||
|
|
|
@ -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; \
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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 *) \
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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>;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue