通用SPI驱动框架编写
前言
在编写驱动框架前我们先需要对SPI子系统框架的整体流程有所了解,下面图说明了子系统的整体流程
一、SPI的设备树编写
2.1 dirver部分的组成
SPI的驱动控制器一般由原厂编写,像瑞芯微的SPI驱动控制器就包含在内核设备树下的这个设备树文件
arch/arm64/boot/dts/rockchip/rk3568.dtsi
然后SPI子系统的驱动框架是遵循LDM数据结构设计的,都是基于这个设备模型的基类的基础上建立起来的。对于我们的设备树的匹配是通过在内核下的kernel/driversspi/spi-rockchip.c 这个文件中。
2.2 添加自己设备的设备树代码
使用设备树插件的方式添加到开发板中,如果你不知道什么是设备树插件可以查看这下面两个链接
野火设备树插件的使用:https://doc.embedfire.com/linux/rk356x/driver/zh/latest/linux_driver/base_dynamic_device_tree.html
讯为电子设备树插件的学习:https://www.bilibili.com/video/BV1to4y1W7wa/?spm_id_from=333.337.search-card.all.click&vd_source=09527683ae3c0bc21c4e9e00ee78154b
&spi3{
status = "okay";
//pinctrl-names = "default", "high_speed";
spi_gc9a01:spi_gc9a01@1 {
compatible = "ailun,gc9a01";
//片选和时钟最大频率必须添加否则会进不去probe函数
reg = <0>; //选择片选0
spi-max-frequency = <12000000>; //最大时钟频率12MHZ ,RK3568最大不能超过50MHZ
};
};
//设备树选配
//spi‐cpha; //如果有配,cpha为1
//spi‐cpol; //如果有配,cpol为1,clk脚保持高电平
//spi‐cs‐high; //如果有配,每传完一个数据,cs都会被拉高,再拉低
在kernel目录下对设备树进行编译
#加载配置文件
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- lubancat2_defconfig
#使用dtbs参数单独编译设备树
make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
#添加
dtoverlay=/dtb/overlay/lubancat-spi-overlay.dtbo
编译出来的设备树插件在 内核源码/arch/arm64/boot/dts/rockchip/overlay/xxx.dtbo, 将设备树插件传到板卡的 /boot/dtb/overlay/ 目录下,并在 /boot/uEnv/uEnv.txt 按照格式添加我们的设备树插件,
#虚拟机执行
adb push lubancat-spi-overlay.dtbo /boot/dtb/overlay/
#重启
reboot
然后重启开发板,那么系统就会加载我们编译的设备树插件。
2.3 关于设备树的处理过程
int spi_register_controller(struct spi_controller *ctlr) --> of_register_spi_devices
static struct spi_device *
of_register_spi_device(struct spi_controller *ctlr, struct device_node *nc)
{
struct spi_device *spi;
int rc;
/* Alloc an spi_device */
spi = spi_alloc_device(ctlr);
if (!spi) {
dev_err(&ctlr->dev, "spi_device alloc error for %pOF\n", nc);
rc = -ENOMEM;
goto err_out;
}
/* Select device driver */
rc = of_modalias_node(nc, spi->modalias,
sizeof(spi->modalias));
if (rc < 0) {
dev_err(&ctlr->dev, "cannot find modalias for %pOF\n", nc);
goto err_out;
}
rc = of_spi_parse_dt(ctlr, spi, nc);
if (rc)
goto err_out;
/* Store a pointer to the node in the device structure */
of_node_get(nc);
spi->dev.of_node = nc;
spi->dev.fwnode = of_fwnode_handle(nc);
/* Register the new device */
rc = spi_add_device(spi);
if (rc) {
dev_err(&ctlr->dev, "spi_device register error %pOF\n", nc);
goto err_of_node_put;
}
return spi;
err_of_node_put:
of_node_put(nc);
err_out:
spi_dev_put(spi);
return ERR_PTR(rc);
}
of_register_spi_devices
/**
* of_modalias_node - Lookup appropriate modalias for a device node
* @node: pointer to a device tree node
* @modalias: Pointer to buffer that modalias value will be copied into
* @len: Length of modalias value
*
* Based on the value of the compatible property, this routine will attempt
* to choose an appropriate modalias value for a particular device tree node.
* It does this by stripping the manufacturer prefix (as delimited by a ',')
* from the first entry in the compatible list property.
*
* This routine returns 0 on success, <0 on failure.
*/
int of_modalias_node(struct device_node *node, char *modalias, int len)
{
const char *compatible, *p;
int cplen;
compatible = of_get_property(node, "compatible", &cplen);
if (!compatible || strlen(compatible) > cplen)
return -ENODEV;
p = strchr(compatible, ',');
strlcpy(modalias, p ? p + 1 : compatible, len);
return 0;
}
EXPORT_SYMBOL_GPL(of_modalias_node);
二、SPI的device部分
对于device我们最后是通过spi控制器完成最后的匹配注册出来的设备,遵守的platform平台总线的框架进行编写的
1.1 device部分的框架代码编写
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
// struct spi_driver {
// const struct spi_device_id *id_table;
// int (*probe)(struct spi_device *spi);
// int (*remove)(struct spi_device *spi);
// void (*shutdown)(struct spi_device *spi);
// struct device_driver driver;
// };
int spi_probe(struct spi_device *spi)
{
printk("spi_probe OK!\n");
return 0;
}
int spi_remove(struct spi_device *spi)
{
return 0;
}
const struct of_device_id SPI_of_math_table[] = {
{ .compatible = "ailun,gc9a01" },
{}
};
const struct spi_device_id SPI_id_table[] = {
{ "SPI_TEST"},
{}
};
//注意如果使用设备树就是SPI_of_math_table进行匹配
//如果没有使用设备树就是使用.name = "SPI_TEST", 和SPI_id_table进行匹配
struct spi_driver spi_test ={
.probe = spi_probe,
.remove = spi_remove,
.driver = {
.name = "SPI_TEST",
.owner = THIS_MODULE,
.of_match_table = SPI_of_math_table,
},
.id_table = SPI_id_table
};
static int __init Spi_init(void)
{
int ret;
ret = spi_register_driver(&spi_test);
if(ret<0){
printk("spi_register_driver failed!\n");
return ret;
}
return ret;
}
static void __exit Spi_Eixt(void)
{
spi_unregister_driver(&spi_test);
}
module_init(Spi_init);
module_exit(Spi_Eixt);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("2450828010@qq.com");