This article documents the process of adding hardware support for a 2-Channel CAN HAT from WaveShare using dual Microchip MCP2515 controllers over SPI on the NXP i.MX93 FRDM evaluation board.
By default, the board exposes native FlexCAN interfaces, but utilizing a popular Raspberry Pi-compatible CAN HAT requires customizing the Linux device tree and kernel configuration.
Hardware: NXP i.MX93 FRDM board, 2-Channel CAN HAT.
Software: NXP Linux BSP (Tested in 6.18.y).
Toolchain: Toolchain obtained from Yocto (Refer to the 4.5.12 How to build U-Boot and Kernel in standalone environment from i.MX Linux User's Guide).
We need to edit the main board device tree file arch/arm64/boot/dts/freescale/imx93-11x11-frdm.dts to configure the SPI master, add the dual MCP2515 nodes, assign interrupt pins, and disable conflicting native interfaces.
The key changes:
VEXP_3V3 and VEXP_5V) correctly pull up and preserve state using pinctrl-assert-gpios.16MHz clock element required by the MCP2515 crystal oscillators.flexcan2 nodes sharing pins.spidev dummy node with two microchip,mcp2515 nodes, adding two distinct Chip Select (CS) pins (GPIO2_IO08 and GPIO2_IO07) and mapping the respective hardware interrupts (GPIO2_IO23 and GPIO2_IO25).diff --git a/arch/arm64/boot/dts/freescale/imx93-11x11-frdm.dts b/arch/arm64/boot/dts/freescale/imx93-11x11-frdm.dts
index 18afe964e020..7b3af73f144f 100644
--- a/arch/arm64/boot/dts/freescale/imx93-11x11-frdm.dts
+++ b/arch/arm64/boot/dts/freescale/imx93-11x11-frdm.dts
@@ -134,6 +134,7 @@ reg_vexp_3v3: regulator-vexp-3v3 {
compatible = "regulator-fixed";
regulator-name = "VEXP_3V3";
gpio = <&pcal6524 2 GPIO_ACTIVE_HIGH>;
+ pinctrl-assert-gpios = <&pcal6524 2 GPIO_ACTIVE_HIGH>;
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
enable-active-high;
@@ -144,6 +145,7 @@ reg_vexp_5v: regulator-vexp-5v {
compatible = "regulator-fixed";
regulator-name = "VEXP_5V";
gpio = <&pcal6524 8 GPIO_ACTIVE_HIGH>;
+ pinctrl-assert-gpios = <&pcal6524 8 GPIO_ACTIVE_HIGH>;
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
enable-active-high;
@@ -269,6 +271,15 @@ K3: user_btn2 {
interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
};
};
+
+ clocks {
+ clk16m: clk16m {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <16000000>;
+ clock-output-names = "clk16m";
+ };
+ };
};
&adc1 {
@@ -292,7 +303,7 @@ &flexcan2 {
pinctrl-0 = <&pinctrl_flexcan2>;
pinctrl-1 = <&pinctrl_flexcan2_sleep>;
xceiver-supply = <®_can2_stby>;
- status = "okay";
+ status = "disabled";
};
&mu1 {
@@ -620,15 +631,29 @@ typec1_dr_sw: endpoint {
&lpspi3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_lpspi3>;
- cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>;
- pinctrl-assert-gpios = <&pcal6408 0 GPIO_ACTIVE_HIGH>;
+ cs-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>, <&gpio2 7 GPIO_ACTIVE_LOW>;
+ pinctrl-assert-gpios = <&pcal6408 0 GPIO_ACTIVE_LOW>;
status = "okay";
- spidev0: spi@0 {
+ can0: can@0 {
+ compatible = "microchip,mcp2515";
reg = <0>;
- compatible = "lwn,bk4";
- spi-max-frequency = <1000000>;
+ clocks = <&clk16m>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <23 IRQ_TYPE_LEVEL_LOW>;
+ spi-max-frequency = <10000000>;
};
+
+ can1: can@1 {
+ compatible = "microchip,mcp2515";
+ reg = <1>;
+ clocks = <&clk16m>;
+ interrupt-parent = <&gpio2>;
+ interrupts = <25 IRQ_TYPE_LEVEL_LOW>;
+ spi-max-frequency = <10000000>;
+ };
+
+
};
&lpuart1 { /* console */
@@ -894,9 +919,12 @@ MX93_PAD_GPIO_IO29__LPI2C3_SCL 0x40000b9e
pinctrl_lpspi3: lpspi3grp {
fsl,pins = <
MX93_PAD_GPIO_IO08__GPIO2_IO08 0x39e
+ MX93_PAD_GPIO_IO07__GPIO2_IO07 0x39e
MX93_PAD_GPIO_IO09__LPSPI3_SIN 0x39e
MX93_PAD_GPIO_IO10__LPSPI3_SOUT 0x39e
MX93_PAD_GPIO_IO11__LPSPI3_SCK 0x39e
+ MX93_PAD_GPIO_IO23__GPIO2_IO23 0x39e
+ MX93_PAD_GPIO_IO25__GPIO2_IO25 0x39e
>;
};
After modifying the file, compile your device tree blobs (.dtb) and deploy them to your target boot partition.
You can refer to the below post to know the process:
How to compile Linux Kernel Image and device tree using Yocto SDK.
The MCP251x driver must be enabled within the Linux kernel configuration framework. Run the configuration tool:
user@host:~/linux-imx$ make menuconfig
Navigate through the menu to enable the driver either statically ([*]) or as a module ([M]
[*] Networking support
<*> CAN bus subsystem support
<*> Raw CAN Protocol (raw access with CAN-ID filtering) <*> Broadcast Manager CAN Protocol (with content filtering)<*> CAN Gateway/Router (with netlink configuration)And:
Device Drivers
[*] Network device support
<*> CAN Device Drivers
CAN SPI interfaces
<*> Microchip MCP251x and MCP25625 SPI CAN controllers<*> Microchip MCP251xFD SPI CAN controllers
Save your configuration and compile your kernel/modules.
Once the board boots with the new device tree and kernel, you should see two new network interfaces listed under ip link show (can0 and can1).
Now, you can setup the CAN interfaces:
$ sudo ip link set can0 up type can bitrate 1000000
$ sudo ip link set can1 up type can bitrate 1000000
$ sudo ifconfig can0 txqueuelen 65536
$ sudo ifconfig can1 txqueuelen 65536
Connect the HAT in loopback:
From Interface can0:
candump can0
From interface can1:
cansend can1 000#11.22.33.44
Hope this can be helpful.
Best regards,
Salas.