Colibri iMX6 Linux: How to assign INT pin of MCP251x CAN to different SOM GPIO pin?

Hi all!
I struggle with some problem and hope to get some help from community.

SoM board: Colibri iMX6 running TDX Wayland with XWayland 5.7.0+build.20 (dunfell)
Installed on custom carrier board and I am attempting to configure CAN bus.
I want to use CAN via SPI in opposite to flex CAN due to some carrier board design.
My carrier board has MCP251x chip on it and CAN bus works well while testing with another Colibri SoM under WEC2013.

My problem is that default INT pin of MCP251x chip is assigned to incorrect pin of Colibri SoM.
I opened imx6dl-colibri-eval-v3.dts file and found there the only possible reference to this IRQ pin:
which are the following two lines:
interrupt-parent = <&gpio3>;
interrupts = <27 0x2>;

However my attempts to change these two lines to my prams did not help.
I compiled device tree, updated my target SOM and CAN is no longer initializing.

My INT pin from MCP251x is connected to pin 67 of SoM.
I understand that this is GPIO1 and my two lines should look like this:

interrupt-parent = <&gpio1>;
interrupts = <67 0x2>;

However this does not work. Also I do not understand what param: 0x2 after 67 means ?
Please help.
Below is the part of the device tree in question.

/* Colibri SSP /
&ecspi4 {
mcp251x0: mcp251x@0 {
compatible = “microchip,mcp2515”;
reg = <0>;
clocks = <&clk16m>;
interrupt-parent = <&gpio3>;
interrupts = <27 0x2>;

spi-max-frequency = <10000000>;
status = “okay”;
};
/
To keep the CAN controller enabled by default,
* disable conflicting spidev. This spidev device
* enables with the devicetree overlay.
*/
spidev0: spidev@0 {
status = “disabled”;
};
};

Hi @avionics,

I believe this is happening because changing this pin is not enough. Checking the Colibri iMX6 datasheet, page 23, we can see that pin 67 is PWM2 by default:

So to turn it into a GPIO pin, you will need to change the pin function of pin 67 to GPIO. Then, your modification should work. This pin is defined inside imx6qdl-colibri.dtsi.

Check this guide for more information: Pin Multiplexing - Changing Pin Functionalities in the Linux Device Tree | Toradex Developer Center

Please let me know if you need any help with this modification.

Best Regards,
Hiago.

Hi Hiago,

Thank you for your reply. I want to clarify few things please:

  1. Yes you are right, by default the pin 67 is PWM2_Out and to disable it we must set in file:
    imx6dl-colibri-eval-v3.dts
    You are also right that the settings for PWM are in other file which is: imx6qdl-colibri.dtsi
    however the actual status “switch” is in imx6dl-colibri-eval-v3.dts

&pwm2 {
status = “disabled”;
};

  1. I still little struggle with understanding of the following two lines related to MCP251x from imx6dl-colibri-eval-v3.dts
    Can you please explain what 'interrupts= ’ line settings should be for pin 67 ?
    I understand that by default interrupts is set to 27 0x2 which means
    GPIO3_27 for pin 73, so the default INT MPC251x pin is 73 right ?

Now I probably need to set it to

interrupt-parent = <&gpio1>;
interrupts = <1 0x2>;

So the question: am I correct on my assumptions ?
and if so what would be the meaning of 0x2 (second param in interrupts line) ?

Thank you

Hi @avionics,

Yes, you’re correct. Even though the pwm2 is disabled there, the pin function will have to change somewhere in the device tree as well to activate the GPIO function. Were you able to modify the device tree to activate de GPIO function? It should be possible to do it with an overlay too if you prefer.

There are two lines related to the same pin because some pins of the module are connected to two different pins of the processor (imx6, in this case). So, with that, we can achieve more functions in the same pin. This is better explained in the Colibri iMX6 Datasheet, page 19:

Some of the i.MX6 pins are paired and share the same SODIMM pin. When using one of these
pins, make sure that the unused pin the pair is tri-stated or configurator as input to avoid
undesired behaviour and/or hardware damage. The following table list all SODIMM pins that have
more than one i.MX6 pin connected:

Pin 67, once the GPIO is activated, it uses GPIO1_IO01. Therefore, the configuration should be

interrupt-parent = <&gpio1>;
interrupts = <1 0x2>;

The default is &gpio3 and <27 0x2>, which points to GPIO3_IO27 (pin 73). So by changing it to the above, we’re changing from GPIO3_IO27 (pin 73) to GPIO1_IO01 (pin 67).

Yes, you are correct, the default is pin 73.

Also correct, can you please try that?

Checking the Documentation about this driver, there isn’t a good explanation about it:

* Microchip MCP251X stand-alone CAN controller device tree bindings

