Configuring a CAN-bus with MCP2515 over SPI in TX1

I was not be able to configure a MCP251x (SPI-CAN interface and Voltage Buffer) driver properly to have the can0 as a device. I did some modifications to the kernel in arch/arm/mach-tegra/board-ardbeg.c I took the https://devtalk.nvidia.com/default/topic/883673 as a base but the interruption pin was difficult to find, i was set the INT_MOTION pin from the J21 connector that is TEGRA_GPIO_PX2 but after replace the kernel the can0 device seems not to be present.

I also try do a modifications to the device tree but i have no success, seems that this way does not work in the TK1 too. https://turlucode.com/nvidia-jetson-tk1-spi-to-can-interface-mcp251x-mcp25625/ the kernel modifications are pretty much the same as the post.

Acording to the http://www.jetsonhacks.com/nvidia-jetson-tx1-j21-header-pinout/ information of this post i tokc the GPIO186 with seems to be the TEGRA_GPIO_PX2 accordig to the arch/arm/mach-tegra/gpio-names.h file, but iḿ not sure about this mapping.

On branch tegra-l4t-r24.2 i do almost the same modifications but change the interruption

diff --git a/arch/arm/mach-tegra/board-ardbeg.c b/arch/arm/mach-tegra/board-ardbeg.c
index 2f75e7c..de0e552 100644
--- a/arch/arm/mach-tegra/board-ardbeg.c
+++ b/arch/arm/mach-tegra/board-ardbeg.c
@@ -1007,6 +1007,35 @@ static struct spi_board_info rm31080a_e2141_spi_board[1] = {
        },
 };
 
