Guide to Enabling MCP251x (MCP2515) on the TX2 (SPI CAN)

Given the success of my original guide for the TX1, I figured I should create a TX2 version. I glossed over some of the specifics, but I’d be happy to go into more detail as needed.

Special Note: The Tegra’s SPI logic level is 1.8V, the MCP2515 runs on 3.3V and expects a logic level of 0.9*VDD (~2.97V) to latch. Thus you’ll need a level shifter between the TX1 and the MCP2515 to bridge the gap if you’re using J23 or J22. It is possible that J21 can be set to 3.3V by setting the jumper on J24, but I haven’t tested that.

Acronyms/Definitions

  • DTB - Device Tree Blob, a compiled form of the device tree loaded by the kernel
  • DTS - Plain-text file describing the linux device tree (files with .dts or .dtsi extension)

Kernel Configuration

First you’re going to want to head to the embedded download center and get a copy of the “L4T Documentation” for your release.

Go to the ‘Kernel Customization’ section and follow instructions to download and build the kernel source. Make sure everything builds with the tegra18_defconfig before moving on.

Now that your sources are ready and configured navigate to the source directory and run

make menuconfig

The symbols you need to activate are:

  • CAN_DEV=y
  • CAN_MCP251X=m

Search the device tree using ‘/’ and jump to the location using the number key in parentheses.

Alternatively you can find it in the menu:

<*> Networking support --->
    <*> CAN bus subsystem support --->
        CAN Device Drivers --->
            <M> Microchip MCP251x SPI CAN controllers

Re-build the kernel and modules with the new configuration and copy everything to the TX2.

  • Image should go in /boot
  • Modules should go in /lib/modules

Device Tree
Now that the driver has been activated in the kernel we need to tell the OS where to find the chip. This is done by adding an entry to the device tree.

Optional: If you are using a different carrier board you may want to check the path of the DTS files. To do this boot your TX2 and SSH in, then run:

dmesg | grep "DTS File Name"

For the TX2 devboard you should see a path to tegra186-quill-p3310-1000-c03-00-base.dts

Now that we have the DTS path navigate back to your source directory and follow it to the file:

cd <kernel_source>
cd hardware/nvidia/platform/t18x/quill/kernel-dts/

Now we can add our mcp2515 chip definition!

Here is the documentation for the mcp251x driver located at:
<kernel_source>/kernel/kernel-4.4/Documentation/devicetree/bindings/net/can/microchip,mcp251x.txt

* 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.
 - reg: SPI chip select.
 - clocks: The clock feeding the CAN controller.
 - interrupt-parent: The parent interrupt 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>;
    };

Before adding anything to the dts file you need to determine what spi bus address you need. From trial and error the addresses and corresponding outputs for the TX2 devboard seem to be as follows:

/*
 * SPI Bus Addresses
 *
 * spi0 = spi@3210000 - J23 Display Expansion Connector, SPI2.0
 * spi0 = spi@3210000 - J22 Camera Expansion Connector, SPI2.1
 * spi1 = spi@c260000 - J23 Display Expansion Connector, SPI0
 * spi3 = spi@3240000 - J21 Expansion Header, SPI1
 */

For my example I will choose SPI2.1 on the Camera Expansion Connector (pins [62,64,66,68] on J22).

/* J22 Camera Expansion Connector SPI2.1 */
spi@3210000 {
    can@1 {
        compatible = "microchip,mcp2515";
        reg = <0x1>;

        clocks = <&<b>CLOCK_DEFINITION</b>>;
        interrupt-parent = <&<b>INTERRUPT_PARENT</b>>;
        interrupts = <<b>INTERRUPT_GPIO</b> IRQ_TYPE_EDGE_FALLING>;
        spi-max-frequency = <2000000>;
    };
};

Note that I used can@1 and reg = <0x1> for chip select 1.

Now we have to fill in the optional parameters.

First the mcp251x driver needs to know what frequency its oscillator is running at. For my example I have a 16MHz crystal oscillator connected to the MCP2515. I will add a definition to the device tree describing it, and then link it to the driver’s clocks field.

clocks {
        /* 16MHz external crystal oscillator */
        clk16m: mcp251x_osc {
            #clock-cells = <0>;
            compatible = "fixed-clock";
            clock-frequency = <16000000>;
        };
    };

Next we need to choose an interrupt pin. Since I am using J22 I could use one of the GPIOs already configured as an input. But instead I will pick an arbitrary output and reconfigure it in the device tree to be used as the interrupt pin. Lets use CAM0_PWDN (pin 93 on J22).

To figure out what GPIO# this is, I will go back to the embedded download center and download the Jetson TX2 Series Pinmux spreadsheet. Looking through the designations I find that GPIO0_CAM0_PWR is routed to GPIO3_PR.00.

Now I can create a gpio-hog definition to reserve the pin for our use and reconfigure it as an input.

