I2C驱动框架(四)

参考:I2C子系统之platform_driver初始化——I2C_adap_s3c_init()

在完成platform_device的添加之后,i2c子系统将进行platform_driver的注册过程。platform_driver的注册通过调用初始化函数i2c_adapter_s3c_init函数来完成。

static struct platform_device_id s3c24xx_driver_ids[] = {
    {
        .name        = "s3c2410-i2c",
        .driver_data    = TYPE_S3C2410,
    }, {
        .name        = "s3c2440-i2c",
        .driver_data    = TYPE_S3C2440,
    }, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
static struct platform_driver s3c24xx_i2c_driver = {
    .probe        = s3c24xx_i2c_probe,
    .remove        = s3c24xx_i2c_remove,
    .id_table    = s3c24xx_driver_ids,
    .driver        = {
        .owner    = THIS_MODULE,
        .name    = "s3c-i2c",
        .pm    = S3C24XX_DEV_PM_OPS,
    },
};

static int __init i2c_adap_s3c_init(void)
{
    return platform_driver_register(&s3c24xx_i2c_driver);
}
 subsys_initcall(i2c_adap_s3c_init);

这里先说一下MODULE_DEVICE_TABLE宏的作用:

MODULE_DEVICE_TABLE(platform, xx_driver_ids);

MODULE_DEVICE_TABLE一般用在热插拔的设备驱动中。
上述xx_driver_ids结构,是此驱动所支持的设备列表。
作用是:将xx_driver_ids结构输出到用户空间,这样模块加载系统在加载模块时,就知道了什么模块对应什么硬件设备。
用法是:MODULE_DEVICE_TABLE(设备类型,设备表),其中,设备类型,包括USB,PCI等,也可以自己起名字,
上述代码中是针对不同的平台分的类;设备表也是自己定义的,它的最后一项必须是空,用来标识结束。

平台驱动匹配到平台设备后,调用s3c24xx_i2c_driver.probe函数,即static int s3c24xx_i2c_probe(struct platform_device *pdev)。

static int s3c24xx_i2c_probe(struct platform_device *pdev)
    -->struct s3c24xx_i2c *i2c;//分配s3c24xx_i2c结构体并初始化
    -->ret = s3c24xx_i2c_init(i2c);//S3C6410 I2C控制器初始化
    -->ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
              dev_name(&pdev->dev), i2c);//设置中断
    -->ret = i2c_add_numbered_adapter(&i2c->adap);

分配一个s3c24xx_i2c结构体,并根据平台设备的内容填充该结构体,如下图

下面具体分析i2c_add_numbered_adapter()函数,该函数根据传入的i2c_adapter结构体的内容分别在i2c_bus_type总线上注册adapter设备和client设备。

该函数执行后在/sys/bus/i2c/devices目录下生成如下图示文件

i2c_add_numbered_adapter(&i2c->adap);
    -->i2c_register_adapter(adap);
        -->dev_set_name(&adap->dev, "i2c-%d", adap->nr);
        -->adap->dev.bus = &i2c_bus_type;
        -->adap->dev.type = &i2c_adapter_type;
        -->res = device_register(&adap->dev);//注释1,注册i2c总线 adapter设备
        -->i2c_scan_static_board_info(adap);
            -->list_for_each_entry(devinfo, &__i2c_board_list, list)
                -->i2c_new_device(adapter,&devinfo->board_info))
                    -->struct i2c_client    *client;//分配i2c_client结构体并初始化
                    -->client->adapter = adap;
                    -->client->dev.parent = &client->adapter->dev;
                    -->client->dev.bus = &i2c_bus_type;
                    -->client->dev.type = &i2c_client_type;
                    -->client->dev.of_node = info->of_node;
                    -->strlcpy(client->name, info->type, sizeof(client->name));
                    -->dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr);
                    -->status = device_register(&client->dev); //注释2,注册i2c总线 client设备
        -->bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);

list_for_each_entry(devinfo, &__i2c_board_list, list)遍历I2C驱动框架(三)中最后图示的链表,以adapter结构体和找到的devinfo结构体中的i2c_board_info结构体为参数在i2c_bus_type总线上添加client设备。

最后还调用bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)来扫描i2c总线,检测刚注册到i2c总线的adapter是否有对应的驱动。不过目前肯定失败,因为到目前为止还未注册adapter driver。

bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter)与I2C驱动框架(二)中分析的

bus_for_each_dev(&i2c_bus_type, NULL, &dummy_driver, __process_new_driver)相似。

bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);//遍历i2c_bus_type总线上的驱动
    //以找到的驱动和i2c_adapter结构体为参数调用__process_new_driver函数
    -->__process_new_adapter(struct device_driver *d, void *data)
        -->i2c_do_add_adapter(to_i2c_driver(d), data);
            -->i2c_detect(adap, driver);
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver)
    -->int adap_id = i2c_adapter_id(adapter);//获取adapter的序列号,即处理器的第几个I2C控制器
    -->const unsigned short *address_list = driver->address_list; //获取I2C从设备的地址数组
    -->if (!(adapter->class & driver->class)) return 0; //类型匹配后才继续执行
    -->struct i2c_client *temp_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);//分配i2c_client结构体
    -->temp_client->adapter = adapter;
    -->for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) //遍历address_list里的i2c地址
        -->temp_client->addr = address_list[i];//设置从地址到i2c_client结构体
        -->i2c_detect_address(temp_client, driver);//检测该从地址对应的设备是否存在