+#define CAN_GPIO_IRQ_MCP251x_SPI TEGRA_GPIO_PX2
+
+static struct mcp251x_platform_data mcp251x_info = {
+       .oscillator_frequency = 8 * 1000 * 1000, /* CAN SPI click 5V has a 8MHz crystal */
+       .irq_flags            = IRQF_TRIGGER_FALLING|IRQF_ONESHOT,  /* This is also defined in the mcp251x driver: can be ommited */
+       .board_specific_setup = NULL, /* We don't have a board specific setup */
+       .power_enable         = NULL, /* We don't want any power enable function */
+       .transceiver_enable   = NULL, /* We don't want any transceiver enable function */
+};
+
+struct spi_board_info mcp251x_spi_board[1] = {
+       {
+               .modalias = "mcp2515", /* (or mcp2510) used chip controller */
+               .platform_data = &mcp251x_info, /* reference to the mcp251x_platform_data mcp251x_info */
+               .max_speed_hz  = 8 * 1000 * 1000, /* max speed of the used chip */
+               .chip_select   = 0, /* the spi cs usage*/
+               .bus_num = 0, 
+               .mode = SPI_MODE_0,
+       },
+};
+
+static int __init mcp251x_init(void)
+{
+       mcp251x_spi_board[0].irq = gpio_to_irq(CAN_GPIO_IRQ_MCP251x_SPI); // #define CAN_GPIO_IRQ_MCP251x_SPI TEGRA_GPIO_PK2
+       spi_register_board_info(mcp251x_spi_board, ARRAY_SIZE(mcp251x_spi_board));
+       pr_info("mcp251x_init\n");
+       return 0;
+}
+
 static int __init ardbeg_touch_init(void)
 {
        tegra_get_board_info(&board_info);
@@ -1254,6 +1283,10 @@ static void __init tegra_ardbeg_late_init(void)
 
        isomgr_init();
 
+#ifdef MCP251x
+       mcp251x_init();
+#endif
+
        if (!of_machine_is_compatible("nvidia,green-arrow" ))
                ardbeg_touch_init();

And patch this compilation issues too.

diff --git a/arch/arm64/kernel/vdso32/Makefile b/arch/arm64/kernel/vdso32/Makefile
index 0c2bc73..0ef7c9a 100644
--- a/arch/arm64/kernel/vdso32/Makefile
+++ b/arch/arm64/kernel/vdso32/Makefile
@@ -11,7 +11,7 @@ obj-vdso32 := $(addprefix $(obj)/, $(obj-vdso32))
 
 GCOV_PROFILE := n
 
-ccflags-y := -shared -fPIC -fno-common -fno-builtin -march=armv7-a
+ccflags-y := -shared -fPIC -fomit-frame-pointer -fno-common -fno-builtin -march=armv7-a
 ccflags-y += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
                $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
 asflags-y := -D__VDSO32__ -s
diff --git a/drivers/platform/tegra/tegra21_clocks.c b/drivers/platform/tegra/tegra21_clocks.c
index f539d42..f31cdc6 100644
--- a/drivers/platform/tegra/tegra21_clocks.c
+++ b/drivers/platform/tegra/tegra21_clocks.c
@@ -1061,7 +1061,7 @@ static struct clk_ops tegra_super_ops = {
  */
 static void tegra21_cpu_clk_init(struct clk *c)
 {
-       c->state = (!is_lp_cluster() == (c->u.cpu.mode == MODE_G)) ? ON : OFF;
+       c->state = ((!is_lp_cluster()) == (c->u.cpu.mode == MODE_G)) ? ON : OFF;
 }

Hitting the same road block at least Im not alone is it a Auvidea carrier?

Excited to hear if you get it working! Getting CAN set up is my next project.

I use the PCAN (USB-CAN) as alternative, pretty sure that there is a cheap way using the MCP2515 instead. I compile the driver in the TX1 change the compiler version to avoid some gcc flags errors during compilation.

http://www.peak-system.com/forum/viewtopic.php?f=59&t=1603&p=4780&hilit=tk1#p4780

Still searching for a SPI solution.

Are you sure that’s the right board file?

I think that board-ardbeg.c is only for the TK1 and probably doesn’t get loaded for the TX1

I think the one we want is board-t210ref.c, but I don’t know for sure.

This person got SPI working using the device tree. Might be worth a shot!
https://devtalk.nvidia.com/default/topic/978809/full-procedure-for-enabling-spi-on-the-tx1-/#5028842

I could enable the spidev0_0 and made working using the dtd method, as i reffer in the second link those guys ‘try’ but the dtb is in a spoiler button take a good look of the article.

I’ll check with the board-t210ref.c file that is the sugestión that i was looking for. But i have the pinmux issue yet for the interruption pin requiered for the MCP

Wargry. did you succeed at the end?

I did it myself with his modifications done to board-t210ref.c and the mcp251x driver probes my device and I can set it up. Just need to figure out the interrupt part so I can assign the pin I’m actually using and see how it performs.

But so far looks good

Thats seems a good news, can you let me know the whole path of the board-t210ref.c and witch changes you mean.

Also would be good to know how the perform the configurations with the driver setup.

Actually I still using the PCAN adapter, the MCP2515 seems a good cheap option that i like to use instead.

That board file is located at:

kernel_source/arch/arm64/mach-tegra/board-t210ref.c

Added this block to the top:

#ifdef CONFIG_CAN_MCP251X
#include <linux/can/platform/mcp251x.h>
#define CAN_GPIO_IRQ_MCP251x_SPI TEGRA_GPIO_PV6

static struct mcp251x_platform_data mcp251x_info = {
       .oscillator_frequency = 8 * 1000 * 1000, /* CAN SPI click 5V has a 8MHz crystal */
       .irq_flags            = IRQF_TRIGGER_FALLING|IRQF_ONESHOT,  /* This is also defined in the mcp251x driver: can be ommited */
       .board_specific_setup = NULL, /* We don't have a board specific setup */
       .power_enable         = NULL, /* We don't want any power enable function */
       .transceiver_enable   = NULL, /* We don't want any transceiver enable function */
};

struct spi_board_info mcp251x_spi_board[1] = {
       {
               .modalias = "mcp2515", /* (or mcp2510) used chip controller */
               .platform_data = &mcp251x_info, /* reference to the mcp251x_platform_data mcp251x_info */
               .max_speed_hz  = 8 * 1000 * 1000, /* max speed of the used chip */
               .chip_select   = 0, /* the spi cs usage*/
               .bus_num = 1,
               .mode = SPI_MODE_0,
       },
};

static int __init mcp251x_init(void)
{
       mcp251x_spi_board[0].irq = gpio_to_irq(CAN_GPIO_IRQ_MCP251x_SPI); // #define CAN_GPIO_IRQ_MCP251x_SPI TEGRA_GPIO_PK2
       spi_register_board_info(mcp251x_spi_board, ARRAY_SIZE(mcp251x_spi_board));
       pr_info("mcp251x_init\n");
       return 0;
}

#endif

And updated the tegra_t210ref_late_init function to include mcp251x_init:

static void __init tegra_t210ref_late_init(void)
{
	struct board_info board_info;
	tegra_get_board_info(&board_info);
	pr_info("board_info: id:sku:fab:major:minor = 0x%04x:0x%04x:0x%02x:0x%02x:0x%02x\n",
		board_info.board_id, board_info.sku,
		board_info.fab, board_info.major_revision,
		board_info.minor_revision);

	t210ref_usb_init();
	tegra_io_dpd_init();
#ifdef CONFIG_PM_SLEEP
	/* FIXME: Assumed all t210ref platforms have sdhci DT support */
	t210ref_suspend_init();
#endif
	tegra21_emc_init();
	isomgr_init();

#ifdef CONFIG_CAN_MCP251X
	mcp251x_init();
#endif

	/* put PEX pads into DPD mode to save additional power */
	t210ref_camera_init();
}

I have my MCP2515 connected to “SPI0” on the expansion connector. Don’t forget you’ll need a level shifter to convert the 1.8V SPI voltage levels to the 3.3V needed by the MCP2515.

I’ll attach my boardfile and the .dtsi for enabling the SPI ports. Not sure if that’s needed or not.
tegra210-daxc03.dtsi.txt (2.22 KB)
board-t210ref.c (13.9 KB)

I’m not getting any messages in or out on the bus, and I’m not seeing the can0 network stats change. Going to try sending messages directly over SPI to the MCP2515 to validate my hardware. Then break open the driver and see where the disconnect is.

I’m getting some really strange stuff out of my MCP2515, perhaps someone else has had a similar issue?

No matter what I send on the bus I get this…

can0  RX - -       0F2   [4]  11 22 33 44
  can0  RX - -       1FA   [4]  11 22 33 44
  can0  RX - -       0F2   [4]  11 22 33 44
  can0  RX - -  03C4FAE1   [7]  remote request
  can0  RX - -       1FA   [4]  11 22 33 44
  can0  RX - -  03C4FAE1   [7]  remote request
  can0  RX - -       0F2   [4]  11 22 33 44
  can0  RX - -       1FA   [4]  11 22 33 44
  can0  RX - -       0F2   [4]  11 22 33 44
  can0  RX - -  03C4FAE1   [7]  remote request
  can0  RX - -       1FA   [4]  11 22 33 44
  can0  RX - -  03C4FAE1   [7]  remote request
  can0  RX - -       0F2   [4]  11 22 33 44
  can0  RX - -       1FA   [4]  11 22 33 44

I checked the line between the transceiver (ISO1050) and the MCP2515. It shows that the transceiver is putting out the correct serialized can data. When I look at the SPI it’s showing these messages, so it’s not a driver problem. It’s something with the MCP2515.

No matter what I change the message to it’s the same output from the MCP2515, persists through power cycles. Any thoughts?

Edit: I’m wondering if that’s the default power on values of the RX registers and the data from the transceiver isn’t getting latched. Would explain why it’s so consistent. Every time my interrupt fires it’s just reading back the default data.

Fixed it, was the wrong oscillator_frequency in the mcp251x_platform_data struct. From the comment in the first post I thought it was the SPI clock, but it’s actually the value of the oscillator connected to the MCP251x which is used for bit timing calculations.

Fully working MCP251x over SPI, I’ll make my own post detailing the steps to make it working and link it here.

Thanks, good to know that is working now.

Yes the oscillator that i have is one of 8 MHz and probably you have a different MCP2515 card configuration, I read that you can change the cristal to 16 MHz if you like, that is where the value comes from. Maybe will be a good to know.

please put the link of your steps in this link when you finish.

Here we go: https://devtalk.nvidia.com/default/topic/998947/jetson-tx1/guide-to-enabling-mcp251x-on-the-tx1-spi-/

I’d appreciate it if you could run through the steps and make sure it works for you/I didn’t miss anything!