SPI
Chip Names and Kernel Versions
Chip Name | Kernel Version |
---|---|
All chips using Linux 4.4 | Linux 4.4 |
All chips using Linux 4.19 and above | Linux 4.19 and above |
Introduction
Overview
This document introduces the Linux SPI driver principles and basic debugging methods.
Target Audience
This document is primarily intended for:
- Technical Support Engineers
- Software Development Engineers
1. Rockchip SPI Features
SPI (serial peripheral interface), the following are features supported by the Linux 4.4 SPI driver:
- Default Motorola SPI protocol
- Support for 8-bit and 16-bit
- Software programmable clock frequency
- Support for 4 SPI transfer modes configuration
- Each SPI controller supports one to two chip selects
- Support for SPI slave mode, with SPI_CS0N only as CS input pin:
- No switching to GPIO function allowed during transfer
- CS1N replacement not supported In addition to the above support, Linux 4.19 adds the following features:
- Framework supports both slave and master modes
1.1 SPI Interface Rates
Chip Name | Master Mode Max Interface Rate | Slave Mode Max Interface Rate |
---|---|---|
RK3506 | 50MHz | 50MHz |
RV1106B/RV1103B | 50MHz | 33MHz |
RK3576 | 50MHz | 33MHz |
RK3562 | 50MHz | 33MHz |
RK3528 | 50MHz | 33MHz |
RV1106/RV1103 | 50MHz | 33MHz |
RK3588 | 50MHz | 33MHz |
RV1126/RV1109 | 50MHz | 16MHz |
RK3568 | 50MHz | 33MHz |
RK1808 | 50MHz | 16MHz |
RK3308 | 50MHz | 16MHz |
Other platforms | 50MHz | 16MHz |
Notes:
- The maximum interface rates are theoretical rates, actual rates depend on PCB trace quality and should be verified through testing.
- Some platforms cannot accurately divide down to the upper limit value due to PLL strategy limitations, actual maximum is determined by the maximum division value.
2. Kernel Software
2.1 Code Paths
drivers/spi/spi.c
: SPI driver frameworkdrivers/spi/spi-rockchip.c
: RK SPI interface implementationsdrivers/spi/spi-rockchip-slave.c
: RK SPI slave interface implementationsdrivers/spi/spidev.c
: Create SPI device nodes for user space usagedrivers/spi/spi-rockchip-test.c
: SPI test driver, needs to be manually added to MakefileDocumentation/spi/spidev_test.c
: User space SPI test tools
2.2 SPI Device Configuration — RK Chip as Master
Kernel Configuration
Device Drivers --->
[*] SPI support --->
<*> Rockchip SPI controller driver
DTS Node Configuration
&spi1 {
status = "okay";
// assigned-clocks = <CLK_SPI1>; // No need to configure by default, check CLK_SPIn from corresponding soc dtsi
// assigned-clock-rates = <200000000>; // No need to configure by default, SPI device working clock value
// dma-names; // No need to configure by default, disable DMA support, only support IRQ transfer
// rockchip,poll-only; // No need to configure by default, force CPU transfer when enabled, only supported in master mode
// rx-sample-delay-ns = <10>; // No need to configure by default, read sampling delay, see "Common Issues" "Delay Sampling Clock Configuration" section for details
// rockchip,autosuspend-delay-ms = <500>; // No need to configure by default, Runtime PM autosuspend delay, see "SPI Transfer Rate and CPU Load Optimization"
// rockchip,rt; // No need to configure by default, puts spi data transfer process in SCHED_FIFO class with priority 50
spi_test@10 {
compatible = "rockchip,spi_test_bus1_cs0"; // Name corresponding to driver
reg = <0>; // Chip select 0 or 1
spi-cpha; // Set CPHA = 1, default is 0 if not configured
spi-cpol; // Set CPOL = 1, default is 0 if not configured
spi-lsb-first; // Transfer LSB first
spi-max-frequency = <24000000>; // SPI clock output frequency, not exceeding 50M
status = "okay"; // Enable device node
};
};
Configuration notes for spiclk assigned-clock-rates and spi-max-frequency:
spi-max-frequency
is the SPI output clock, derived from the SPI working clockspiclk assigned-clock-rates
through internal division. Due to minimum internal division of 2, the relationship isspiclk assigned-clock-rates >= 2 * spi-max-frequency
- For example, if 50MHz SPI IO rate is needed, consider configuring (remember internal division must be even)
spi_clk assigned-clock-rates = <100000000>
,spi-max-frequency = <50000000>
, meaning 100MHz working clock (PLL divided to closest value not exceeding 100MHz), then internal division by 2 for final IO close to 50MHz spiclk assigned-clock-rates
should not be lower than 24M to avoid potential issues
2.3 SPI Device Configuration — RK Chip as Slave
Key Patches
It is recommended to use the SPI slave source code spi-rockchip-slave.c. Due to SDK version issues, please confirm whether the SDK has the following patch:
commit 10cbf3c2c93fca6e5ec6c99b5bdb319ca0494d45
Author: Jon Lin <jon.lin@rock-chips.com>
Date: Tue Nov 21 10:58:57 2023 +0800
spi: rockchip-slave: Add code
1. Implement one msg mechanism
2. Support SRAM extension by dts rockchip,sram property
Change-Id: I0fccc5d4347294488b5382ad3ba5ae72b35610f2
Signed-Off-By: Jon Lin <jon.lin@rock-chips.com>
Note:
- If the patch is not available, customers can directly obtain it from Redmine -> FAE Project -> Documentation -> Development Configuration Document -> SPI path.
Kernel Configuration
Device Drivers --->
[*] SPI support --->
[*] SPI slave protocol handlers
[*] Rockchip SPI Slave controller driver
DTS Node Configuration
&spi1 {
compatible = "rockchip,spi-slave"; // Prefer to use dedicated SPI slave driver
status = "okay";
// ready-gpios = <&gpio1 RK_PD2 GPIO_ACTIVE_LOW>; // Recommended configuration, signal for SPI slave to complete transmission, refer to "Kernel SPI Slave Software" section for details
// rockchip,cs-inactive-disable; // No need to configure by default, when SPI master timing tod_cs (Clk Rise To CS Rise Time) exceeds multiple io clock cycles, enable this to mask CS release detection
slave { // According to framework requirements, SPI slave sub-node naming must start with "slave"
compatible = "rockchip,spi_test_bus1_cs0";
reg = <0>; // Chip select only supports 0
spi-cpha; // Set CPHA = 1, default is 0 if not configured
spi-cpol; // Set CPOL = 1, default is 0 if not configured
spi-lsb-first; // Transfer LSB first
status = "okay"; // Enable device node
};
};
Note:
- RK SPI defaults to enable DMA transfer, it is not recommended to disable DMA transfer in slave mode. When a transmission exceeds the controller's cache size, the software will configure DMA transfer to avoid delayed interrupt response.
2.4 SPI Slave Considerations
2.4.1 Recommended Performance Settings
When the master rate exceeds a certain frequency, it is recommended to set the performance mode during the transmission process to avoid controller cache overflow caused by DRAM frequency switching:
bits_per_word = 8bits
,master io rate exceeds 5MHzbits_per_word = 16bits
,master io rate exceeds 10MHz
Reference Code:
diff --git a/drivers/spi/spi-rockchip-test.c b/drivers/spi/spi-rockchip-test.c
index 544d6038919a..c1037153ff86 100644
--- a/drivers/spi/spi-rockchip-test.c
+++ b/drivers/spi/spi-rockchip-test.c
@@ -36,6 +36,8 @@
#include <linux/platform_data/spi-rockchip.h>
#include <linux/uaccess.h>
#include <linux/syscalls.h>
+#include <soc/rockchip/rockchip-system-status.h>
+#include <dt-bindings/soc/rockchip-system-status.h>
#define MAX_SPI_DEV_NUM 10
#define SPI_MAX_SPEED_HZ 12000000
@@ -242,8 +244,10 @@ static ssize_t spi_test_write(struct file *file,
}
start_time = ktime_get();
+ rockchip_set_system_status(SYS_STATUS_PERFORMANCE);
for (i = 0; i < times; i++)
spi_read_slt(id, rxbuf, size);
+ rockchip_clear_system_status(SYS_STATUS_PERFORMANCE);
end_time = ktime_get();
cost_time = ktime_sub(end_time, start_time);
us = ktime_to_us(cost_time);
Note:
-
It is recommended that all slave mode transmission behaviors operate in performance mode.
-
The set/clear performance interface has some time overhead, so it is recommended to be set by the upper business layer to avoid frequent calls.
-
If the cache overflows and the slave cannot complete the DMA transfer, it will be blocked and unable to exit. By printing the SPI->SPI_RISR register, you can check whether a cache overflow has occurred.
2.4.2 Recommended 16bits Width Setting
Maximize the use of slave FIFO capacity, with burst size at least 2, to accelerate the DMA transfer rate on the slave side and avoid FIFO overflow due to slow data movement.
2.4.3 Other Considerations
SPI Slave Testing Notes: When SPI acts as a slave, start the slave read first, then start the master write, otherwise, the slave may not finish reading before the master has already written.
For slave write and master read, the slave write must be started first, because only after the master sends the clock, the slave will work, and the master will immediately send or receive data.
For example:Based on the third chapter:
- First slave:
echo write 0 1 16 > /dev/spi_misc_test
- Then master:
echo read 0 1 16 > /dev/spi_misc_test
2.5 SPI Device Driver Introduction
Device Driver Registration:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/spi/spi.h>
static int spi_test_probe(struct spi_device *spi)
{
int ret;
if(!spi)
return -ENOMEM;
spi->bits_per_word= 8;
ret= spi_setup(spi);
if(ret < 0) {
dev_err(&spi->dev,"ERR: fail to setup spi\n");
return-1;
}
return ret;
}
static int spi_test_remove(struct spi_device *spi)
{
printk("%s\n",__func__);
return 0;
}
static const struct of_device_id spi_test_dt_match[]= {
{.compatible = "rockchip,spi_test_bus1_cs0", },
{.compatible = "rockchip,spi_test_bus1_cs1", },
{},
};
MODULE_DEVICE_TABLE(of, spi_test_dt_match);
static struct spi_driver spi_test_driver = {
.driver = {
.name = "spi_test",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(spi_test_dt_match),
},
.probe = spi_test_probe,
.remove = spi_test_remove,
};
static int __init spi_test_init(void)
{
int ret = 0;
ret = spi_register_driver(&spi_test_driver);
return ret;
}
module_init(spi_test_init);
static void __exit spi_test_exit(void)
{
return spi_unregister_driver(&spi_test_driver);
}
module_exit(spi_test_exit);
For SPI read and write operations, please refer to include/linux/spi/spi.h, here are a few simple ones
static inline int
spi_write(struct spi_device *spi,const void *buf, size_t len)
static inline int
spi_read(struct spi_device *spi,void *buf, size_t len)
static inline int
spi_write_and_read(structspi_device *spi, const void *tx_buf, void *rx_buf,
size_t len)
2.6 User Mode SPI Device Configuration
User mode SPI device refers to direct operation of the SPI interface from user space, making it convenient for many SPI peripheral drivers to run in user space without modifying the kernel, facilitating driver porting and development.
Kernel Configuration
Device Drivers --->
[*] SPI support --->
[*] User mode SPI device driver support
DTS Configuration
&spi0 {
status = "okay";
max-freq = <50000000>;
spi_test@0 {
compatible = "rockchip,spidev";
reg = <0>;
spi-max-frequency = <5000000>;
};
};
Usage Notes:
- After the driver device is successfully loaded and registered, a device with a name similar to
/dev/spidev1.1
will appear. - For device node read/write operation examples, please refer to:
- Kernel 4.4:
Documentation/spi/spidev_test.c
- Kernel 4.19 and later:
tools/spi/spidev_test.c
- Kernel 4.4:
- After kernel project compilation, enter the corresponding path and input the following command to directly compile the standard SPI app program:
make CROSS_COMPILE=~/path-to-toolchain/gcc-xxxxx-toolchain/bin/xxxx-linux-gnu-
Supports configuration as an SPI slave device, refer to "SPI Device Configuration — RK Chip as Slave End", where the DTS configuration sub node should remain as "rockchip,spidev"
.
2.7 cs-gpios Support
Users can implement GPIO-simulated CS to extend SPI chip select signals through the cs-gpios
property of spi-bus
. Detailed information about the cs-gpios
property can be found in the kernel documentation Documentation/devicetree/bindings/spi/spi-bus.txt
.
2.7.1 Linux 4.4 Configuration
This support requires multiple patches. Please contact RK engineers to obtain the corresponding patches.
2.7.2 Linux 4.19 and Later Kernel Configuration
Example using SPI1 setting GPIO0_C4 as spi1_cs2n extension pin
Set cs-gpio pins and reference them in SPI node:
diff --git a/arch/arm/boot/dts/rv1126-evb-v10.dtsi b/arch/arm/boot/dts/rv1126-
evb-v10.dtsi
index 144e9edf1831..c17ac362289e 100644
--- a/arch/arm/boot/dts/rv1126-evb-v10.dtsi
+++ b/arch/arm/boot/dts/rv1126-evb-v10.dtsi
&pinctrl {
...
+
+ spi1 {
+ spi1_cs0n: spi1-cs1n {
+ rockchip,pins =
+ <0 RK_PC2 RK_FUNC_GPIO
&pcfg_pull_up_drv_level_0>;
+ };
+ spi1_cs1n: spi1-cs1n {
+ rockchip,pins =
+ <0 RK_PC3 RK_FUNC_GPIO
&pcfg_pull_up_drv_level_0>;
+ };
+ spi1_cs2n: spi1-cs2n {
+ rockchip,pins =
+ <0 RK_PC4 RK_FUNC_GPIO
&pcfg_pull_up_drv_level_0>;
+ };
+ };
};
diff --git a/arch/arm/boot/dts/rv1126.dtsi b/arch/arm/boot/dts/rv1126.dtsi
index 351bc668ea42..986a85f13832 100644
--- a/arch/arm/boot/dts/rv1126.dtsi
+++ b/arch/arm/boot/dts/rv1126.dtsi
spi1: spi@ff5b0000 {
compatible = "rockchip,rv1126-spi", "rockchip,rk3066-spi";