cancel
Showing results for 
Search instead for 
Did you mean: 

CAN Bitrate does not match commanded bitrate [Linux][Socket CAN]

ADeck
Associate III

I have enabled the FD-CAN device in the device tree with the following entry

&m_can2 {
 	pinctrl-names = "default", "sleep";         Template:Highlight
 	pinctrl-0 = <&m_can2_pins_a>;               Template:Highlight
 	pinctrl-1 = <&m_can2_sleep_pins_a>;         Template:Highlight
 	status = "okay";                            Template:Highlight 
 };

upon booting Linux I run the following commands to initalize the interface to a bit rate of 500,000

ip link set can1 type can bitrate 500000
ip link set up can1

and send a can message using the can-utils binaries with.

cansend can1 3E8#112233

Using a Saleae Logic analyzer I capture the following waveform which the bit-time measures out to be around 1.91 microseconds. Performing the inverse of this measurement provides the measured bit-rate to be 523,560 bps which is nowhere near the 500,000 I commanded.

0690X000009Z6oJQAS.pngI assume I have a clocking error somewhere but i haven't changed any of the clocking settings. What am I doing wrong?

5 REPLIES 5
Olivier GALLIEN
ST Employee

Hi @ADeck​ 

Could you please share the fdcan_k value applied and the return of command :

ip -details link show can1

Thanks,

Olivier

Olivier GALLIEN
In order to give better visibility on the answered topics, please click on 'Accept as Solution' on the reply which solved your issue or answered your question.
CC
Associate II

I encountered this issue as well. My colleague found a workaround - manually unbinding and rebinding the m_can driver should fix the clock rate. I'm curious as to why this is happening in the first place, though.

root@stm32mp1:/sys/bus/platform/drivers/m_can# ip -details link show can0
2: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can state STOPPED (berr-counter tx 0 rx 0) restart-ms 0
          m_can: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..512 brp-inc 1
          m_can: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..32 dbrp-inc 1
          clock 49500000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
root@stm32mp1:/sys/bus/platform/drivers/m_can# echo 4400e000.can > unbind
root@stm32mp1:/sys/bus/platform/drivers/m_can# ip -details link show can0
Device "can0" does not exist.  
root@stm32mp1:/sys/bus/platform/drivers/m_can# echo 4400e000.can > bind
[65237.797444] m_can 4400e000.can: m_can device registered (irq=31, version=32)
root@stm32mp1:/sys/bus/platform/drivers/m_can# ip -details link show can0
5: can0: <NOARP,ECHO> mtu 16 qdisc noop state DOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0
    can state STOPPED (berr-counter tx 0 rx 0) restart-ms 0
          m_can: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..512 brp-inc 1
          m_can: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..32 dbrp-inc 1
          clock 54000000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

root@stm32mp1:~# ip link set can1 type can bitrate 500000
 
root@stm32mp1:~# ip link set up can1
 
root@stm32mp1:~# ip -details link show can1
3: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 
	  bitrate 496240 sample-point 0.873 
	  tq 31 prop-seg 27 phase-seg1 27 phase-seg2 8 sjw 1
	  m_can: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..512 brp-inc 1
	  m_can: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..32 dbrp-inc 1
	  clock 31263158numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

Interesting the bitrate is reporting yet a different number than the actual calculated bitrate.

I have not changed fdcan_k from it's default setting in the OpenSTLinux distribution. This is what is present in my device tree

 m_can1: can@4400e000 {
 	compatible = "bosch,m_can";                       
 	reg = <0x4400e000 0x400>, <0x44011000 0x1400>;    Template:Highlight
 	reg-names = "m_can", "message_ram";
 	interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
 		     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 	interrupt-names = "int0", "int1";
 	clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
 	clock-names = "hclk", "cclk";
 	bosch,mram-cfg = <0x0 0 0 32 0 0 2 2>;
 	status = "disabled";
 };
 
 m_can2: can@4400f000 {
 	compatible = "bosch,m_can";
 	reg = <0x4400f000 0x400>, <0x44011000 0x2800>;    Template:Highlight
 	reg-names = "m_can", "message_ram";
 	interrupts = <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>,
 		     <GIC_SPI 22 IRQ_TYPE_LEVEL_HIGH>;
 	interrupt-names = "int0", "int1";
 	clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
 	clock-names = "hclk", "cclk";
 	bosch,mram-cfg = <0x1400 0 0 32 0 0 2 2>;         Template:Highlight
 	status = "disabled";
 };