tegra_main_gpio: gpio@2200000 {
        /* Hog CAM0_PWDN as Input. */
        mcp2515_int {
            gpio-hog;
            input;
            gpios = <TEGRA_MAIN_GPIO(R, 0) 0>;
            label = "mcp2515_int";
        };
    };

For more information on GPIO hogging see cospan’s post here.

Now that we have the supporting blocks added we can finish off our mcp2515 definition!

spi@3210000 {
    can@1 {
        compatible = "microchip,mcp2515";
        reg = <0x1>;

        clocks = <&clk16m>;
        interrupt-parent = <&tegra_main_gpio>;
        interrupts = <TEGRA_MAIN_GPIO(R, 0) IRQ_TYPE_EDGE_FALLING>;
        spi-max-frequency = <2000000>;
    };
};

Edit: here is a completed dtsi file for a 16MHz MCP2515 connected to SPI0 on the J23 Expansion Connector.

Now follow the Kernel Customization -> Building the NVIDIA Kernel section to build and flash your new dtb.

Bringing CAN interface up
https://elinux.org/Bringing_CAN_interface_up

Debugging
If your device is not probing add #define DEBUG 1 to the top of <kernel_source>/kernel/kernel-4.4/drivers/net/can/spi/mcp251x.c before compiling to activate the driver debug prints.

Reserved

Reserved

Hi!

I’m trying to connect a MCP2515 module to J21 expansion header.

My version of Jetpack already has a MCP2515 module, so:

sudo modprobe mcp251x

dmesg output:

[ 3609.239709] CAN device driver interface

Now, based on https://elinux.org/Jetson/TX2_SPI, I’m trying to update the device-tree, using your instructions, but I got an error on “gpios = <TEGRA_MAIN_GPIO(R, 0) 0>;

Error: extracted_proc.dts:10049.26-27 syntax error
FATAL ERROR: Unable to parse input tree

It’s not clear where I have to put the gpio-hog definition and clock definition…

Thanks!
Mauricio

Hi Mauricio,

Here’s the full file that we use for our board so you can see a complete example. I also added the link into the guide.

The gpio-hog and clock definitions are their own blocks in this file within the root ‘{ };’ block.

Hi. This is an old thread but I have problem about using CAN-SPI and TX2 together.

I am using CAN-SPI board which uses MCP2515 and MCP2551. Device details can be found here.

I am using elroy carrier module with TX2. What steps should I complete in order to get data from CAN-SPI board?

Looks like your carrier board has SPI on the the System Connector (P5). You’ll need to figure out what SPI# that is either through trial and error using a logic analyzer/scope, or by simple asking Connect Tech. Once you hook up to the SPI you’ll need to modify the dtsi and Image for your carrier board. Again Connect Tech should be able to provide those files.

First step is to get set up to flash the board and download the source. Once you can successfully flash the existing known-working image you can move on to activating the driver.

Thanks for your reply.
I have done the loopback test (jumpering miso and mosi lines). Test succed.

The question is :
I convert the CAN data to SPI with MCP2515. And I connected the SPI out into to the my board.
Should not I just read the SPI port without considering the CAN?

My CAN-SPI board is here : https://www.mikroe.com/can-spi-board
It uses MCP2515.

Physical connection ;

CAN_SPI MOSI - Elroy Carrier MISO
CAN_SPI MISO - Elroy Carrier MOSI
CAN_SPI SCK - Elroy Carrier SPI_CLK
CAN_SPI CS - Elroy Carrier SPI_CS0#

Can I read the SPI port with just reading SPI port operations using spi driver (spidev). Or Should I configure the kernel as you explain in the topic.

Thanks for your reply.

You can use spidev to communicate with the MCP2515, but don’t do that for more than validation. If you are using CAN then you will need to get the driver set up so that you can use the socketcan interface. Trying to transfer data with spidev would be slow and painful.

Ok, I will install the drivers according your topic , thanks for your reply.

But there are some questions in my mind that I can’t understand.

Is the driver of the MCP2515 manipulates the SPI port as a CAN? By this way, userspace programs thinks that they talk with CAN interface, but in reality, physical connection is on SPI port. Am I wrong ?

If I am wrong, question is How can we talk CAN interface while the physical connection is on SPI port?

Yes, my main goal is get CAN data, but my carrier board has no built-in can, so I have to convert it to SPI over MCP2515.
In addittion, I cannot get data by spidev using standart test program (spidev_test.c).

Best Regards

You are correct, the driver will take care of the SPI port for you. Your program will talk through a socketcan interface, for example:

$ ifconfig can0
can0: flags=193<UP,RUNNING,NOARP>  mtu 72
        unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  txqueuelen 1000  (UNSPEC)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

You’ll be able to use can-utils or any sort of socketcan library to connect to ‘can0’ at that point

Hi again wilkinsw,

After running these commands ;

modprobe mcp251x
modprobe can

I am able to see the modules by lsmod | grep can ;

