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

https://www.cnblogs.com/lifexy/p/7816324.html

上一节 我们学习了:

IIC接口下的AT24C02驱动分析:https://blog.youkuaiyun.com/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的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值