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.