drivers: can: stm32h7: fdcan: add support for domain clock and divider
Add support for specifying the domain/kernel clock along with a common
clock divider for the STM32H7 CAN controller driver via devicetree.
Previously, the driver only supported using the PLL1_Q clock for
domain/kernel clock, but now the driver defaults to the HSE clock, which is
the chip default. Update existing boards to continue to use the PLL1_Q
clock.
Signed-off-by: Henrik Brix Andersen <hebad@vestas.com>
diff --git a/boards/arm/arduino_giga_r1/arduino_giga_r1_m7.dts b/boards/arm/arduino_giga_r1/arduino_giga_r1_m7.dts
index ef3544e..9cc042f 100644
--- a/boards/arm/arduino_giga_r1/arduino_giga_r1_m7.dts
+++ b/boards/arm/arduino_giga_r1/arduino_giga_r1_m7.dts
@@ -139,6 +139,8 @@
status = "okay";
pinctrl-0 = <&fdcan2_tx_pb13 &fdcan2_rx_pb5>;
pinctrl-names = "default";
+ clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000100>,
+ <&rcc STM32_SRC_PLL1_Q FDCAN_SEL(1)>;
bus-speed = <125000>;
bus-speed-data = <1000000>;
};
diff --git a/boards/arm/arduino_portenta_h7/arduino_portenta_h7-common.dtsi b/boards/arm/arduino_portenta_h7/arduino_portenta_h7-common.dtsi
index 87a22fa..87d85f2 100644
--- a/boards/arm/arduino_portenta_h7/arduino_portenta_h7-common.dtsi
+++ b/boards/arm/arduino_portenta_h7/arduino_portenta_h7-common.dtsi
@@ -105,6 +105,8 @@
&fdcan1 {
pinctrl-0 = <&fdcan1_rx_pb8 &fdcan1_tx_ph13>;
pinctrl-names = "default";
+ clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000100>,
+ <&rcc STM32_SRC_PLL1_Q FDCAN_SEL(1)>;
};
&rtc {
diff --git a/boards/arm/nucleo_h743zi/nucleo_h743zi.dts b/boards/arm/nucleo_h743zi/nucleo_h743zi.dts
index f939644..ad88d87 100644
--- a/boards/arm/nucleo_h743zi/nucleo_h743zi.dts
+++ b/boards/arm/nucleo_h743zi/nucleo_h743zi.dts
@@ -170,6 +170,8 @@
&fdcan1 {
pinctrl-0 = <&fdcan1_rx_pd0 &fdcan1_tx_pd1>;
pinctrl-names = "default";
+ clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000100>,
+ <&rcc STM32_SRC_PLL1_Q FDCAN_SEL(1)>;
bus-speed = <125000>;
bus-speed-data = <1000000>;
status = "okay";
diff --git a/boards/arm/stm32h7b3i_dk/stm32h7b3i_dk.dts b/boards/arm/stm32h7b3i_dk/stm32h7b3i_dk.dts
index 3547017..b8b781b 100644
--- a/boards/arm/stm32h7b3i_dk/stm32h7b3i_dk.dts
+++ b/boards/arm/stm32h7b3i_dk/stm32h7b3i_dk.dts
@@ -152,6 +152,8 @@
&fdcan1 {
pinctrl-0 = <&fdcan1_rx_pa11 &fdcan1_tx_pa12>;
pinctrl-names = "default";
+ clocks = <&rcc STM32_CLOCK_BUS_APB1_2 0x00000100>,
+ <&rcc STM32_SRC_PLL1_Q FDCAN_SEL(1)>;
phys = <&transceiver0>;
bus-speed = <125000>;
bus-speed-data = <1000000>;
diff --git a/drivers/can/can_stm32h7_fdcan.c b/drivers/can/can_stm32h7_fdcan.c
index 9b75932..977da15 100644
--- a/drivers/can/can_stm32h7_fdcan.c
+++ b/drivers/can/can_stm32h7_fdcan.c
@@ -14,18 +14,29 @@
#include <stm32_ll_rcc.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
+#include <zephyr/sys/util.h>
LOG_MODULE_REGISTER(can_stm32h7, CONFIG_CAN_LOG_LEVEL);
#define DT_DRV_COMPAT st_stm32h7_fdcan
+/* This symbol takes the value 1 if one of the device instances */
+/* is configured in dts with a domain clock */
+#if STM32_DT_INST_DEV_DOMAIN_CLOCK_SUPPORT
+#define STM32H7_FDCAN_DOMAIN_CLOCK_SUPPORT 1
+#else
+#define STM32H7_FDCAN_DOMAIN_CLOCK_SUPPORT 0
+#endif
+
struct can_stm32h7_config {
mm_reg_t base;
mem_addr_t mrba;
mem_addr_t mram;
void (*config_irq)(void);
const struct pinctrl_dev_config *pcfg;
- struct stm32_pclken pclken;
+ size_t pclk_len;
+ const struct stm32_pclken *pclken;
+ uint8_t clock_divider;
};
static int can_stm32h7_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
@@ -72,6 +83,7 @@
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);
+ uint32_t cdiv;
ARG_UNUSED(dev);
@@ -80,9 +92,12 @@
return -EIO;
}
- *rate = rate_tmp;
-
- LOG_DBG("rate=%d", *rate);
+ cdiv = FIELD_GET(FDCANCCU_CCFG_CDIV, FDCAN_CCU->CCFG);
+ if (cdiv == 0U) {
+ *rate = rate_tmp;
+ } else {
+ *rate = rate_tmp / (cdiv << 1U);
+ }
return 0;
}
@@ -94,22 +109,32 @@
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
int ret;
- LL_RCC_SetFDCANClockSource(LL_RCC_FDCAN_CLKSOURCE_PLL1Q);
-
if (!device_is_ready(clk)) {
LOG_ERR("clock control device not ready");
return -ENODEV;
}
- ret = clock_control_on(clk, (clock_control_subsys_t)&stm32h7_cfg->pclken);
+ if (IS_ENABLED(STM32H7_FDCAN_DOMAIN_CLOCK_SUPPORT) && (stm32h7_cfg->pclk_len > 1)) {
+ ret = clock_control_configure(clk,
+ (clock_control_subsys_t)&stm32h7_cfg->pclken[1],
+ NULL);
+ if (ret < 0) {
+ LOG_ERR("Could not select can_stm32fd domain clock");
+ return ret;
+ }
+ }
+
+ ret = clock_control_on(clk, (clock_control_subsys_t)&stm32h7_cfg->pclken[0]);
if (ret != 0) {
LOG_ERR("failure enabling clock");
return ret;
}
- if (!LL_RCC_PLL1Q_IsEnabled()) {
- LOG_ERR("PLL1Q clock must be enabled!");
- return -EIO;
+ if (stm32h7_cfg->clock_divider != 0U) {
+ can_mcan_enable_configuration_change(dev);
+
+ FDCAN_CCU->CCFG = FDCANCCU_CCFG_BCC |
+ FIELD_PREP(FDCANCCU_CCFG_CDIV, stm32h7_cfg->clock_divider >> 1U);
}
return 0;
@@ -204,16 +229,18 @@
PINCTRL_DT_INST_DEFINE(n); \
CAN_MCAN_DT_INST_CALLBACKS_DEFINE(n, can_stm32h7_cbs_##n); \
\
+ static const struct stm32_pclken can_stm32h7_pclken_##n[] = \
+ STM32_DT_INST_CLOCKS(n); \
+ \
static const struct can_stm32h7_config can_stm32h7_cfg_##n = { \
.base = CAN_MCAN_DT_INST_MCAN_ADDR(n), \
.mrba = CAN_MCAN_DT_INST_MRBA(n), \
.mram = CAN_MCAN_DT_INST_MRAM_ADDR(n), \
.config_irq = stm32h7_mcan_irq_config_##n, \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
- .pclken = { \
- .enr = DT_INST_CLOCKS_CELL(n, bits), \
- .bus = DT_INST_CLOCKS_CELL(n, bus), \
- }, \
+ .pclken = can_stm32h7_pclken_##n, \
+ .pclk_len = DT_INST_NUM_CLOCKS(n), \
+ .clock_divider = DT_INST_PROP_OR(n, clk_divider, 0) \
}; \
\
static const struct can_mcan_config can_mcan_cfg_##n = \
diff --git a/dts/bindings/can/st,stm32h7-fdcan.yaml b/dts/bindings/can/st,stm32h7-fdcan.yaml
index 1d258ea..14f6fad 100644
--- a/dts/bindings/can/st,stm32h7-fdcan.yaml
+++ b/dts/bindings/can/st,stm32h7-fdcan.yaml
@@ -16,3 +16,29 @@
interrupt-names:
required: true
+
+ clk-divider:
+ type: int
+ enum:
+ - 1
+ - 2
+ - 4
+ - 6
+ - 8
+ - 10
+ - 12
+ - 14
+ - 16
+ - 18
+ - 20
+ - 22
+ - 24
+ - 26
+ - 28
+ - 30
+ description: |
+ Divides the kernel clock giving the time quanta clock that is fed to the FDCAN core
+ (FDCAN_CCU->CCFG CDIV register bits). Note that the divisor is common to all
+ 'st,stm32h7-fdcan' instances.
+
+ Divide by 1 is the peripherals reset value and remains set unless this property is configured.