当前位置:   article > 正文

Linux i2c 设备树或者平台总线配置和Linux i2c 调用原理_linux iic 设备如何添加到总线

linux iic 设备如何添加到总线

1设备树配置

代码编写过程
1.在对应的.dts中的i2c中写入设备数据即i2c(硬件模块)设备名和i2c设备(硬件)地址
2.在自己的驱动代码中填入i2c设备信息名字即可
3.在自己的驱动代码中添加i2c_add_driver代码即可,再通过驱动代码的proe添加设备号来实现文件操作符初始化
4.在字符驱动read,write或者ioctl中填入i2c_transfer(client->adapter, &msg, num);来读写信息
  注意:msg.flags = 1;为读出信息 msg.flags = 0;为写入信息
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
1-1.在对应的.dts中的i2c中写入设备数据

        &i2c1 {                                                         <--i2c0或者i2c1或者i2c2要自己找有没有这个设备
            clock-frequency = <100000>;                                 <--100000Hz 为 100kHz
            pinctrl-names = "default";
            pinctrl-0 = <&pinctrl_i2c1>;
            status = "okay";                                            <--okay代表启动i2c
                                                                           否则disable为关闭

            ...

            ap3216c@1e {                                                <--自己填 设备名@设备硬件地址
                compatible = "ap3216c";                                 <--设备名   
                reg = <0x1e>;                                           <--设备硬件地址
            };
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
1-2.在自己的驱动代码中填入:

        /* 设备树匹配列表 */
        static const struct of_device_id ap3216c_of_match[] = {     <-- 自己创建
            { .compatible = "alientek,ap3216c" },                       <--在这里填入
            { /* Sentinel */ }
        };

        /* i2c驱动结构体 */	
        static struct i2c_driver ap3216c_driver = {                 <-- 自己创建
            .probe = ap3216c_probe,
            .remove = ap3216c_remove,
            .driver = {
                    .owner = THIS_MODULE,
                    .name = "ap3216c",
                    .of_match_table = ap3216c_of_match,             <--在这里填入
                },
            .id_table = ap3216c_id,
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

2平台总线i2c配置

代码编写过程
1.在平台总线系统上,写入i2c设备信息名字和硬件地址
2.在自己的驱动代码中填入i2c设备信息名字即可
3.在自己的驱动代码中添加i2c_add_driver代码即可,再通过驱动代码的proe添加设备号来实现文件操作符初始化
4.在字符驱动read,write或者ioctl中填入i2c_transfer(client->adapter, &msg, num);来读写信息
  注意:msg.flags = 1;为读出信息 msg.flags = 0;为写入信息
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
2-1.在平台总线系统上,i2c的匹配:
    开发者在 mach-s5pv210 或者 mach-xxxx 上写入对应的信息在 
    i2c_board_info 结构体中的 smdkv210_i2c_devs‘x’ (x=0,1,..,n表示第几个i2c) 中
  • 1
  • 2
例:
    static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
        { I2C_BOARD_INFO("at24co2a", 0x50), },      /* Samsung S524AD0XD1 */
        { I2C_BOARD_INFO("wm8580", 0x1b), },
    };

    还有
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2-2.在自己的驱动代码中填入
        const struct i2c_device_id at24_table[] = {
            {"at24co2a",0x1234},
                 |          |
 必须和平台总线上信息一致   随便填写
            {"at24co4a",0x1134},
            {"at24co8a",0x1244},
        };
            
        struct i2c_driver at24_driver = {
            .probe = at24_i2c_drv_probe,
            .remove = at24_i2c_drv_remove,
            .driver = {
                .name = "s5pv210_e2prom",
            },
            .id_table = at24_table,  --> const struct i2c_device_id at24_table[]
        };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
2-3.通过i2c_add_driver注册i2c信息
    而不是通过平台总线初始化代码platform_driver_register(&at24_driver)

    1.因为已经通过arch/arm/mach-s5pv210/mach-smdkv210.c,
    文件中的static void __init smdkv210_machine_init(void)代码块里的,
        1.i2c_register_board_info(0, smdkv210_i2c_devs0,ARRAY_SIZE(smdkv210_i2c_devs0));
            注册里i2c的信息到i2c的i2c-core中,产生数据链表。
        2.platform_add_devices中的代码块,里的platform_device_register(devs[i])注册到了平台总线。
    2.产生:i2c文件中数据链表和i2c的总线信息
    3.现在只需要通过i2c_add_driver在i2c里添加设备的驱动信息即刻,注册i2c的适配器来供设备驱动使用适配器,
    4.不然只有平台总线的i2c设备信息(/sys/bus/i2c/devices/中),而没有i2c适配器的功能。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
    例如:
    static int __init at24_i2c_drv_init(void)
    {
        return i2c_add_driver(&at24_driver);
        (执行过程中会在/sys/bus/platform/drivers下创建目录)
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

3 i2c设备信息设置原理

3-1. 设备信息配置
3-1-1. 添加设备信息在例如: Mach-smdkv210.c(s5pv210芯片)
  代码:
    static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {     <--i2c0
                { I2C_BOARD_INFO("24c08", 0x50),},        /* Samsung S524AD0XD1 */
                { I2C_BOARD_INFO("wm8580", 0x1b),},: { I2C_BOARD_INFO("at24c02a", 0x50),}
                                    ^         ^
                                    |         |
                                设备名字   设备地址(硬件)  
                注意:在平台总线中驱动代码的设备名称必须和当前文件的设备名称(id_table里的,而不是i2c_driver里的name)一致
                     否则匹配不成功!                 
            };

    static struct i2c_board_info smdkv210_i2c_devs1[] __initdata = {	<--i2c1
                /* To Be Updated */
            };

    static struct i2c_board_info smdkv210_i2c_devs1[] __initdata = {	<--i2c2
                /* To Be Updated */
            };
																			 ^
																			 |:这里有3个i2c:i2c0,i2c1,i2c2

    static void __init smdkv210_machine_init(void)
    {
        ......

        s3c_i2c1_set_platdata(NULL);
        s3c_i2c2_set_platdata(NULL);
        i2c_register_board_info(0, smdkv210_i2c_devs0, ARRAY_SIZE(smdkv210_i2c_devs0));
                                |           |                           |
                            i2c适配器0//将上面配置好的smdkv210_i2c_devs0传入到i2c适配器0号
                
            |
           int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info, unsigned len)
            {                                                                                          |
                int status;                                            i2c0的多少个设备:ARRAY_SIZ(smdkv210_i2c_devs0) 

                down_write(&__i2c_board_lock);
                    ....

                for (status = 0; len; len--, info++) {
                    struct i2c_devinfo	*devinfo;  //devinfo --i2c的设备信息动态空间
                    devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
                    if (!devinfo) {
                        pr_debug("i2c-core: can't register boardinfo!\n");
                        status = -ENOMEM;
                        break;
                    }
                    devinfo->busnum = busnum;      全部i2c的板载所有设备信息
                    devinfo->board_info = *info;           |
                    list_add_tail(&devinfo->list, &__i2c_board_list);
                } 
                ...
            }

        i2c_register_board_info(1, smdkv210_i2c_devs1,
                ARRAY_SIZE(smdkv210_i2c_devs1));
        i2c_register_board_info(2, smdkv210_i2c_devs2,
                ARRAY_SIZE(smdkv210_i2c_devs2));
        ......
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
3-1-2. 设置好Linux内核i2c配置并且编译内核
farsight@ubuntu:~/2012/linux/linux-3.0.8$ make -j2 zImage
farsight@ubuntu:~/2012/linux/linux-3.0.8$ cp -raf arch/arm/boot/zImage /tftpboot/
  • 1
  • 2
3-2. i2c适配器配对详解
3-2-1. i2c-core层:------ i2c核心层:linux-3.0.8/drivers/i2c/i2c-core.c

postcore_initcall(i2c_init);    入口函数
        |
    static int __init i2c_init (void)
            |
        {
            retval = bus_register(&i2c_bus_type);   注册一个总线
                                        |
                                    struct bus_type i2c_bus_type = {
                                        .name		= "i2c",
                                        .match		= i2c_device_match,
                                        .probe		= i2c_device_probe,
                                        .remove		= i2c_device_remove,
                                        .shutdown	= i2c_device_shutdown,
                                    };

            retval = i2c_add_driver(&dummy_driver);  i2c系统不做任何处理   
                                        |
                                    static struct i2c_driver dummy_driver = {
                                        .driver.name	= "dummy",
                                        .probe		= dummy_probe,
                                        .remove		= dummy_remove,
                                        .id_table	= dummy_id,
                                    };       
                            |
                        i2c_register_driver(THIS_MODULE, driver)
                                |
                            driver_register
                                    
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

3-2-2 i2c_adapter层:------- linux-3.0.8/drivers/i2c/busses/i2c-s3c2410.c
    subsys_initcall(i2c_adap_s3c_init); 适配器层入口函数
        |
    static int __init i2c_adap_s3c_init(void)
            |
        platform_driver_register        
                |
            s3c24xx_i2c_probe    
            { 
                pdata = pdev->dev.platform_data;
				//给struct s3c24xx_i2c分配空间,s3c24xx_i2c里面有struct i2c_adapter,所以struct i2c_adapter对象也被分配了空间
				i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
				//初始化struct i2c_adapter
				i2c->adap.owner   = THIS_MODULE;
				i2c->adap.algo    = &s3c24xx_i2c_algorithm;
				i2c->adap.retries = 2;
				i2c->adap.class   = I2C_CLASS_HWMON | I2C_CLASS_SPD;
				//打开i2c的时钟
				i2c->clk = clk_get(&pdev->dev, "i2c");
				clk_enable(i2c->clk);
				res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
				i2c->ioarea = request_mem_region(res->start, resource_size(res),pdev->name);
				i2c->regs = ioremap(res->start, resource_size(res));
				//初始化i2c控制器
				ret = s3c24xx_i2c_init(i2c);
				i2c->irq = ret = platform_get_irq(pdev, 0);
				ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED, dev_name(&pdev->dev), i2c);
				i2c->adap.nr = pdata->bus_num;
                ret = \
                i2c_add_numbered_adapter(&i2c->adap);
            }
                i2c_add_numbered_adapter        
                        |
                    i2c_add_adapter        
                                |
                        ---------------------------------------------------------
                        |    __i2c_add_numbered_adapter                         |
                        |                |                                      |
                        |        i2c_register_adapter                           |
                        |                    |                                  |
                        |            i2c_scan_static_board_info                 |
                        |                        |                              |
                        |                list_for_each_entry                    |
                        |                    |        |                         |
                        |                    |        __i2c_board_list          |
                        |                    |                                  |
                        ---------------------------------------------------------
                                             |
            if (devinfo->busnum == adapter->nr && !i2c_new_device(adapter, &devinfo->board_info))
                                             |
                                        //创建struct i2c_client ,即用户调用适配器
                                        client = kzalloc(sizeof *client, GFP_KERNEL);
                                        client->adapter = adap;
                                        client->dev.platform_data = info->platform_data;
                                        client->flags = info->flags;
                                        client->addr = info->addr;
                                        client->irq = info->irq;
                                        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;
                                        dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),client->addr);
                                        status = device_register(&client->dev);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

