Canbus Hat support
Comments
-
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. -
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 -
Thanks for the reply! I look forward to trying one of your boards out, the small kernel module looks reasonably straightforward for my purposes.
-
Androvsky how did you can bus hat implementation ended? I am also looking into it
-
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.
-
@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 ?? Thank you !
-
@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.
-
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...
-
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"); -
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.
-
@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 -
so I'm running in the problem where I awlays get spi2.0 cannot initialize mcp2515. Wrong wiring?
spi2.0: Probe failed, err=19Has 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.
-
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