SPI
Update time:2018-04-16 Views:2454
Introduction
SPI is a high-speed, full-duplex, synchronous serial communication interface for connection to a microcontroller, sensors, storage devices, etc.
AIO-3399J all the spi are used for other modules, so take a example with Firefly-RK3399
There are one SPI(SPI1) on the Firefly-RK3399 development board, the following picture shows the position:
SPI working mode
SPI work in master-slave mode, This usually has a master device and one or more slave devices. They need at least four lines, which are:
CS Chip select signals SCLK Clock signal MOSI Master data output, and Slave data input MISO Master data input, and Slave data output
Linux kernel using a combination of CPOL and CPHA to represent the SPI's four working modes:
CPOL=0,CPHA=0 SPI_MODE_0 CPOL=0,CPHA=1 SPI_MODE_1 CPOL=1,CPHA=0 SPI_MODE_2 CPOL=1,CPHA=1 SPI_MODE_3
CPOL:Represents the initial level of the state of the clock signal,0 is low,1 is high.
CPHA:Represents the sampling clock edge, 0 is leading edge, 1 is trailing edge.
SPI waveforms of four working modes as follows:
Driver
In this paper, let's take W25Q128FV Flash module for example to introduce the use of SPI.
Hardware connections
the connections between Firefly-RK3399 and W25Q128FV like the following table:
W25Q128FV | Firefly-RK3399 |
---|---|
/CS | SPI1 CSN0 |
DO | SPI1 RX |
GND | GND |
VCC | 3.3V |
CLK | SPI1 CLK |
DI | SPI1 TX |
Writing Makefile/Kconfig
Add SPI_FIREFLY in kernel/drivers/spi/Kconfig:
config SPI_FIREFLY tristate "Firefly SPI demo support " default y help Select this option if your Firefly board needs to run SPI demo.
Add the driver into the kernel/drivers/spi/Makefile such as:
obj-$(CONFIG_SPI_FIREFLY) += spi-firefly-demo.o
Select the driver files to add to the kernel options using make menuconfig:
│ Symbol: SPI_FIREFLY [=y] │ Type : tristate │ Prompt: Firefly SPI demo support │ Location: │ -> Device Drivers │ -> SPI support (SPI [=y]) │ Defined at drivers/spi/Kconfig:704 │ Depends on: SPI [=y] && SPI_MASTER [=y]
Define SPI DTS
Add SPI driver node description in DTS, such as:
/* Firefly SPI demo */ &spi1 { spi_demo: spi-demo@00{ status = "okay"; compatible = "firefly,rk3399-spi"; reg = <0x00>; spi-max-frequency = <48000000>; /* rk3399 driver support SPI_CPOL | SPI_CPHA | SPI_CS_HIGH */ //spi-cpha; /* SPI mode: CPHA=1 */ //spi-cpol; /* SPI mode: CPOL=1 */ //spi-cs-high; }; }; &spidev0 { status = "disabled"; };
Status: If you want to enable SPI, then set "okay", if not enabled, set "disable".
spi-demo@00:Because this example uses CS0, so here is set to 00, if you use CS1, is set to 01.
Compatible:This property must be consistent with the structure of_device_id members.
Reg: Make this 0x00, consistent with spidev@00.
spi-max-frequency: the highest frequency of SPI, Firefly-RK3399 support up to 48000000.
spi-cpha,spi-cpol: Set the SPI work mode here, The SPI module in this example work in SPI_MODE_1, so we set spi-cpha = <1>. You should mask this two, if your SPI module work in SPI_MODE0. You should set spi-cpha = <1>;spi-cpol = <1>, if your SPI module work in SPI_MODE3.
spidev0: need to close spidev0, because spi_demo and spidev0 use the same hardware resource.
Define SPI driver
Create a new file in the kernel source: kernel/drivers/spi/spi-firefly-demo.c
Before defining the SPI driver, define the of_device_id first.
of_device_id is defined to match the corresponding node in the dts file:
static struct of_device_id firefly_match_table[] = {{ .compatible = "firefly,rk3399-spi",}, {},};
the value of compatible is the same as DTS's.
Define the spi_driver:
static struct spi_driver firefly_spi_driver = { .driver = { .name = "firefly-spi", .owner = THIS_MODULE, .of_match_table = firefly_match_table,}, .probe = firefly_spi_probe,};
Register SPI driver
Register SPI driver to the kernel in firefly_spi_init:
spi_register_driver(&firefly_spi_driver);
If it match Successful when kernel start up, it will config the mode , speed and so on, call the driver's probe function. Probe function as following: static int firefly_spi_probe(struct spi_device *spi)
SPI transfer
there are two transfer interface in firefly_spi_probe.
firefly_spi_read_w25x_id_0: using spi_transfer and spi_message to read ID.
firefly_spi_read_w25x_id_1: using spi_write_then_read to read ID.
if everything is ok, you will see the information in console:
root@rk3399_firefly_box:/ # dmesg | grep firefly-spi [ 1.006235] firefly-spi spi0.0: Firefly SPI demo program [ 1.006246] firefly-spi spi0.0: firefly_spi_probe: setup mode 0, 8 bits/w, 48000000 Hz max [ 1.006298] firefly-spi spi0.0: firefly_spi_read_w25x_id_0: ID = ef 40 18 00 00 [ 1.006361] firefly-spi spi0.0: firefly_spi_read_w25x_id_1: ID = ef 40 18 00 00
Enable SPI demo
spi-firefly-demo is disabled in default, enable it in kernel/arch/arm64/boot/dts/rockchip/rk3399-firefly-demo.dtsi like the following steps:
set "okay" in spi1 status;
set "disabled" in spidev0 status.
Commonly used SPI APIs
Here are the definition of some commonly used GPIO APIs:
static void spi_message_init(struct spi_message *m); static void spi_message_add_tail(struct spi_transfer *t, struct spi_message *m); int spi_sync(struct spi_device *spi, struct spi_message *message) ; static int spi_write(struct spi_device *spi, const void *buf, size_t len); static int spi_read(struct spi_device *spi, void *buf, size_t len); static ssize_t spi_w8r8(struct spi_device *spi, u8 cmd); static ssize_t spi_w8r16(struct spi_device *spi, u8 cmd); static ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd); int spi_write_then_read(struct spi_device *spi, const void *txbuf, unsigned n_tx, void *rxbuf, unsigned n_rx);
Interface
SPI devices have a limited userspace API, you can use it except kernel space interface such as IRQ handlers or other layers of the driver stack.
the path of spidev device in Firefly-RK3399:
/dev/spidev0.0
and the source path:
kernel/drivers/spi/spidev.c
enable SPI_SPIDEV in config:
│ Symbol: SPI_SPIDEV [=y] │ Type : tristate │ Prompt: User mode SPI device driver support │ Location: │ -> Device Drivers │ -> SPI support (SPI [=y]) │ Defined at drivers/spi/Kconfig:684 │ Depends on: SPI [=y] && SPI_MASTER [=y]
the DTS node:
&spi1 { status = "okay"; max-freq = <48000000>; dev-port = <0>; spidev0: spidev@00 { status = "okay"; compatible = "linux,spidev"; reg = <0x00>; spi-max-frequency = <48000000>; }; };
Please refer to spidev for detailed instructions.
FAQs
Q1: SPI data transfer error
A1: make sure pins iomux is SPI, TX/RX/CLK signal accurately, and set the correct mode with device。