Hello,
I try to add a new SPI driver in the kernel, but it doesn’t seem to work.
I tried my code with Spidev in userspace, and it works fine :
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
static inline int pabort(const char *s)
{
perror(s);
return -1;
}
int main(void)
{
char *device = "/dev/spidev3.0";
int fd;
uint8_t default_tx[] = {0xAA,0x12,0x01};
uint8_t default_rx[ARRAY_SIZE(default_tx)] = {0, };
static uint16_t delay;
static uint32_t mode;
static uint8_t bits = 8;
static uint32_t speed = 500000;
//Configuration du mode
mode |= SPI_CPHA;
fd = open(device, O_RDWR);
if(fd<0)
return pabort("Can't open device ");
/*
* spi mode
*/
if(ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1);
return pabort("can't set spi mode");
if(ioctl(fd, SPI_IOC_RD_MODE, &mode) == -1)
return pabort("can't get spi mode");
/*
* bits per word
*/
if(ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1)
return pabort("can't set bits per word");
if(ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits) == -1)
return pabort("can't get bits per word");
/*
* max speed hz
*/
if(ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) == -1)
return pabort("can't set max speed hz");
if(ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) == -1)
return pabort("can't get max speed hz");
printf("spi mode: 0x%x\n", mode);
printf("bits per word: %d\n", bits);
printf("max speed: %d Hz (%d KHz)\n", speed, speed/1000);
struct spi_ioc_transfer tr = {
.tx_buf = (unsigned long)default_tx,
.rx_buf = (unsigned long)default_rx,
.len = sizeof(default_tx),
.delay_usecs = delay,
.speed_hz = speed,
.bits_per_word = bits,
};
if(ioctl(fd, SPI_IOC_MESSAGE(1), &tr) == -1)
return pabort("can't send spi message");
close(fd);
return 0;
}
Now I try to make it works inside the kernel, this is what I used :
#include <linux/spi/spi.h>
//device at spi3.0
#define BUS_NUM 3
#define CS_NUM 0
int send_spi_data(unsigned char * tx_data)
{
int ret;
struct spi_master *master;
struct spi_device *spi_device;
struct spi_board_info spi_device_infos = {
.modalias = "mymodule",
.max_speed_hz = 2, //for debug
.bus_num = BUS_NUM,
.chip_select = CS_NUM,
.mode = SPI_CPHA, //Clock phase
};
master = spi_busnum_to_master(spi_device_infos.bus_num);
if(!master )
{
printk(KERN_ERR"----------------SPI Master not found\n");
return -ENODEV;
}
spi_device = spi_new_device(master,&spi_device_infos);
if( !spi_device )
{
printk(KERN_ERR"----------------SPI Failed to create slave\n");
return -ENODEV;
}
spi_device->bits_per_word = 8;
spi_device->cs_gpio=1;//not tested with 1
ret = spi_setup( spi_device );
if( ret )
{
printk(KERN_ERR"----------------SPI Failed to setup slave.\n");
spi_unregister_device(spi_device);
return -ENODEV;
}
printk(KERN_ERR"----------------SPI Writing.\n");
ret = spi_write(spi_device, &tx_data, sizeof(tx_data));
if (ret ==0)
printk(KERN_ERR"----------------Writing successful! %d bytes\n", sizeof(tx_data));
else
printk(KERN_ERR"----------------Writing fail.\n");
spi_unregister_device(spi_device);
return 0;
}
This is what I get when I run my code :
[ 11.994341] spi_imx 2014000.ecspi: chipselect 0 already in use
[ 12.001372] ----------------SPI Failed to create slave
But no other device use this chip select!
Is it because this spi is configured in spidev?
I tried to remove the SPIdev declaration in the dts, /dev/spidev3.0 disapear, but /sys/class/spi_master/spi3/spi3.0 also.
Did I make something wrong? I use cs-gpio, not original CS.
My .dtsi:
&ecspi4 {
fsl,spi-num-chipselects = <2>;
cs-gpios = <&gpio2 31 0>,<&gpio2 11 0>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi4 &pinctrl_spi_cs4>;
status = "okay";
};
My .dts
/* Colibri SPI */
//spidev3.* in linux
&ecspi4 {
status = "okay";
spidev0: spidev@1 {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <50000000>;
};
spidev1: spidev@2 {
compatible = "spidev";
reg = <1>;
spi-max-frequency = <50000000>;
};
};
Thank you for the help.
Pierre-Olivier