基于trace的正点原子imx6ull开发板linux内核spi驱动调用关系分析

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

学习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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值