Linux SPI初始化及接口函数代码细究
2012-01-08 22:11:38
目的:我需要掌握spi驱动相关数据结构关系,及在哪部分函数中把这些数值进行底层寄存器赋值的。结合应用层函数完成spi驱动的代码测试。已达到灵活修改的目的。
按顺序看probe函数中
if (!pdata->set_cs)
则 hw->set_cs = s3c24xx_spi_gpiocs;
gpio_direction_output(pdata->pin_cs, 1);
由于我的platform_device.platform_data没设置set_cs。默认设置gpio片选。并且把pin_cs脚设置为输出。接着
s3c24xx_spi_initialsetup(hw);函数里面有设置寄存器操作,设置默认值,代码如下:
/* for the moment, permanently enable the clock */
clk_enable(hw->clk);
/* program defaults into the registers */
writeb(0xff, hw->regs + S3C2410_SPPRE);
writeb(SPPIN_DEFAULT, hw->regs + S3C2410_SPPIN);
writeb(SPCON_DEFAULT, hw->regs + S3C2410_SPCON);
再接着就是spi_add_device函数中调用s3c24xx_spi_setup,里面有设置寄存器。
if (!spi->bits_per_word)
spi->bits_per_word = 8;
由于我的spi->bits_per_word之前都没有定义过。所以为0,那么默认设置spi->bits_per_word = 8;
接着调用s3c24xx_spi_setupxfer函数第一句就是struct s3c24xx_spi *hw = to_hw(spi);分析一下。
仔细看了下to_hw就是说spi把spi_device结构转换为s3c24xx_spi结构。
方法就是spi(spi_device结构)先指向父指针master(spi_master结构)。
接着就是指向dev(spi_master的device成员),再接着指向driver_data(device结构的driver_data成员)为什么说这就是s3c24xx_spi的类型哪?看probe一开始的几句代码即可。
struct s3c24xx_spi *hw;
hw = spi_master_get_devdata(master);
memset(hw, 0, sizeof(struct s3c24xx_spi));
接着回到s3c24xx_spi_setupxfer函数下面的代码是
bpw = t ? t->bits_per_word : spi->bits_per_word;
hz = t ? t->speed_hz : spi->max_speed_hz;
由于t传进来是NULL,所以bpw= spi->bits_per_word;刚才分析过了s3c24xx_spi_setup中把它设置为默认值8。hz = spi->max_speed_hz;在spi_add_device函数之前的spi_new_device有赋值proxy->max_speed_hz = chip->max_speed_hz;就是spi_board_info里的赋值,我自己设置的值。至于要写入寄存器是在接下来的hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE)中调用hw->set_cs(hw->pdata, spi->chip_select, cspol^1);(probe中hw->set_cs = s3c24xx_spi_gpiocs里定义的) s3c24xx_spi_gpiocs函数,设置cspol值为0。
此函数也调用div = clk_get_rate(hw->clk) / hz;设置波特率寄存器。
===========================================================bitbang_work函数中有调用bitbang->chipselect(spi, BITBANG_CS_ACTIVE);
s3c24xx_spi_probe中有定义 hw->bitbang.chipselect = s3c24xx_spi_chipsel;
s3c24xx_spi_chipsel函数里面有设置spi模式。数据来源是spi->mode。spi->mode又是在那里赋值的呢?带着问题做了如下探索
bitbang_work是在spi_bitbang_start中调用。
s3c24xx_spi_probe->spi_bitbang_start->bitbang_work但此时,spi-mode并没有赋值。
赋值是在哪里呢?换个方法,按顺序查找。
s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device通过从头到尾的方式查找,唯一首先出现spi_master与spi_device关系的是spi_new_device函数。进入其中调用的spi_alloc_device函数,查看注释发现。
* Caller is responsible to call spi_add_device() on the returned
* spi_device structure to add it to the SPI master.
可以确定,spi->mode的首次赋值就是词句代码
proxy->mode = chip->mode;在bitbang_work函数中spi-mode并没有赋值但是还在用是为什么呢?再仔细看代码,原来while有个判断条件。之前没看到,搜索错了方向。
在spi_new_device函数中proxy是spi_device结构。Spi->mode等都是在如下这里赋值
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
proxy->mode = chip->mode; //s3c2410_spi1_board中没定义,则默认为0
proxy->irq = chip->irq;
strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
proxy->dev.platform_data = (void *) chip->platform_data;
proxy->controller_data = chip->controller_data;
proxy->controller_state = NULL;
========================
接着就是spi_add_device函数,里面有一句if (spi->chip_select >= spi->master->num_chipselect) 他们分别是什么值呢?spi->master->num_chipselect在哪里?带着问题,开始了如下探索历程
spi->chip_select在spi_board_info s3c2410_spi1_board[]结构赋值中为0。
static struct spi_board_info s3c2410_spi1_board[] = {
[0] = {
.modalias = "spidev",
.bus_num = 1, .chip_select = 0,
.irq = IRQ_EINT9,
.max_speed_hz = 2000*1000,
},
};
在scan_boardinfo 中调用函数时候传递的参数是(void) spi_new_device(master, chip);master是spi_master结构。再之前的spi_register_master函数中有对spi_master的num_chipselect成员赋值的核对。代码如下:
if (master->num_chipselect == 0)
return -EINVAL;再往前找应该就能找到赋值了。
spi_bitbang_start中有对spi_register_master的调用。代码如下:
status = spi_register_master(bitbang->master);
再往前看。s3c24xx_spi_probe函数中调用spi_bitbang_start,往上看到了master->num_chipselect的赋值语句了。代码如下:
master->num_chipselect = hw->pdata->num_cs;
而hw->pdata = pdata = pdev->dev.platform_data;
而struct platform_device *pdev是platform_device中的platform_data结构,已经赋值了如下
static struct s3c2410_spi_info s3c2410_spi1_platdata = {
.pin_cs = S3C2410_GPG3,
.num_cs = 1,
.bus_num = 1,
};
所以chip_select= 0, spi->master->num_chipselect值为1.
通过倒序来找,从spi_add_device一直找到了s3c24xx_spi_probe,按顺序写下:
s3c24xx_spi_probe->spi_bitbang_start->spi_register_master->scan_boardinfo->spi_new_device->spi_add_device
一个接口对应一个master,一个master对应一条SPI总线,一条总线上可能挂有多个设备,num_chipselect 就表示该总线上的设备, chip_select表示该SPI设备在该条SPI总线上的设备号的唯一标识。
====================================================probe中函数绑定与调用分析
hw->bitbang.master = hw->master;
hw->bitbang.setup_transfer = s3c24xx_spi_setupxfer;// s3c24xx_spi_setup中调用ret = s3c24xx_spi_setupxfer(spi, NULL);
hw->bitbang.chipselect = s3c24xx_spi_chipsel;//s3c24xx_spi_setupxfer中调用hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
hw->bitbang.txrx_bufs = s3c24xx_spi_txrx;// bitbang_work中调用status = bitbang->txrx_bufs(spi, t);
hw->bitbang.master->setup = s3c24xx_spi_setup;// spi_add_device中调用status = spi->master->setup(spi);
按probe初始化顺序,则spi_add_device函数中调用
status = spi->master->setup(spi);//(s3c24xx_spi_setup)
接着s3c24xx_spi_setup函数中调用
ret = s3c24xx_spi_setupxfer(spi, NULL);
接着s3c24xx_spi_setupxfer函数中调用
hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE)
关于如上图的问题,怎么HZ数一开始不是我默认设置的呢?原来是因为,用户层函数调用的时候先是从octl函数开始的不是从probe函数开始的。
probe函数打印出来的信息完全正确。如下图:
先调用spidev_message如何调用到bitbang_work的?
spi_bitbang_start->bitbang_work接着就没方向了,不可能到spidev_message了。于是查了网上的资料,是spi_bitbang_transfer 中的代码如下:queue_work(bitbang->workqueue, &bitbang->work); 调用了bitbang_work函数。为什么bitbang->work指示的就是bitbang_work函数呢?原来在 spi_bitbang_start函数中的第一句代码INIT_WORK(&bitbang->work, bitbang_work);就说明了。好了,这样就有方向继续摸索了。关系流程如下:
spidev_message ->spidev_sync->spi_async【spi->master->transfer(spi, message); 定义在spi_bitbang_start函数中bitbang->master->transfer = spi_bitbang_transfer;】 -> spi_bitbang_transfer->bitbang_work
===============================================================
bitbang_work函数中有句代码
setup_transfer = bitbang->setup_transfer;
if (setup_transfer) {
status = setup_transfer(spi, t);
bitbang->setup_transfer(spi, t)又是调用哪个具体函数呢?
很面熟,想起来之前好像看到过在bitbang相关函数中,于是找到了spi_bitbang_start函数中有,代码如下:
if (!bitbang->master->setup) {
if (!bitbang->setup_transfer)
bitbang->setup_transfer = spi_bitbang_setup_transfer;
原来是调用准备spi_bitbang_setup_transfer函数,先设置指针,传递进来的参数是(spi, t); 接着就在bitbang_work函数中调用status = setup_transfer(spi, t);即spi_bitbang_setup_transfer函数
其中与修改频率相关的代码如下:t是指向用户传递来的数值,如果应用层函数没有传递Hz值,则使用spi->max_speed_hz;就是spi_board_info中我自己定义的值。终于找到了赋值的元凶。代码如下:
/* nsecs = (clock period)/2 */
if (!hz)
hz = spi->max_speed_hz;
if (hz) {
cs->nsecs = (1000000000/2) / hz;
if (cs->nsecs > (MAX_UDELAY_MS * 1000 * 1000))
return -EINVAL;
}
从打印的信息看出spi_bitbang_setup_transfer会调用s3c24xx_spi_setupxfer函数?原来之前分析错了status = setup_transfer(spi, t);调用的probe中的s3c24xx_spi_setupxfer。因为这个if (!bitbang->master->setup) 条件不成了,所以bitbang.setup_transfer = s3c24xx_spi_setupxfer。这样就能进入s3c24xx_spi_setupxfer函数了,并且这时候t不等于NULL。
bitbang_work函数中的struct spi_transfer *t = NULL;那么到底是哪一句为t赋值的呢?再调试。应该是list_for_each_entry (t, &m->transfers, transfer_list) 句。
继续分析串口信息,又进入了s3c24xx_spi_setupxfer啊!bitbang_work有调用吗?继续看,代码如下
/* restore speed and wordsize */
if (setup_transfer)
setup_transfer(spi, NULL);
if (!(status == 0 && cs_change)) {
ndelay(nsecs);
bitbang->chipselect(spi, BITBANG_CS_INACTIVE);
ndelay(nsecs);
}
天呢!原来回复数据。也就是说我的此次spi自收发,用的不是初始化的值。原因是在应用层函数中传入了if (t->speed_hz || t->bits_per_word) 判断条件中的2个值。如果不传这2个值,那么应该就用的是我在kernel中设置的默认值了。试一下,名称为spidev_test5
见下图,效果和想象的是一致的。speed_hz与bits_per_word我在应用层注释掉了。就没调用如下代码段,途中的“ready to change freq”信息应该写成finish to change freq and other比较好,呵呵。
if (t->speed_hz || t->bits_per_word) {
printk("go bitbang_work 1\n");//by apple
setup_transfer = bitbang->setup_transfer;
if (!setup_transfer) {
status = -ENOPROTOOPT;
break;
}
}
if (setup_transfer) {
printk("setup_transfer\n"); //by apple
printk("NULL addr is 0x%2x,t addr is 0x%2x ",NULL,t); //by apple
status = setup_transfer(spi, t);
if (status < 0)
break;
}
备注:我的spidev_test函数的主函数中write和read函数都被我注释掉了。直接先transfer(fd);接着在read 2个寄存器后打印出来mode和speed。
上图1:
上图2:
上图3:
Linux SPI初始化及接口函数代码细究的更多相关文章
- Linux时间子系统之(三):用户空间接口函数
专题文档汇总目录 Notes:用户空间时间相关接口函数: 类型 API 精度 说明 时间 time stime time_t 精度为秒级 逐渐要被淘汰.需要定义__ARCH_WANT_SYS_TIME ...
- Linux时间子系统(三) 用户空间接口函数
一.前言 从应用程序的角度看,内核需要提供的和时间相关的服务有三种: 1.和系统时间相关的服务.例如,在向数据库写入一条记录的时候,需要记录操作时间(何年何月何日何时). 2.让进程睡眠一段时间 3. ...
- 使用line_profiler查看api接口函数每行代码执行时间
项目情景描述: 在restful架构风格的项目交付测试的过程中,某接口出现 请求超时导致的http 502 Bad Gateway,于是开始排查具体是接口函数中的哪行代码或函数 响应时间过长导致的50 ...
- linux下使用gcc/g++编译代码时gets函数有错误
今天在linux中使用个g++编译一个名为myfirst.cpp的代码的时候,出现如下错误 myfirst.cpp: In function ‘int main()’:myfirst.cpp:11:2 ...
- linux spi驱动开发学习-----spidev.c和spi test app
一.spidev.c文件 看一个设备驱动的方法: module_init标识的入口初始化函数spidev_init,(module_exit标识的出口函数) 设备与设备驱动匹配时候调用的probe方法 ...
- Linux SPI总线和设备驱动架构之四:SPI数据传输的队列化
我们知道,SPI数据传输可以有两种方式:同步方式和异步方式.所谓同步方式是指数据传输的发起者必须等待本次传输的结束,期间不能做其它事情,用代码来解释就是,调用传输的函数后,直到数据传输完成,函数才会返 ...
- Linux SPI总线和设备驱动架构之三:SPI控制器驱动
通过第一篇文章,我们已经知道,整个SPI驱动架构可以分为协议驱动.通用接口层和控制器驱动三大部分.其中,控制器驱动负责最底层的数据收发工作,为了完成数据的收发工作,控制器驱动需要完成以下这些功能:1. ...
- linux驱动基础系列--linux spi驱动框架分析
前言 主要是想对Linux 下spi驱动框架有一个整体的把控,因此会忽略某些细节,同时里面涉及到的一些驱动基础,比如平台驱动.设备模型等也不进行详细说明原理.如果有任何错误地方,请指出,谢谢! spi ...
- Linux 编程中的API函数和系统调用的关系【转】
转自:http://blog.chinaunix.net/uid-25968088-id-3426027.html 原文地址:Linux 编程中的API函数和系统调用的关系 作者:up哥小号 API: ...
随机推荐
- CSS3_边框 border 详解_一个 div 的阴阳图
(面试题) 怎么样通过 CSS 画一个三角形: 1. 元素的 width 和 height 设置为 0 2. 边框 足够大 3. 需要的三角形的部分, border-top-color 设置为 ...
- Jquery常用的方法总结
1.关于页面元素的引用通过jquery的$()引用元素包括通过id.class.元素名以及元素的层级关系及dom或者xpath条件等方法,且返回的对象为jquery对象(集合对象),不能直接调用dom ...
- 课堂动手动脑String
一 public class StringPool { public static void main(String args[]) { String s0="Hello"; St ...
- 微信小程序 组件 Demo
文字跑马灯效果: http://www.wxapp-union.com/portal.php?mod=view&aid=1038 触摸水波涟漪效果: http://www.wx ...
- 保护模式.vbs
Sub Main Dim cnt Dim delay delay = 10000 For cnt = 0 To 80 crt.screen.Send "interface optical-r ...
- robot framework + win7 64 上的安装
1.安装 python 2.7 2.cmd 管理模式 python -m pip install --upgrade pip pip install robotframework==3. ...
- [02-02 ]Java数据库链接范列
/* 01 连接池版本的 数据库 连接管理工具,适合于并发场合 */ package cn.tedu.jdbc.day02; import java.io.InputStream; import ja ...
- MGR
单主模式 参数修改 server_id=1 gtid_mode=ON enforce_gtid_consistency=ON binlog_checksum=NONE log_bin=binlog l ...
- [emacs] emacs设置python code的indent
装python-mode的包 https://gitlab.com/python-mode-devs/python-mode/tree/master python-mode emacswiki文档 e ...
- 可扩展的Web架构和分布式系统
原文链接:http://www.aosabook.org/en/distsys.html 开源软件已经成为一些大型网站的基石.随着这些网站的发展,围绕其架构的最佳实践和指导原则应运而生.本章旨在讨论设 ...