×
单片机 > 单片机程序设计 > 详情

S3C2440 Linux2.6 I2C驱动程序之框架和编写(二十八)

发布时间:2024-05-18 发布时间:
|

上一节 我们学习了:


IIC接口下的AT24C02驱动分析:/zixunimg/eeworldimg/blog.csdn.net/xiaodingqq/article/details/81808875


接下来本节,学习Linux下如何利用linux下I2C驱动体系结构来操作AT24C02


1、I2C体系结构分析


1.1 首先进入linux内核的driver/i2c目录下,如下图所示:

其中重要的文件介绍如下:

1)algos文件夹(algorithms)


里面保存I2C的通信方面的算法


2)busses文件夹


里面保存I2C总线驱动相关的文件,比如i2c-omap.c、 i2c-versatile.c、 i2c-s3c2410.c等。


3)chips文件夹


里面保存I2C设备驱动相关的文件夹,如下图所示,比如mt41t00,就是RTC实时时钟

4) i2c-core.c


这个文件实现了I2C核心的功能(I2C总线的初始化、注册和适配器添加和注销等相关工作)以及/proc/bus/i2c*接口。


5)i2c-dev.c


提供了通用的read()、write()和ioctl()等接口,实现了适配器设备文件的功能,其中I2C设备的主设备号都为89,次设备号为0~255。


应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。


显然,它和前几次驱动类似,I2C也分为总线驱动和设备驱动,总线就是协议相关的,它知道如何收发数据,但不知道数据含义,设备驱动却知道数据含义


1.2 I2C驱动架构,如下图所示:


如上图所示,每一条I2C对应一个adapter适配器,在Kernel中,adapter适配器是通过struct adapter结构体定义,主要是通过i2c core层将i2c设备与i2c adapter关联起来。


在kernel中,提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter()。由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号。这个总线号的PCI中的总线号不同。它和硬件无关,只是软件上便于区分而已。


对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分析一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。


2、接下来便分析I2C总线驱动


参考drivers/i2c/busses/i2c-s3c2410.c

在init函数中,注册一个"s3c2440-i2c"的platform平台驱动,我们来看看probe函数做了些什么。


3、进入s3c24xx_i2c_probe函数


static int s3c24xx_i2c_probe(struct platform_device *pdev)

{

struct s3c24xx_i2c *i2c = &s3c24xx_i2c;

... ...

/* 获取,使能I2C时钟 */

i2c->clk = clk_get(&pdev->dev, "i2c"); //获取i2c时钟

clk_enable(i2c->clk); //使能i2c时钟

... ...

/* 获取资源 */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

i2c->regs = ioremap(res->start, (res->end-res->start)+1);

... ...

/* 设置i2c_adapter适配器结构体,将i2c结构体设为adap私有数据 */

i2c->adap.algo_data = i2c; //i2c_adapter适配器指向s3c24xx_i2c

i2c->adap.dev.parent = &pdev->dev;

/* initialise the i2c controller */

/* 初始化2440的I2C相关寄存器 */

ret = s3c24xx_i2c_init(i2c);

if (ret != 0)

goto err_iomap;

... ...

/* 注册中断服务函数 */

ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,

pdev->name, i2c);

... ...

/* 注册i2c_adapter适配器结构体 */

ret = i2c_add_adapter(&i2c->adap);

}


其中i2c_adapter结构体是放在s3c24xx_i2c->adap下,如下图所示:

4、接下来我们进入i2c_add_adapter()函数看看,到底如何注册的


int i2c_add_adapter(struct i2c_adapter *adapter)

{

int id, res = 0;

retry:

if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //调用idr_pre_get()为i2c_adapter预留内存空间

return -ENOMEM;

mutex_lock(&core_lists);

/* "above" here means "above or equal to", sigh */

res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);

//调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体

mutex_unlock(&core_lists);

if (res < 0) {

if (res == -EAGAIN)

goto retry;

return res;

}

adapter->nr = id;

return i2c_register_adapter(adapter); //调用i2c_register_adapter()函数进一步来注册.

}