Required properties:
 - compatible: Should be one of the following:
   - "microchip,mcp2510" for MCP2510.
   - "microchip,mcp2515" for MCP2515.
   - "microchip,mcp25625" for MCP25625.
 - reg: SPI chip select.
 - clocks: The clock feeding the CAN controller.
 - interrupts: Should contain IRQ line for the CAN controller.

Optional properties:
 - vdd-supply: Regulator that powers the CAN controller.
 - xceiver-supply: Regulator that powers the CAN transceiver.

Example:
	can0: can@1 {
		compatible = "microchip,mcp2515";
		reg = <1>;
		clocks = <&clk24m>;
		interrupt-parent = <&gpio4>;
		interrupts = <13 0x2>;
		vdd-supply = <&reg5v0>;
		xceiver-supply = <&reg5v0>;
	};

However the source code of the IRQ explains it a little better:

/*
 * IRQ line status.
 *
 * Bits 0-7 are the same as the IRQF_* bits in linux/interrupt.h
 *
 * IRQ_TYPE_NONE		- default, unspecified type
 * IRQ_TYPE_EDGE_RISING		- rising edge triggered
 * IRQ_TYPE_EDGE_FALLING	- falling edge triggered
 * IRQ_TYPE_EDGE_BOTH		- rising and falling edge triggered
 * IRQ_TYPE_LEVEL_HIGH		- high level triggered
 * IRQ_TYPE_LEVEL_LOW		- low level triggered
 * IRQ_TYPE_LEVEL_MASK		- Mask to filter out the level bits
 * IRQ_TYPE_SENSE_MASK		- Mask for all the above bits
 * IRQ_TYPE_DEFAULT		- For use by some PICs to ask irq_set_type
 *				  to setup the HW to a sane default (used
 *                                by irqdomain map() callbacks to synchronize
 *                                the HW state and SW flags for a newly
 *                                allocated descriptor).
 *
 * IRQ_TYPE_PROBE		- Special flag for probing in progress
 *
 * Bits which can be modified via irq_set/clear/modify_status_flags()
 * IRQ_LEVEL			- Interrupt is level type. Will be also
 *				  updated in the code when the above trigger
 *				  bits are modified via irq_set_irq_type()
 * IRQ_PER_CPU			- Mark an interrupt PER_CPU. Will protect
 *				  it from affinity setting
 * IRQ_NOPROBE			- Interrupt cannot be probed by autoprobing
 * IRQ_NOREQUEST		- Interrupt cannot be requested via
 *				  request_irq()
 * IRQ_NOTHREAD			- Interrupt cannot be threaded
 * IRQ_NOAUTOEN			- Interrupt is not automatically enabled in
 *				  request/setup_irq()
 * IRQ_NO_BALANCING		- Interrupt cannot be balanced (affinity set)
 * IRQ_MOVE_PCNTXT		- Interrupt can be migrated from process context
 * IRQ_NESTED_THREAD		- Interrupt nests into another thread
 * IRQ_PER_CPU_DEVID		- Dev_id is a per-cpu variable
 * IRQ_IS_POLLED		- Always polled by another interrupt. Exclude
 *				  it from the spurious interrupt detection
 *				  mechanism and from core side polling.
 * IRQ_DISABLE_UNLAZY		- Disable lazy irq disable
 */