3-3 芯片硬件信息匹配层调用: ------- linux-3.0.8\linux-3.0.8\arch\arm\mach-s5pv210
static void __init smdkv210_machine_init(void)
        |
        { 
            i2c_register_board_info(0, smdkv210_i2c_devs0, ARRAY_SIZE(smdkv210_i2c_devs0)); 
                                                                                |
                                            static struct i2c_board_info smdkv210_i2c_devs0[] __initdata = {
											 { I2C_BOARD_INFO("at24co2a", 0x50), },      /* Samsung S524AD0XD1 */
                                             { I2C_BOARD_INFO("wm8580", 0x1b), },
                                          };				^
                                          					|
                                        系统会这里自动导入自己写的i2c设备的信息到i2c板载链表上
                    |
            { 
                devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
                devinfo->busnum = busnum;
                devinfo->board_info = *info;
                list_add_tail(&devinfo->list, &__i2c_board_list);
                									^
                									|
                						系统导入i2c设备信息到链表中
            }
            ...
            platform_add_devices(smdkv210_devices, ARRAY_SIZE(smdkv210_devices));
                                        |                          |
                static struct platform_device *smdkv210_devices[] __initdata = {
                                                    ...
                                                    &s3c_device_i2c0,
                                                            |
                                                    struct platform_device s3c_device_i2c0 = {
                                                    .name		  = "s3c2410-i2c",
                                                    #ifdef CONFIG_S3C_DEV_I2C1
                                                    .id		  = 0,
                                                    #else
                                                    .id		  = -1,
                                                    #endif
                                                    .num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
                                                                                        |
                                                                static struct resource s3c_i2c_resource[] = {
                                                                    [0] = {
                                                                        .start = S3C_PA_IIC,
                                                                        .end   = S3C_PA_IIC + SZ_4K - 1,
                                                                        .flags = IORESOURCE_MEM,
                                                                    },
                                                                    [1] = {
                                                                        .start = IRQ_IIC,
                                                                        .end   = IRQ_IIC,
                                                                        .flags = IORESOURCE_IRQ,
		                                                                        ^
		                                                                        |
		                                                          系统导入对应的i2c的寄存器信息
                                                                    },
                                                                };                
                                                    .resource	  = s3c_i2c_resource,
                                                    };
                                                    &s3c_device_i2c1,
                                                    &s3c_device_i2c2,
                                                    ...
                                                };
                
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
4 平台总线设备显示
  ls /sys/bus/i2c/devices/
                    |
       0-001b(i2c0:从机设备地址1b) 0-0050 (0:i2c0 - 0050(设备硬件地址)):1-0058(i2c1:从机设备地址58)
       2-0058(i2c2:从机设备地址58)
  ls /sys/bus/i2c/devices/0-0050
                            |
                          name
                            |
                        "at24c02a"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/586214
推荐阅读
相关标签
  

闽ICP备14008679号