其中register_adapter()函数代码如下所示:


static int i2c_register_adapter(struct i2c_adapter *adap)

{

struct list_head *item; //链表头,用来存放i2c_driver结构体的表头

struct i2c_driver *driver; //i2c_driver,用来描述一个IIC设备驱动

list_add_tail(&adap->list, &adapters); //添加到内核的adapter链表中

... ...

list_for_each(item,&drivers) { //for循环,从drivers链表里找到i2c_driver结构体的表头

driver = list_entry(item, struct i2c_driver, list); //通过list_head表头,找到i2c_driver结构体

if (driver->attach_adapter)

/* We ignore the return code; if it fails, too bad */

driver->attach_adapter(adap);

//调用i2c_driver的attach_adapter函数来看看,这个新注册的设配器是否支持i2c_driver

}

}


在i2c_register_adapter()函数里主要执行以下几步:


1 将adapter放入i2c_bus_type的adapter链表


2 将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数来匹配


其中,i2c_driver结构体会在后面讲述到


而i2c_adapter适配器结构体的成员结构,如下所示:


struct i2c_adapter {

struct module *owner; //所属模块

unsigned int id; //algorithm的类型,定义于i2c-id.h,

unsigned int class;

const struct i2c_algorithm *algo; //总线通信方法结构体指针

void *algo_data; //algorithm数据

struct mutex bus_lock; //控制并发访问的自旋锁

struct mutex clist_lock;

int timeout;

int retries; //重试次数

struct device dev; //适配器设备

int nr; //存放在i2c_adapter_idr里的位置号

struct list_head clients;

struct list_head list;

char name[48]; //适配器名称

struct completion dev_released;

};

i2c_adapter表示物理上的一个i2c设备(适配器),在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct i2c_adapter adap)成员中


5、其中s3c24xx_i2c的结构体成员如下所示


/* i2c bus registration info */

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {

.master_xfer = s3c24xx_i2c_xfer, //主机传输

.functionality = s3c24xx_i2c_func,

};

static struct s3c24xx_i2c s3c24xx_i2c = {

.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),

.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),

.tx_setup = 50, //用来延时,等待SCL被释放

.adap = { //i2c_adapter适配器结构体

.name = "s3c2410-i2c",

.owner = THIS_MODULE,

.algo = &s3c24xx_i2c_algorithm,//存放i2c_algorithm算法结构体

.retries = 2, //重试次数

.class = I2C_CLASS_HWMON,

},

};


显然,这里是直接设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe()函数中没有分配i2c_adapter适配器结构体


其中,i2c_adapter结构体的名称等于“s3c2410-i2c”,它的通信方式等于s3c24xx_i2c_algorithm,重新次数等于2


PS:如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是个i2c设备,而没有通信方式


s3c24xx_i2c_algorithm中的关键函数master_xfer()就是用于产生i2c访问周期需要的start stop ack信号


比如,在s3c24xx_i2c_algotithm中的关键函数mater_xfer()里,调用了:


s3c24xx_i2c_xfer->s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()


来启动传输message信息,其中s3c24xx_i2c_message_start()函数代码如下:


static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,

struct i2c_msg *msg)

{

unsigned int addr = (msg->addr & 0x7f) << 1;//I2C从设备地址的最低位为读写标志位

... ...

stat = 0;

stat |= S3C2410_IICSTAT_TXRXEN;//设置标志位启动IIC收发使能

if (msg->flags & I2C_M_RD) { //判断是读,还是写

stat |= S3C2410_IICSTAT_MASTER_RX;

addr |= 1; //设置从IIC设备地址为读标志

} else

stat |= S3C2410_IICSTAT_MASTER_TX;

... ...

s3c24xx_i2c_enable_ack(i2c); //使能ACK信号

iiccon = readl(i2c->regs + S3C2410_IICCON); //读出IICCON寄存器

writel(stat, i2c->regs + S3C2410_IICSTA

[1] [2] [3] [4]
S3C2440Linux26I2C驱动程序

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
51单片机CO2检测显示程序解析