enum {
	IRQ_TYPE_NONE		= 0x00000000,
	IRQ_TYPE_EDGE_RISING	= 0x00000001,
	IRQ_TYPE_EDGE_FALLING	= 0x00000002,
	IRQ_TYPE_EDGE_BOTH	= (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
	IRQ_TYPE_LEVEL_HIGH	= 0x00000004,
	IRQ_TYPE_LEVEL_LOW	= 0x00000008,
	IRQ_TYPE_LEVEL_MASK	= (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
	IRQ_TYPE_SENSE_MASK	= 0x0000000f,
	IRQ_TYPE_DEFAULT	= IRQ_TYPE_SENSE_MASK,

	IRQ_TYPE_PROBE		= 0x00000010,

	IRQ_LEVEL		= (1 <<  8),
	IRQ_PER_CPU		= (1 <<  9),
	IRQ_NOPROBE		= (1 << 10),
	IRQ_NOREQUEST		= (1 << 11),
	IRQ_NOAUTOEN		= (1 << 12),
	IRQ_NO_BALANCING	= (1 << 13),
	IRQ_MOVE_PCNTXT		= (1 << 14),
	IRQ_NESTED_THREAD	= (1 << 15),
	IRQ_NOTHREAD		= (1 << 16),
	IRQ_PER_CPU_DEVID	= (1 << 17),
	IRQ_IS_POLLED		= (1 << 18),
	IRQ_DISABLE_UNLAZY	= (1 << 19),
};
	IRQ_TYPE_EDGE_FALLING	= 0x00000002,

Please let me know if you have any questions.

Best Regards,
Hiago.

Hi Hiago,

First of all Thank you for very detailed explanations, I can see that you are well know this subject!
So far my problem still persists, despite I did all corrections.

What I did was:

  1. Set SSP settings as followed:
    interrupt-parent = <&gpio1>;
    interrupts = <1 0x2>;

At this point yes we understand that the PWM2 is still active and shares the same pins so I did the following as well:

&pwm2 {
status = “disabled”;
};

pwm2: pwm@2084000 {
status = “disabled”;
};

/* Colibri PWM /
/
&pwm2 {/
/
#pwm-cells = <3>;/
/
pinctrl-names = “default”;/
/
pinctrl-0 = <&pinctrl_pwm2>;/
/
};*/

These 3 above are just to completely shutdown the PWM2 function. I guess it is overkill and the very first one would be enough.

Now we need to change pins from PWM2 to GPIO function so we do:

Lock up original pin control for PWM2
/pinctrl_pwm2: pwm2grp {/
/* fsl,pins = </
/
MX6QDL_PAD_GPIO_1__PWM2_OUT 0x1b0b1*/
/* MX6QDL_PAD_EIM_A21__GPIO2_IO17 0x00040*/
/* >;/
/
};*/

and now set our pin67 to GPIO1:

pinctrl_gpio_1: gpio-1 {
fsl,pins = <
MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x1b0b0

and

pinctrl_gpio_2: gpio-2 {
fsl,pins = <
MX6QDL_PAD_GPIO_1__GPIO1_IO01 0x1b0b0

So at this point I would expect my pin67 to be set as GPIO and input pin for INT signal from MCP251x chip. However I still have identically the same problem as before.

My problem description is:

I am able to initialize CAN bus with MCP2515:

5: can2: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UP mode DEFAULT group default qlen 10
link/can promiscuity 0 minmtu 0 maxmtu 0
can state ERROR-ACTIVE restart-ms 0
bitrate 500000 sample-point 0.875
tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
mcp251x: tseg1 3…16 tseg2 2…8 sjw 1…4 brp 1…64 brp-inc 1
clock 8000000

root@colibri-imx6-10559655:~# dmesg | grep can
[ 1.987974] imx_epdc_fb 20f4000.epdc: can’t get/select pinctrl
[ 2.633581] mxs_phy 20c9000.usbphy: Data pin can’t make good contact.
[ 6.485584] flexcan 2090000.flexcan: 2090000.flexcan supply xceiver not found, using dummy regulator
[ 6.538653] flexcan 2094000.flexcan: 2094000.flexcan supply xceiver not found, using dummy regulator
[ 8.511195] mcp251x spi3.0 can2: MCP2515 successfully initialized.
[ 111.596707] IPv6: ADDRCONF(NETDEV_CHANGE): can0: link becomes ready
[ 114.130425] IPv6: ADDRCONF(NETDEV_CHANGE): can1: link becomes ready
[ 116.288005] IPv6: ADDRCONF(NETDEV_CHANGE): can2: link becomes ready
[ 124.012276] can: controller area network core (rev 20170425 abi 9)
[ 124.036645] can: raw protocol (rev 20170425)

I am able to set this CAN link UP. Also I am able to send a packet, but only 1 packet, as soon as I attempt to send a second packet it does not go through.

I am unable to receive any single packet via this CAN bus

candump -t A -c -a -d -e -x can2
show no data at all.

To explain little further I have two flex CAN active and also one MCP2515 CAN active.
I am sure that my MCP2515 works well because the same hardware, while running under WEC2013 (WinCE8) is able to send and receive packets via MCP2515 without any problems.
Both of my flex CAN lines are able to send and receive packets without problems.

My MCP2515 CAN bus is able to send only single packet, after no more packets go through the pipe. Also MCP2515 is unable to receive any packets at all.

The fact that I am able to initialize MCP2515 and able to send at least one correctly received on other end packet confirms that SPI bus is OKAY.,
The problem is somewhere else but I do not know how to check that.
Can you please recommend the steps to debug or possibly you already see some mistake which I made in pin configurations for GPIO of MCP2515 ?

Thank you!

Hi

Perhaps problem is the same, but something had to change, unless 1) DT compile and 2) make new DT used by boot process failed for some reason. Quick check for this could be cat /proc/interrupts
gpio entries indicate interrupts enabled for particular gpio lines. You should see there clear difference regarding CAN INT interrupt.3

And what about that single pocket, is it properly recognized by other nodes? If not, then perhaps you configure baudrate prior to enabling CAN interface? Does MCP2515 crystal value match value specified in DT? Any activity on CAN lines? Can you verify resulting baudrate using scope (1 / minimum pulse width).

Edward

Hi @avionics,

Were you able to fix this issue? Do you have any updates?

Best Regards,
Hiago.

Hello Hiago,

I will get back to you on this in 1 week. We have some urgent project on TK1 at the moment.
So far we just added two FlexCAN on iMX6 for now. In 1 week I will get back to the MCP2515 CAN issue and will provide details if I am able to get it fixed.
Thank you.