Canbus Hat support

Andrew Pease
Andrew Pease New Member Posts: 4
I'm glad there's a lot of effort by the Up team to make Raspberry Pi Hats compatible, but I'm not clear on what is involved. I'm interested in the Up for a relatively low-volume product, but the only thing missing is canbus support. We can make a simple canbus hat that uses an MCP2515 via SPI that the Raspberry Pi supports, but I don't know if that would require driver work by the Up team. Does Linux on the Up use the arm device tree driver infrastructure, or does each hat have to be ported individually?

Comments

  • Dan O'Donovan
    Dan O'Donovan Administrator, Moderator, Emutex Posts: 241 admin
    Thanks for asking this question!
    The x86-64 kernel for UP doesn't use the ARM Device Tree to register SPI and I2C devices so, to use kernel drivers to manage an I2C or SPI HAT, a little bit of (trivial) extra work is needed to create a small kernel module which registers the SPI device. The code would look something like this:
    ...
    static struct mcp251x_platform_data mcp251x_info = {    .oscillator_frequency = 8000000, };
    static struct spi_board_info canbus_hat_spi_info = {
            .modalias = "mcp2515",
            .platform_data = &mcp251x_info,
            .irq = gpio_to_irq(?),
            .max_speed_hz = 2*1000*1000,
            .bus_num = 2,
            .chip_select = 0,
    };
    ...
    static struct spi_device *dev;
    static int __init canbus_hat_init(void)
    {
    	dev = spi_new_device(spi_busnum_to_master(2), &canbus_hat_spi_info);
    	return dev ? -ENODEV : 0;
    }
    static void __exit canbus_hat_exit(void)
    {
    	if (dev)
    		spi_unregister_device(dev);
    }
    module_init(canbus_hat_init);
    module_exit(canbus_hat_exit);
    MODULE_LICENSE("GPL");
    
    Code taken from this example: https://github.com/MinnowBoard/minnow-max-extras/blob/master/modules/calamari/calamari.c

    We are planning to implement a better solution for this. We want to adopt the approach that MinnowBoard folks are working towards, which is to use ACPI SSDT overlays to register SPI and I2C devices before the OS boots (quite similar to how the Raspberry Pi bootloader uses Device Tree overlays). I'll keep you posted as we make progress on this.
  • Dan O'Donovan
    Dan O'Donovan Administrator, Moderator, Emutex Posts: 241 admin
    Actually, there have been some recent developments on this (which I haven't had a chance to examine yet) but it is an indication of where this is headed:
    https://lkml.org/lkml/2016/3/31/333
  • Andrew Pease
    Andrew Pease New Member Posts: 4
    Thanks for the reply! I look forward to trying one of your boards out, the small kernel module looks reasonably straightforward for my purposes.
  • Federico
    Federico New Member Posts: 2
    Androvsky how did you can bus hat implementation ended? I am also looking into it
  • Andrew Pease
    Andrew Pease New Member Posts: 4
    I never ended up using the Up board in that project, sorry. I've since moved on to other projects, but I'm still keeping an eye on the Up board for them.
  • MaryemAyadi
    MaryemAyadi New Member Posts: 4

    @Dan O'Donovan Are there any updates ?? If not yet, can you please explain more ? I just wrote the following Kernel Module and charged it on UP2 but still not wotking :

            #include <linux/module.h>
            #include <linux/init.h>
            #include <linux/kernel.h> 
            #include <linux/can/platform/mcp251x.h> 
            #include <linux/gpio.h> 
            #include <linux/spi/spi.h> 
            #include <config/x86/reroute/for/broken/boot/irqs.h>
            #include <linux/interrupt.h> 
    
    
            MODULE_AUTHOR("Maryem");
            MODULE_DESCRIPTION("exemple de module");
            MODULE_LICENSE("GPL");
    
            static struct mcp251x_platform_data mcp251x_info = {    .oscillator_frequency = 10000000, };
    
            static struct spi_board_info canbus_hat_spi_info = {
                    .modalias = "mcp2515",
                    .platform_data = &mcp251x_info,
                    .irq = gpio_to_irq(430),
                    .max_speed_hz = 10*1000*1000,
                    .bus_num = 0,
                    .chip_select = 0,
            };
    
            static struct spi_device *dev;
    
            static int __init canbus_hat_init(void)
            {   dev = spi_new_device(spi_busnum_to_master(2), &canbus_hat_spi_info);
                return dev ? -ENODEV : 0;
            }
    
    
            static void __exit canbus_hat_exit(void)
            {   if (dev)
                    spi_unregister_device(dev);
            }
    
    
            module_init(canbus_hat_init);
            module_exit(canbus_hat_exit);
    

    Any Ideas ?? :neutral: Thank you !

  • npurtschert
    npurtschert New Member Posts: 6
    edited October 2018

    @Dan O'Donovan I would like to know about the status of supporting a CAN bus through a SPI or I2C Interface in UP boards. I hope there is a solution for this so far? I would like to make some tests with the UP board and CAN bus like this one: https://copperhilltech.com/pican2-duo-can-bus-board-for-raspberry-pi-2-3/

    I would be glad to hear from you soon.

  • totolito
    totolito New Member Posts: 1
    edited November 2018

    @MaryemAyadi

    Maybe this misstake is your problem:

    canbus_hat_spi_info .bus_num = 0
    but
    spi_busnum_to_master(2) in spi_new_device(...)

    Have a look at spi_board_info definition and identify on which bus you connect your MCP25...

  • nukular
    nukular New Member Posts: 61 ✭✭✭

    CAN definitely works via SPI with the MCP2515, I've used it with one of those little cheap modules you can buy for Arduinos (They may require hardware modifications, though, because of the voltage/logic levels required by the mcp and the CAN tranciever).

    What @totolito said is correct, and you also have to make sure the GPIO for the IRQ is configured correctly.

    Here is my kernel module

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/spi/spi.h>
    #include <linux/can/platform/mcp251x.h>
    #include <linux/gpio.h>
    #include <linux/interrupt.h>

    int busnum = 1;
    int chip_select = 1;
    int gpio_int = 462;

    module_param(busnum, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    MODULE_PARM_DESC(busnum, "busnum of spi bus to use");

    module_param(gpio_int, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    MODULE_PARM_DESC(gpio_int, "linux gpio number of INT gpio");

    module_param(chip_select, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    MODULE_PARM_DESC(chip_select, "spi chip select");

    int gpio_requested = 0;

    struct spi_device *dev1;
    static struct mcp251x_platform_data mcp251x_info = {
    .oscillator_frequency = 8000000,
    };

    static struct spi_board_info spi_device_info = {
    .modalias = "mcp2515",
    .platform_data = &mcp251x_info,
    .irq = -1,
    .max_speed_hz = 8 * 1000 * 1000,
    };

    static int __init mcp2515_init(void)
    {
    int ret;
    struct spi_master *master;

    printk("mcp2515_init: init\n");

    ret = gpio_request(gpio_int, "sysfs");

    if(ret){
    printk("mcp2515_init: could not request gpio %d\n", gpio_int);
    gpio_free(gpio_int);
    return ret;
    }
    gpio_requested = 1;

    gpio_direction_input(gpio_int);

    ret = gpio_to_irq(gpio_int);
    printk("mcp2515_init: irq for pin %d is %d\n", gpio_int, ret);
    spi_device_info.irq = ret;
    spi_device_info.bus_num = busnum;
    spi_device_info.chip_select = chip_select;

    master = spi_busnum_to_master( spi_device_info.bus_num );
    if( !master ){
    printk("mcp2515_init: MASTER not found.\n");
    ret = -ENODEV;
    goto error_postgpio;
    }

    // create a new slave device, given the master and device info
    dev1 = spi_new_device( master, &spi_device_info );

    if( !dev1) {
    printk("mcp2515_init: FAILED to create slave.\n");
    ret = -ENODEV;
    goto error_postgpio;
    }

    printk("mcp2515_init: device created!\n");

    return 0;

    error_postgpio:
    gpio_free(gpio_int);
    return ret;
    }

    static void __exit mcp2515_exit(void)
    {
    printk("mcp2515_init: exit\n");

    if( dev1 ){
    spi_unregister_device(dev1);
    }
    if(gpio_requested)
    gpio_free(gpio_int);
    }
    module_init(mcp2515_init);
    module_exit(mcp2515_exit);
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("nukular <[email protected]>");
    MODULE_DESCRIPTION("MCP2515 init");

  • Sam V
    Sam V New Member Posts: 7

    I am a newbie to UP and a total noob when it comes to kernel programming. Can some give instructions to how the kernel module can be setup. I am using UPBoard with ubilinux installed. I wish to interface my UPBoard with Arduino and raspberrypi over CAN. How can I use the above code from @nukular.

  • BenSpex
    BenSpex New Member Posts: 8

    @Sam V this https://www.thegeekstuff.com/2013/07/write-linux-kernel-module/ shows you how to write the kernel.
    you might need to change the busnum to get your hat to work.
    also the max_speed_hz might be too high in the above code from @nukular

  • BenSpex
    BenSpex New Member Posts: 8

    so I'm running in the problem where I awlays get spi2.0 cannot initialize mcp2515. Wrong wiring?
    spi2.0: Probe failed, err=19

    Has anyone had a similar problem and found a fix?
    I'm using this HAT https://www.waveshare.com/w/upload/2/29/RS485-CAN-HAT-user-manuakl-en.pdf
    Using the Kernel from @nukular and changing busnum = 2 and gpio_int = 25 or 402.

    I even tryed all GPIO Linux numbers from https://wiki.up-community.org/Pinout_UP2
    If I change the busnum to 1 or 0 I get no such device as an error.

    Any help would be highly appreciated.

  • DCleri
    DCleri Administrator, AAEON Posts: 1,213 admin

    BenSpex found a solution already available in the community, you can check the resolution on this topic:
    https://forum.up-community.org/discussion/3811/solved-mcp2515-can-bus-ubuntu-18-04-intel-cpu#latest