static int i2c_detect_address(struct i2c_client *temp_client,struct i2c_driver *driver)
    -->int addr = temp_client->addr;
    -->i2c_check_addr_validity(addr);//检测地址有效性
    -->i2c_check_addr_busy(adapter, addr);//检测设备是否正在使用,同一条物理I2Cbus上不能有两个相同address的器件
    -->i2c_default_probe(adapter, addr);//检测i2c物理总线上是否有设备应答
    -->struct i2c_board_info info.addr=temp_client->addr
    -->driver->detect(temp_client, &info);//调用i2c_driver的detect函数检测设备,并经info.type赋值
    -->struct i2c_client *client = i2c_new_device(adapter, &info);//在i2b_bus_type总线上创建i2c_client设备
    -->list_add_tail(&client->detected, &driver->clients);//创建设备成功则将该i2c_client挂到i2c_driver的链表上
时间: 2024-08-08 13:57:21

I2C驱动框架(四)的相关文章

20150503 imx257下实现I2C驱动的四种方法

20150503 imx257下实现I2C驱动的四种方法 2015-05-3 Lover雪儿 时间过得好快,转眼间五一假期就即将结束了,假期期间,大家都潇洒的去玩了,徒留辛辛苦苦的程序员还是窝在宿舍乖乖的敲着代码... 好啦,开开玩笑,辛酸史每家都有一大本,还是要积极的面对生活!!! 今天我们的任务是简单的入门linux内核下i2c设备驱动分离的四种写法. 一.一个简单的i2c驱动 和以前的驱动程序不同,i2c驱动分为drv驱动和dev设备驱动两个文件,不懂的可以参考我以前写的<20150313

I2C驱动框架之probe方式

基于Linux的I2C驱动,采用probe方式.按照如下这个框架可以写出任何支持I2C总线的器件的Linux驱动. I2C器件连接至cpu的特定的i2c的接口,也就是挂载在cpu的i2c适配器上,i2c器件要和cpu进行信息交换必须要通过cpu操作适配器来交互.cpu上有1个或多个适配器,每个适配器上可以挂载256个设备地址不一样的i2c器件,通过i2c驱动就可以让cpu和适配器上的多个不一样的i2c器件通信而不会产生冲突. 驱动包括两个文件,dev.c和drive.c,其中dev.c是构建I2

I2C驱动框架

以用i2c通信的实时时钟为例 框架入口源文件:i2c_m41t11.c (可根据入口源文件,再按着框架到内核走一遍) 内核版本:linux_2.6.22.6   硬件平台:JZ2440 以下是驱动框架: 以下是驱动代码 i2c_m41t11.c : #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #inclu

I2C驱动框架(二)

在linux内核启动的时候最先执行的和I2C子系统相关的函数应该是driver/i2c/i2c-core.c文件中的i2c_init()函数. 1 static int __init i2c_init(void) 2 { 5 retval = bus_register(&i2c_bus_type); 9 i2c_adapter_compat_class = class_compat_register("i2c-adapter"); 15 retval = i2c_add_dri

I2C驱动详解

I2C讲解: 在JZ2440开发板上,I2C是由两条数据线构成的SCL,SDA:SCL作为时钟总线,SDA作为数据总线:两条线上可挂载I2C设备,如:AT24C08 两条线连接ARM9 I2C控制器,通过控制来控制I2C设备的识别设备地址.读.写操作:如图所示 从中所知:I2C线上可以挂载很多个I2C设备:挂载简单,只需要一根数据线和一根时钟线就可以挂载上去,通过地址来去别每个设备的区别: I2C操作: 对I2C操作主要思想为:1.找到设备  2.进行读写操作 主要原理为: 1.发送开始信号S

spi驱动框架全面分析,从master驱动到设备驱动

内核版本:linux2.6.32.2 硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括: 1.I2C 驱动框架回顾 2.SPI 框架简单介绍 3.master 驱动框架 3.1 驱动侧 3.2 设备侧 4.SPI 设备驱动框架 4.1 设备册 4.2 驱动侧 5.设备驱动程序实例 1.I2C 驱动框架回顾 在前面学习 I2C 驱动程序的时候我们知道,I2C 驱动框架分为两层,一层是控制器驱动程序 i2c_adapter,它一般是由芯片厂商写好的,主要提供一个 algorithm

PCI驱动框架简单分析

一.PCI 概念介绍 PCI是CPU和外围设备通信的高速传输总线.PCI规范能够实现32位并行数据传输,工作频率为 33MHz 或 66MHz ,最大吞吐率高达266MB/s,PCI的衍生物包括 CardBus.mini-PCI.PCI-Express.cPCI等. PCI总线体系结构是一种层次式的体系结构.在这种层次体系结构中,PCI桥设备占据着重要的地位,它将父总线与子总线连接在一起,从而使整个系统看起来像一个倒置的树状结构,树的顶端是CPU,它通过一个较为特殊的CPI桥设备-Host/PC

TQ2440学习笔记——Linux上I2C驱动的两种实现方法(1)

作者:彭东林 邮箱:[email protected] 内核版本:Linux-3.14 u-boot版本:U-Boot 2015.04 硬件:TQ2440 (NorFlash:2M   NandFlash:256M  内存:64M) 摘要 这里并不深入分析Linux下I2C驱动的实现,只是以TQ2440硬件平台为例分析I2C驱动的两种方法. 第一种方法: 使用S3C2440自带的I2C控制器实现,这个kernel已经支持,我们只需要配置即可. 第二种方法: 使用GPIO模拟,这个在kernel中

3.4.2内核下的I2C驱动

1. 框架1.1 硬件协议简介1.2 驱动框架1.3 bus-drv-dev模型及写程序a. 设备的4种构建方法a.1 定义一个i2c_board_info, 里面有:名字, 设备地址 然后i2c_register_board_info(busnum, ...) (把它们放入__i2c_board_list链表) list_add_tail(&devinfo->list, &__i2c_board_list); 链表何时使用: i2c_register_adapter > i2