SPI开发指南
1.Rockchip SPI 功能特点
SPI (serial peripheral interface), 以下是 linux 4.4 SPI 驱动支持的一些特性︰
- 默认采用摩托罗拉 SPI 协议
- 支持 8 位和 16 位
- 软件可编程时钟频率和传输速率高达 50MHz
- 支持 SPI 4 种传输模式配置
- 每个 SPI 控制器支持一个到两个片选
除以上支持,linux 4.19 新增以下特性:
- 框架支持 slave 和 master 两种模式
2.内核软件
2.1 代码路径
drivers/spi/spi.c spi 驱动框架
drivers/spi/spi-rockchip.c rk spi 各接口实现
drivers/spi/spidev.c 创建 spi 设备节点,用户态使用。
drivers/spi/spi-rockchip-test.c spi 测试驱动,需要自己手动添加到 Makefile 编译
Documentation/spi/spidev_test.c 用户态 spi 测试工具
2.2 SPI 设备配置 -- RK 芯片作 Master 端
内核配置
Device Drivers --->
[*] SPI support --->
<*> Rockchip SPI controller driver
DTS 节点配置
&spi1{
status = "okay";
//assigned-clock-rates = <200000000>; // 默认不用配置,SPI设备工作时钟值,io时钟由工作时钟分频获取
dma-names = "tx","rx"; // 使能DMA模式,通讯长度少于32字节不建议用,置空赋值去掉使能,如 "dma-names;";
//rx-sample-delay-ns = <10>; // 默认不用配置,读采样延时,详细参考 “常见问题”“延时采样时钟配置方案” 章节
spi_test@10 {
compatible ="rockchip,spi_test_bus1_cs0"; //与驱动对应的名字
reg=<0>; //片选0或者1
spi-cpha; //设置CPHA=1,不配置则为0
spi-cpo1; spi-1sb-first; //设置CPOL=1,不配置则为0 //IO先传输1sb
spi-max-frequency =<24000000>; //spic1k输出的时钟频率,不超过50M
status = "okay"; //使能设备节点
);
);
spilk assigned-clock-rates 和 spi-max-frcquney 的配置说明:
- spiimax-fqueney 是 SPI 的输出时钟,由 SPI 工作时钟 spiclk assignd-clockrates 内部分频后输出,由于内部至少 2 分频,所以关系是 spiclk assigned-lock-rates>=2*spi-max-frcquency:
- 假定需要 50MHz 的 SPIO 速率,可以考虑配置 (记住内部分频为偶数分频)
spi_clkassind-lock ratcs=<10000000
,pi-max-ficquency=<50000000
, 即工作时钟 100MHz (PLL 分频到一个不 大于 100MHz 但最接近的值), 然后内部二分频最终 10 接近 50 MHz: - spiclk assigned-lock-rates 不要低于 24M, 否则可能有问题;
- 如果需要配置 spi-cpha 的话,要求
spiclk assigned-clock-rates<=6M,1M<=spi-max-fequeney> 3M
。
2.3 SPI 设备配置 --RK 芯片作 Slave 端
SPI 做 slave 使用的接口和 master 模式一样,都是 spi read 和 spi_writc。
2.3.1 Linux 4.4 配置
内核补丁
请先检查下自己的代码是否包含以下补丁,如果没有,请手动打上补丁:
diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c
index 060806e..38eecde 100644
--- a/drivers/spi/spi-rockchip.c
+++ b/drivers/spi/spi-rockchip.c
@@ -519,6 +519,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs)
cr0 |= ((rs->mode & 0x3) << CRO_SCPH_OFFSET);
cr0 |= (rs->tmode << CRO_XFM_OFFSET);
cr0 |= (rs->type << CR0_FRF_OFFSET);
+ if (rs->mode & SPI_SLAVE_MODE)
+ cr0 |= (CR0_OPM_SLAVE << CR0_OPM_OFFSET);
if(rs->use_dma){
if (rs->tx)
@@ -734,7 +736,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)
master->auto_runtime_pm = true;
master->bus_num = pdev->id;
- master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP;
+ master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_SLAVE_MODE;
master->num_chipselect = 2;
master->dev.of_node = pdev->dev.of_node;
master->bits_per_word_mask = SPI_BPW_MASK(16) | SPI_BPW_MASK(8);
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index dee1cb8..4172da1 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1466,6 +1466,8 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc)
spi->mode |= SPI_3WIRE;
if (of_find_property(nc, "spi-lsb-first", NULL))
spi->mode |= SPI_LSB_FIRST;
+ if (of_find_property(nc, "spi-slave-mode", NULL))
+ spi->mode |= SPI_SLAVE_MODE;
/*Device DUAL/QUAD mode*/
if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index cce80e6..ce2cec6 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -153,6 +153,7 @@ struct spi_device {
#define SPI_TX_QUAD 0x200 /* transmit with 4 wires */
#define SPI_RX_DUAL 0x400 /* receive with 2 wires */
#define SPI_RX_QUAD 0x800 /* receive with 4 wires */
+#define SPI_SLAVE_MODE 0x1000 /* enable SPI slave mode*/
int irq;
void *controller_state;
void *controller_data;