Searching all the device tree files for it does not yield a definition. Am I looking the wrong spot?

grep -r "FDCAN_K" /openstlinux-4.19-thud-mp1-19-02-20/build-openstlinuxeglfs-stm32mp1/workspace/sources/linux-stm32mp/arch/arm/boot/dts/*
 
arch/arm/boot/dts/stm32mp157c.dtsi:			clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
arch/arm/boot/dts/stm32mp157c.dtsi:			clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi:			clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;
arch/arm/boot/dts/stm32mp157c-m4-srm.dtsi:			clocks = <&rcc CK_HSE>, <&rcc FDCAN_K>;

ADeck
Associate III

This work around does work for me as well with both can0 and can1. This is very interesting. Would defiantly like some answers here. Maybe

@Community member​ can weigh in here. It's very interesting that the reported clock speed changes and the reported bitrate is spot on. The logic analyzer is now showing 2 us bit-times so the measured timing is correct now.

Before:

ip -details link show can1
3: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0 
	  bitrate 496240 sample-point 0.873 
	  tq 31 prop-seg 27 phase-seg1 27 phase-seg2 8 sjw 1
	  m_can: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..512 brp-inc 1
	  m_can: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..32 dbrp-inc 1
	  clock 31263158numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

After:

ip -details link show can1
7: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc fq_codel state UNKNOWN mode DEFAULT group default qlen 10
    link/can  promiscuity 0 
    can state ERROR-ACTIVE (berr-counter tx 127 rx 0) restart-ms 0 
	  bitrate 500000 sample-point 0.863 
	  tq 90 prop-seg 9 phase-seg1 9 phase-seg2 3 sjw 1
	  m_can: tseg1 2..256 tseg2 1..128 sjw 1..128 brp 1..512 brp-inc 1
	  m_can: dtseg1 1..32 dtseg2 1..16 dsjw 1..16 dbrp 1..32 dbrp-inc 1
	  clock 33000000numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535

AntonioST
ST Employee

As you can see from the output of command "ip show", the clock used by CAN controller changes between "Before" and "After". It's reported in the last printed line, but the missing separator space between the clock value and the text "numtxqueues" makes it hard to read.

Now, the reason for this clock change is that CAN controller and LTDC display controller are by default on the same PLL output.

The display controller has to modify the divider at the output of the PLL to adapt the clock frequency to the display characteristics.

But CAN framework, in Linux, reads the clock frequency during probe and cache it, considering it immutable.

The clock source is not locked by the CAN driver at probe; it is locked only later on when the CAN interface goes UP. Being the clock not locked, the frequency modification requested by LTDC is allowed by the clock framework!

We can say this is a "limitation" (or a real "bug") of the Linux CAN framework. It should read the clock frequency when the interface goes UP.

So CAN driver is probed first, reads the initial clock frequency and keeps it for the next user configuration. Then display is probed, and it changes the clock frequency. The unbind/bind of CAN cause the CAN driver to read again the clock frequency. This explains the workaround!

You can get the full clock tree with the following command at Linux console:

cat /sys/kernel/debug/clk/clk_summary

How to solve it!

The easier way is to use independent clock sources for CAN and LTDC.

The modification that will be published in next deliveries of OpenSTLinux moves CAN to PLL4_R and left the LTDC on PLL4_Q.

In mean time, you can either use the workaround above (it works fine) or you can anticipate the same modifications of OpenSTLinux as follows.

If you use trusted boot, you have to modify the DT in TF-A code:

sed -i s/CLK_FDCAN_PLL4Q/CLK_FDCAN_PLL4R/ fdts/stm32mp1*

If you use basic boot, you have to modify the DT in U-Boot code:

sed -i s/CLK_FDCAN_PLL4Q/CLK_FDCAN_PLL4R/ arch/arm/dts/stm32mp1*