can_raw   11022 0
can_dev    17212 1 mcp251x
can  50049 can_raw

but,

dmesg | grep mcp251x

gives ;

[ 5.542194] mcp251x : probe of spi0.0 failed with error -2

I did’nt modify the device tree since I am using carrier board.

There is no can0 file, by checking ;

ls /sys/bus/spi/devices/spi0.0/

So ifconfig dont shot the can0 interface. I think, it is based on editing the dts, right ?
Are all these means that the problem is on the DTS ?

You are correct, next step will be the modifying the device tree (DTS). The DTS gives the driver important information such as the clock rate (for calculating your CAN-bus bitrate) and the interrupt pin for receiving data. Without an entry in the device tree the driver won’t probe.

You can check what your device tree’s source file is using: dmesg | grep dtsi

You may need to contact your supplier to get the correct source for your carrier board.

Thanks for all wilkins,

Can I edit the device tree without creating new image , flashing kernel ? Or the supplier can give information about whole process ?

Yes I should get these files from supplier.

I haven’t done it but I believe you can modify your DTB in place. Take a look at this page for instructions on how to add the spi-dev driver. It will be very similar to what you are trying to do here:

Spidev is already in my dtb.

I have done the loopback test (short circuiting the miso and mosi). Test succeed.

I can see this file ;

/dev/spidev0.0

So, how can I read can frames thorugh SPI. You have already told that reading spi is painful , but I want to try it before I get the related files from supplier.

That guide has a section on how to compile your DTB in place. That it is about SPI is not relevant

In addition;

I have tested my mcp2515 chip by raspi with disconnecting the interrupt pin, and I can get data.

So, should I add interrupt pin to the dts, since it works without interrupt.

If it is, How Can I configure my dts ?

Modifying the DTS is covered in the first post. You can either try to do it in place following the instructions in that spi-dev guide or you can get the source from your vendor and cross compile it.

Hi wilkins,

After the editing the dts file, problem still continues.
I switched to tx2 development board, I am not using elroy for now.
I have edited the dts as you explained in the topic.

Some outputs that can help the understanding the problem ;

dmesg | grep mcp

    [    0.460666] GPIO line 456 (mcp2515_int) hogged as input
    [    1.396458] parse_throttle_dt_data: clk=mcpu type=1
    [    5.378449] mcp251x spi3.1: Cannot initialize MCP2515. Wrong wiring?
    [    5.408511] mcp251x spi3.1: Probe failed, err=19

lsmod

Module                  Size  Used by
bnep                   19334  2
fuse                  119883  4
zram                   30209  4
overlay                54798  0
hid_logitech_hidpp     25399  0
bcmdhd                985232  0
hid_logitech_dj        15838  0
cfg80211              726136  1 bcmdhd
mcp251x                14355  0
can_dev                17276  1 mcp251x
spidev                 14763  0
ov5693                 34198  0
nvgpu                1728981  33
bluedroid_pm           16187  0
ip_tables              21475  0
x_tables               38080  1 ip_tables

lsmod | grep spi 

[    0.431529] iommu: Adding device 3210000.spi to group 11
[    0.431863] iommu: Adding device c260000.spi to group 12
[    0.432189] iommu: Adding device 3240000.spi to group 13
[    5.378449] mcp251x spi3.1: Cannot initialize MCP2515. Wrong wiring?
[    5.408511] mcp251x spi3.1: Probe failed, err=19

My physical connection ;

MCP2515 MOSI - J21 Mosi
MCP2515 MISO - J21 Miso
MCP2515 SCK - J21 SCK
MCP2515 INT - not connected to the tx2

And there is a spidev3.0 under the /dev.

Before making and flashing to kernel to the jetson tx2, I have edited the dts file under hardware/nvidia/platform/t18x/quill/kernel-dts/tegra186-quill-p3310-1000-c03-00-base.dts.

I have added these lines that you provide ;

clocks {
        /* 16MHz external crystal oscillator */
        clk16m: mcp251x_osc {
            #clock-cells = <0>;
            compatible = "fixed-clock";
            clock-frequency = <8000000>;
        };
    };

tegra_main_gpio: gpio@2200000 {
        /* Hog CAM0_PWDN as Input. */
        mcp2515_int {
            gpio-hog;
            input;
            gpios = <TEGRA_MAIN_GPIO(R, 0) 0>;
            label = "mcp2515_int";
        };
    };

spi@3240000 {
    can@1 {
        compatible = "microchip,mcp2515";
        reg = <0x1>;

        clocks = <&clk16m>;
        interrupt-parent = <&tegra_main_gpio>;
        interrupts = <TEGRA_MAIN_GPIO(R, 0) IRQ_TYPE_EDGE_FALLING>;
        spi-max-frequency = <2000000>;
    };
};

And then I have build the kernel and flashed to the tx2 dev board.
But problem still continues.

Jetpack version 4.3, (32.3.1)