Connecting MCP2515 via SPI for CAN driver with Android kernel 4.9

We have wired an MCP2515 chip to the SPI lo-speed connector and have configured the kernel build of hikey-kernel to activate CAN and SPI. When we try to create a device via: ip link add dev can0 type can we get the message: RTNETLINK ANSWERS: Operation not supported on transport endpoint. Also the ‘ip’ command does not appear to support type ‘can’.

I believe that we are missing something in our configuration. Any suggestions?

1 Like

Did you enable CAN_RAW as well ?

Yes, CAN_RAW is configured as ‘y’.

In the file: arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts we added the following in the &spi2 section:
can0: mcp2515@0 { compatible = "microchip,mcp2515"; reg = <0>; clocks = <&clk16m>; interrupt-parent = <&gpio2>; interrupts = <0 0x2>; spi-max-frequency = <500000>; /* may be too high. 500000 was recommended */ };
The spi-max-frequency was initially set to 10MHz. By reducing it to 500KHz we started to see additional messages in the kernel logs:

[ 7.013583] ssp-pl022 ffd68000.spi: flush [ 7.013588] ssp-pl022 ffd68000.spi: flush [ 7.013591] ssp-pl022 ffd68000.spi: polling transfer ongoing ... [ 7.013595] ssp-pl022 ffd68000.spi: readwriter, rx: ffffffc0b4f33b18, rxend: ffffffc0b4f33b19, tx: ffffffc0b4f33b98, txend: ffffffc0b4f33b99 [ 7.013600] ssp-pl022 ffd68000.spi: readwriter, rx: ffffffc0b4f33b18, rxend: ffffffc0b4f33b19, tx: ffffffc0b4f33b99, txend: ffffffc0b4f33b99
This was not sufficient to get the CAN driver up, though.

I work with Marianne here at Pillar Technology.
We don’t quite recall what our original problem was (we ended up stumbling over a number of problems), but the title of this question is very searchable so we wanted to share a working solution.
The following is a step by step guide to enabling CAN on the Hikey960, with lots of checkpoints to verify things are going correctly along the way.

Enable CAN in Kernel

Run lunch to put the prebuilt cross compiler on the PATH.

$ . build/envsetup.sh
$ lunch hikey960-userdebug

Clone the kernel source and verify you can build it

https://source.android.com/setup/devices#960kernel

git clone https://android.googlesource.com/kernel/hikey-linaro
cd hikey-linaro
git checkout -b android-hikey-linaro-4.9 origin/android-hikey-linaro-4.9
make ARCH=arm64 hikey960_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- -j24

Copy the dtb and image files into the AOSP tree.

cp ~/kernel/hikey-linaro/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dtb ~/aosp/device/linaro/hikey-kernel/hi3660-hikey960.dtb-4.9

cp ~/kernel/hikey-linaro/arch/arm64/boot/Image.gz ~/aosp/device/linaro/hikey-kernel/Image.gz-hikey960-4.9

Build & Flash the AOSP image

cd ~/aosp
make -j16

If everything boots up properly, congrats, you’re ready to add the CAN modules to the kernel.

Add CAN Modules to Kernel

Add the following lines to ~/kernel/hikey-linaro/arch/arm64/configs/hikey960_defconfig.

CONFIG_CAN=y
CONFIG_CAN_VCAN=y
CONFIG_CAN_MCP251X=y
CONFIG_CAN_DEBUG_DEVICES=y
CONFIG_SPI_DEBUG=y

Then regenerate the .config file and rebuild the kernel.

make ARCH=arm64 hikey960_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- -j24

Follow the steps above to copy the kernel image into the aosp tree, rebuild aosp, and flash your device.

Verify the CAN modules have been added with VCAN.

adb root # must be root to add the net device
adb shell ip link add dev vcan0 type vcan 
adb shell ifconfig vcan0 up
adb shell ifconfig # you should see vcan0 in the results

Modify the Device Tree

Enable the 1.8V regulator

The GPIO on the Hikey960 runs at 1.8V and CAN runs at either 3.3v or 5v.
This means you need a level shifter in your hardware design.
The level shifter will require a 1.8V reference voltage.

