SPI kernel driver

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

Can you clarify your end requirement? May I know the reason for the approach you are following?

Unfortunately user space code adhering to a user space API (e.g. spidev in your case) can’t just be moved into kernel space like that. The kernel space code will have to adhere to the kernel space API (e.g. probably a SPI protocol driver in your case). Have a look at the kernel documentation for more information. And yes, of course only one driver can be active for a particular piece of hardware at any one time. As @sanchayan.tx pointed out it would be important for us to know what exactly you are trying to achieve to better guide you.

I try to make a SPI communication works between the iMX6 and a FPGA in a kernel module. With the SPIDEV (first function) in the userspace, it works (i can light up a led with a SPI command on the FPGA). I add the second code (kernel) into a driver, as a new function, I call the new function in the driver with

unsigned char * data[] = {0xAA, 0x12, 0x01};
send_spi_data(data);

But the LED does not light up. So I think I’m doing something wrong somewhere. I would like to know where is my error.

Thanks

It was a DTS mistake, spidev was used for the used SPI, I need to remove the SPIDEv declaration and now it works.