提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
学习linux内核spi驱动,发现结构体中函数指针指来指去让人很绕,总是找不到调用函数,网上看到有大佬分享linux下trace可查看函数调用关系,故总结此文章,通过trace工具分析spi_sync()和spi_async()的调用过程。
spi驱动主要涉及文件如下:
spi-imx.c
spi-bitbang.c
spidev.c
一、linux下trace功能开启
1.1menuconfig操作
进入linux下menuconfig,tracers配置如下图所示:
配置完tracers后,debug filesystem是默认开启的,但是最好也检查一下,配置界面如下:
1.2编译内核
编译内核和设备树。
make
将新编译的内核和根文件系统添加入tftpboot目录下,重启正点原子IMX6ULL开发板。
二、trace使用步骤
2.1spi_sync同步模式调用关系查询
2.1.1spi驱动加载
spi驱动加载参考《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6.pdf》下第62章节。
正点原子的spi驱动是使用的同步模式,调用的spi_sync()函数,具体代码如下:
static s32 icm20608_write_regs(struct icm20608_dev *dev, u8 reg, u8 *buf, u8 len)
{
int ret = -1;
unsigned char *txdata;
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); /* 申请内存 */
if(!t) {
return -ENOMEM;
}
txdata = kzalloc(sizeof(char)+len, GFP_KERNEL);
if(!txdata) {
goto out1;
}
/* 一共发送len+1个字节的数据,第一个字节为
寄存器首地址,len为要写入的寄存器的集合,*/
*txdata = reg & ~0x80; /* 写数据的时候首寄存器地址bit8要清零 */
memcpy(txdata+1, buf, len); /* 把len个寄存器拷贝到txdata里,等待发送 */
t->tx_buf = txdata; /* 要发送的数据 */
t->len = len+1; /* t->len=发送的长度+读取的长度 */
spi_message_init(&m); /* 初始化spi_message */
spi_message_add_tail(t, &m);/* 将spi_transfer添加到spi_message队列 */
ret = spi_sync(spi, &m); /* 同步发送 */
if(ret) {
goto out2;
}
out2:
kfree(txdata); /* 释放内存 */
out1:
kfree(t); /* 释放内存 */
return ret;
}
我们在板子串口输入如下命令加载驱动:
depmod //第一次加载驱动的时候需要运行此命令
modprobe icm20608.ko //加载驱动模块
2.1.2trace调用函数追踪
我们在板子串口输入如下命令:
mount -t debugfs none /sys/kernel/debug/
cd /sys/kernel/debug/tracing/
echo 0 > tracing_on
echo function_graph > current_tracer
//同步模式查询
echo spi_sync > set_graph_function
echo *spi* > set_ftrace_filter
echo *dma* >> set_ftrace_filter
echo *spin* >> set_ftrace_notrace
echo 1 > tracing_on
/lib/modules/4.1.15/icm20608App /dev/icm20608
echo 0 > tracing_on
cat trace
注意:
echo dma >> set_ftrace_filter 的>>表示在此文件中append添加,如果是>则是覆盖。
2.1.3spi_sync调用关系
trace中打印如下:
0) | spi_sync() {
0) | __spi_sync() {
0) 2.667 us | __spi_validate();
0) 1.667 us | __spi_queued_transfer();
0) | __spi_pump_messages() {
0) 1.667 us | spi_bitbang_prepare_hardware();
0) + 11.667 us | spi_imx_prepare_message();
0) 1.333 us | spi_imx_can_dma();
0) | spi_bitbang_transfer_one() {
0) | spi_imx_setupxfer() {
0) 4.000 us | mx51_ecspi_config();
0) + 11.000 us | }
0) 5.000 us | spi_imx_chipselect();
0) | spi_imx_transfer() {
0) | spi_imx_push() {
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 0.667 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.000 us | spi_imx_buf_tx_u8();
0) 1.334 us | mx51_ecspi_trigger();
0) ! 106.000 us | }
0) 1.333 us | mx51_ecspi_intctrl();
0) ! 188.666 us | }
0) 3.334 us | spi_imx_chipselect();
0) | spi_finalize_current_message() {
0) 0.667 us | spi_imx_can_dma();
0) 7.000 us | spi_imx_unprepare_message();
0) 1.667 us | spi_complete();
0) + 69.000 us | }
0) ! 314.667 us | }
0) ! 356.334 us | }
这就是spi_sync()的整体调用关系,我们可根据上述函数学习spi驱动源码。
2.2spi_async异步模式调用关系查询
2.2.1spidev驱动加载
2.2.1.1源码学习
linux内核源码中spidev.c是调用的spi_async()函数,正好可以供我们分析,源码如下:
static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
message->complete = spidev_complete;
message->context = &done;
spin_lock_irq(&spidev->spi_lock);
if (spidev->spi == NULL)
status = -ESHUTDOWN;
else
status = spi_async(spidev->spi, message);
spin_unlock_irq(&spidev->spi_lock);
if (status == 0) {
wait_for_completion(&done);
status = message->status;
if (status == 0)
status = message->actual_length;
}
return status;
}
如果我们想将正点原子的icm20608.c里的spi同步发送改成异步发送,可以参考上述源码进行修改。
2.2.1.2menuconfig配置
进入menuconfig下,使能spidev功能,勾选下图黄框中功能。
2.2.1.3修改设备树
spidev.c源码中,compatible属性为"rohm,dh2228fv",如下所示,所以我们需要修改匹配值。
static const struct of_device_id spidev_dt_ids[] = {
{
.compatible = "rohm,dh2228fv" },
{
},
};
基于正点原子第62章节的设备树,屏蔽spidev: icm20608@0 内容,添加spidevtest: test@0内容,具体修改为如下:
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
spidevtest: test@0 {
compatible = "rohm,dh2228fv";
spi-max