By default, LDO11 (Pin 35) is not enabled, so you need to apply the following patch to the dts, credit leo-yan.

diff --git a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
index c7dfbd1..eb0fad7 100644
--- a/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
+++ b/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts
@@ -271,6 +271,7 @@
                        };
 
                        ldo11: LDO11 { /* Low Speed Connector */
+                               regulator-always-on;
                                regulator-name = "VOUT11_1V8_2V95";
                                regulator-min-microvolt = <1750000>;
                                regulator-max-microvolt = <3300000>;

Build the dtb.

make ARCH=arm64 CROSS_COMPILE=aarch64-linux-android- dtbs

Copy the dtb to the aosp tree, build, and flash.

Verify the regulator on Pin 35 is working with a voltmeter and/or run the following commands.

adb shell cat /sys/class/regulator/regulator.3/name
# VOUT11_1V8_2V95
adb shell cat /sys/class/regulator/regulator.3/state
# enabled

Add a CAN entry to SPI2

Now we need to tell the kernel about the external MCP2515 CAN hardware we’re connecting to SPI2 on the low speed connector.

There are a number of things to explain here, but the TLDR is create this partial device tree file.

arch/arm64/boot/dts/hisilicon/hi3660-hikey960-can.dts

&spi2 {
    can0: mcp2515@0 {
        compatible = "microchip,mcp2515";
        reg = <0>;
        clocks = <&mcp2515_clk>;
        /* GPIO_019 -> Hikey960 Pin 32 */
        interrupt-parent = <&gpio2>;
        interrupts = <3 0x2>; //falling edge
        spi-max-frequency = <500000>; 
        mcp2515_clk: oscillator {
            #clock-cells = <0>;
            compatible = "fixed-clock";
            clock-frequency = <8000000>; //8MHz
        };  
    };  
};

&ldo11 { /* 1V8 pwr supply Hikey 960 LS Pin 35 */
    regulator-always-on;
};

Then include it at the bottom of the main hi3660-hikey960.dts file.
(We want our additions to be overlayed on top of the original hardware definition.)

/include/ "hi3660-hikey960-can.dts"

Clock Speed

Be sure to verify the oscillator frequency of the crystal on your CAN module. Ours runs at 8MHz, but many run at 16MHz.
Adjust this accordingly.
The driver uses this frequency to calculate the bit timing for the CAN protocol.
If this frequency isn’t correct, you won’t be able to establish communications later.

Interrupts

In the device tree node above, the interrupt-parent is a reference to one of the Hikey’s GPIO groups.
The interrupts entry defines the GPIO’s index within the group and as well as the trigger type.

For our board, we picked GPIO_019 (Pin 32) because there’s no alternative function for the pin.
We connected this pin to the interrupt pin on the MCP2515 module.

The Hi3660 Datasheet describes how to convert from a GPIO to a GPIO group and index in Sect. 8.5.1.

You can obtain the group ID of a GPIO pin by using the following formula:

Group ID = int(GPIO pin number/8)

The remainder is the sequence number of this pin in the group, ranging from 0 to 7. (0 is the
LSB and 7 is the MSB.)

Sequence number within the group = mod(GPIO pin number/8)

Take GPIO_017 as an example. The pin number is 17. Its group ID is 2 (int(17/8)), and the
remainder is 1. Therefore, GPIO_017 belongs to GPIO group 2 (GPIO2), and its sequence
number within this group is 1. Similarly, the sequence number of GPIO_016 in GPIO2 is 0,
and that of GPIO_023 in GPIO2 is 7.

For GPIO_019, the calculation is as follows.

GPIO_019
Pin# = 19
19/8 = 2r3
GroupId = int(19/8) = 2
Seq# = mod(19/8) = 3

GPIO2, idx=3

Which is how we arrive at the following entry.

/* GPIO_019 -> Hikey960 Pin 32 */
interrupt-parent = <&gpio2>;
interrupts = <3 0x2>; //falling edge

If you use a pin other than 32, you’ll need to reference the hardware guide and perform this calculation yourself.

Verify device tree modifications

Build the dtb, copy it to the aosp tree, build, and flash.
Wire your CAN module into the low-speed connector then
add the can0 network device, and verify you can bring it up.

adb shell ip link set can0 type can bitrate 500000
adb shell ifconfig can0 up
adb shell ifconfig

Adding CAN-Utils to the device

You may skip this step, but we found it useful to have can utils like cansend and candump on the target device for debugging/development/verification purposes while we were going through this process.

Add the CAN-Utils Source to the AOSP Tree

Add a local manifest to the ~/aosp/.repo/local_manifests/ directory.

<?xml version="1.0" encoding="UTF-8"?>
<manifest>
  <remote fetch="https://github.com/" name="github"/>
  <project name="linux-can/can-utils" path="device/external/can-utils" remote="github" revision="master"/>
</manifest>

Note that can-utils cannot be placed inside the external directory because it has compiler warnings.
It has to go inside of either device or vendor.

Run a repo sync to download the project into the AOSP source tree.

Add the utils to the device

Add a can directory inside of device/linaro/hikey and create the following can.mk file.

PRODUCT_PACKAGES += \
    candump \
    cansend \
    bcmserver \
    can-calc-bit-timing \
    canbusload \
    canfdtest \
    cangen \
    cangw \
    canlogserver \
    canplayer \
    cansniffer \
    isotpdump \
    isotprecv \
    isotpsend \
    isotpserver \
    isotpsniffer \
    isotptun \
    isotpperf \
    log2asc \
    log2long \
    slcan_attach \
    slcand \
    slcanpty

Then import the makefile by adding the following line to device/linaro/hikey/hikey960.mk.

$(call inherit-product, device/linaro/hikey/can/can.mk)

Build and flash, then start the CAN network by running the following commands.

adb shell ip link set can0 type can bitrate 500000
adb shell ifconfig can0 up

Verify your network is working properly with cansend and candump.

Automatically Setup CAN Network on Boot

Again, an optional step, but it’s likely you want the CAN to be available when your device boots.

First, create a shell script to configure the network and bring it up.

device/linaro/hikey/can/setup-can.sh

#!/usr/bin/env bash

ip link set can0 type can bitrate 500000
ifconfig can0 up

And have the can.mk makefile copy the script to the device.

PRODUCT_COPY_FILES += \
    device/linaro/hikey/can/setup-can.sh:/system/etc/setup-can.sh

Note that if you build and flash right now, the script will be copied to the device, but it will not be executable (even if you chmod +x first).

In order to run the script, you have to pass it to the sh command.

adb shell
su
sh /system/etc/setup-can.sh

Verify the can0 network device is up by running ifconfig.

Next, create a new init.can.rc file.
We’ll create a one shot service to execute our setup script with sh.

# Set up the can network
service can /system/bin/sh /system/etc/setup-can.sh
    class core
    oneshot

Import our new init.can.rc file into device/linaro/hikey/init.common.rc.

import init.${ro.hardware}.usb.rc		  import init.${ro.hardware}.usb.rc
import init.${ro.hardware}.power.rc		  import init.${ro.hardware}.power.rc
import init.can.rc # add this line

And make sure the can.mk file is copying our new init file to the device.

PRODUCT_COPY_FILES += \
    device/linaro/hikey/can/setup-can.sh:/system/etc/setup-can.sh \
    device/linaro/hikey/can/init.can.rc:root/init.can.rc

Building and flashing at this point will now result in… nothing.
Or rather, apparently nothing.

Manually starting the service and inspecting the logs will reveal a SELinux error.

$ adb root
$ adb shell start can && dmesg
init: Control Message 'start' recieved
init: Could not ctl.start for service can: File /system/bin/sh(labeled "u:object_r:shell_exec:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials

The link will tell you to create a new domain, but the fact that the /system/bin/sh executable is already labeled indicates that a domain already exists.
We just need to allow it to run in the init domain.
All we need to do is add a device/linaro/hikey/sepolicy/shell.te file with the following line.

# Allow init to run sh 
# We need this for one shot setup of the can network

init_daemon_domain(shell)

Build & flash one more time.
Running ifconfig will show that the can0 network device was automatically configured and started.

2 Likes

This is awesome! Thanks!

2 Likes

Hi,
I followed the steps as described. Unfortunately it is not working. When I run the command "adb shell ip link set can0 type can bitrate 500000 " it says no device can0 available.
Any idea about the reason ? Thanks in advance for your support.
Hatem

Hi! What have you tried? Are you seeing any messages in the kernel logs? Did you power on the remote system before the Hikey? I did sometimes see issues if the Hikey wasn’t powered on after the rest of the devices on the CAN bus. Or it might have been the other way around. I haven’t worked on this in some months now. I believe we saw this most often when we had some kind of hardware connection issue on the SPI bus. Make sure the SPI interrupt pin is securely connected. Those tiny European headers can be hard to get a good connection with sometimes.

1 Like

Hi Christopher, first of all thx for your prompt reply. :slight_smile:

Actually i still did not connect any device from the other side. right now i have only the hikey board and the CAN module. (please see pictures).

i am sharing also part of the kernel log. it says may be wiring is wrong ???

my wiring:
CAN ← → Hikey

INT ← → PIN 32 (GPIO_019)
SCK ← → PIN 8 (SPI2_CLK)
SI ← → PIN 10 (SPI2_DI)
SO ← → PIN 14 (SPI2_DO)
CS ← → PIN 12 (SPI2_CS_N)
GND ← → PIN 39 (GND)
VCC ← → PIN 37 (SYS_5V)

e[32m[ 6.880569] e[33mspi spi32766.0e[0m: 4 <= n <=8 bits per word
e[32m[ 6.880572] e[33mspi spi32766.0e[0m: DMA mode NOT set in controller state
e[32m[ 6.880578] e[33mspi spi32766.0e[0m: setup mode 0, 8 bits/w, 500000 Hz max → 0
e[32m[ 6.880762] e[33mssp-pl022 ffd68000.spie[0m: SSP Target Frequency is: 500000, Effective Frequency is 500000
e[32m[ 6.880765] e[33mssp-pl022 ffd68000.spie[0m: SSP cpsdvsr = 2, scr = 199
e[32m[ 6.880767] e[33mmcp251x spi32766.0e[0m: 4 <= n <=8 bits per word
e[32m[ 6.880769] e[33mmcp251x spi32766.0e[0m: DMA mode NOT set in controller state
e[32m[ 6.880772] e[33mmcp251x spi32766.0e[0m: setup mode 0, 8 bits/w, 500000 Hz max → 0
e[32m[ 6.885810] e[33mssp-pl022 ffd68000.spie[0m: flush
e[32m[ 6.885815] e[33mssp-pl022 ffd68000.spie[0m: flush
e[32m[ 6.885819] e[33mssp-pl022 ffd68000.spie[0m: polling transfer ongoing …
e[32m[ 6.885823] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc218304499, tx: ffffffc218304518, txend: ffffffc218304519
e[32m[ 6.885838] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc218304499, tx: ffffffc218304519, txend: ffffffc218304519
e[32m[ 6.885841] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc218304499, tx: ffffffc218304519, txend: ffffffc218304519
e[32m[ 6.890870] e[33mssp-pl022 ffd68000.spie[0m: flush
e[32m[ 6.890873] e[33mssp-pl022 ffd68000.spie[0m: flush
e[32m[ 6.890876] e[33mssp-pl022 ffd68000.spie[0m: polling transfer ongoing …
e[32m[ 6.890879] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc218304518, txend: ffffffc21830451b
e[32m[ 6.890885] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890889] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890892] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890894] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890897] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304498, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890901] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890904] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890907] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890910] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890913] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890916] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc218304499, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890919] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890922] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890926] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890928] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890931] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890933] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890936] e[33mssp-pl022 ffd68000.spie[0m: readwriter, rx: ffffffc21830449a, rxend: ffffffc21830449b, tx: ffffffc21830451b, txend: ffffffc21830451b
e[32m[ 6.890954] e[33mmcp251x spi32766.0e[31m: Cannot initialize MCP2515. Wrong wiring?
e[32m[ 6.890962] e[33mmcp251x spi32766.0e[31m: Probe failed, err=19
e[32m[ 6.890974] e[33mssp-pl022 ffd68000.spie[0m: registered child spi32766.0
e[32m[ 6.890977] e[33mssp-pl022 ffd68000.spie[0m: probe succeeded
e[32m[ 6.891029] e[33mssp-pl022 ff3b3000.spie[0m: ARM PL022 driver, device ID: 0x00041022
e[32m[ 6.891049] e[33mssp-pl022 ff3b3000.spie[0m: BUSNO: -1
e[32m[ 6.891056] e[33mssp-pl022 ff3b3000.spie[0m: mapped registers from 0x00000000ff3b3000 to ffffff800bb03000
e[32m[ 6.891085] e[33mssp-pl022 ff3b3000.spie[0m: setup for DMA on RX dma0chan2, TX dma0chan3
e[32m[ 6.891172] e[33mssp-pl022 ff3b3000.spie[0m: registered master spi32765 (dynamic)
e[32m[ 6.891616] e[33mssp-pl022 ff3b3000.spie[0m: probe succeeded
e[32m[ 6.892222] e[0mrt1711_i2c_probe
e[32m[ 6.892224] e[33mI2C functionality e[0m: OK…

Thank for you support
Hatem

It looks like there is some traffic on the SPI bus, but it’s unable to initialize properly. What’s the clock speed of the crystal on your CAN module? That looks like a genuine Electrodragon. It should be 16hz, if I recall correctly, but check for markings on the crystal (the silver oblong) and the datasheet. My module was a knock off with an 8hz clock. Double check that you’ve set the clock speed correctly in the device tree.

1 Like

Hi, clock is 8Mhz

is my wiring correct ?

my wiring:

CAN ← → Hikey

INT ← → PIN 32 (GPIO_019)
SCK ← → PIN 8 (SPI2_CLK)
SI ← → PIN 10 (SPI2_DI)
SO ← → PIN 14 (SPI2_DO)
CS ← → PIN 12 (SPI2_CS_N)
GND ← → PIN 39 (GND)
VCC ← → PIN 37 (SYS_5V)

thnx
Hatem

HOLD ON JUST A MINUTE THERE!!!
Are you TRYING to murder your hikey960?
You are interfacing a 5v device with a 1.8v device without a LEVEL SHIFTER.

Even if its protected from setting the world on fire, the high and low logic levels are NOT going to align, which means they will NOT communicate successfully.

2 Likes

OK :grimacing:
What shall I add/do exactly ?

Well, how about doing the obvious? Add a level shifter. And cross your fingers that you didn’t cook your SoC.

You have to make sure that the level shifter you use is capable of SPI.

It looks like the “sensors mezzanine” board has SPI routed through a level shifter (TXB0108), but I’m not sure how well that will work, I’ve read that TXB is not a great level shifter for SPI, rather it would be better to use TXS0108 for that. Its possible that the TXB may work adequately at low speeds, but I can’t confirm.

EDIT: I would be tempted to try the sensors mezzanine, especially if you happen to already own one, and if not, they’re a great tool for this type of hacking. If SPI doesn’t work through the TXB and you (or someone you know) is halfway decent at surface mount soldering, you can swap the TXB for a TXS. They are pin compatible, so its a real easy swap.

2 Likes

@doitright thanks a lot :slight_smile: . I understand better now the choice of your name (do it right) .
Hi @rubberduck203, in your case how did you manage to do the level shifting. thx

Hatem

Oh geez! Great catch.

I bought some modules from Sparkfun and fabbed a proto-board.
It takes two of these to cover all the pins.

1 Like

@rubberduck203 thx a lot :slight_smile:

1 Like

You’re welcome! One tip I do have there is to get yourself some of the smaller 0.2mm jumpers and strip one end off to solder to your level shifter proto. I did manage to make 0.2mm headers work, but it takes an extremely steady hand to solder those tiny buggers. I think you’d have better luck with using jumpers. Bad hardware connections plagued my project for months.

I think you mean 2mm.

Also, you have moose hands if you have a hard time with those. Try 0.5mm SM, those are small.

I did and, alas, I do have moose hands.