I2C驱动实现原理

I2C 驱动

💙

写在前面
  • i2c_client:连接到i2c硬件总线上的设备

  • i2c_adapter:每一个i2c控制器都有一个adapter和一个i2c_client之对应

  • 内核中的注册与注销:就是把i2c_adapter_info结构体从链表中删除和添加

  • struct i2c_client介绍

  • i2c_client->addr:从设备唯一的器件地址,一般是7位的地址最后一位为读写位,0写/1读

  • i2c_client->flag:决定设备的器件地址addr是7位的还是10位的

  • i2c_client->name:设备的名字,可以在/sys/bus/i2c/device/找到,或者在/dev/下找到

I2c设备模型:

user-speace(会暴露出来/dev/i2c-x) <------------app

				input   block net misc cdev

				----->probe----->|

			struct i2c_client        struct i2c_driver

    			  			struct bus_type
               					|
							i2c-core(i2c核心)
								|

					struct i2c_adapter   ---->   i2c_algorithm

						|   |   |						|
					devices(连接很多设备)	    master_xfer(是个函数指针能够对i2c控制器中的数据寄存器,进行读写的方法)
					
											通信方法一般芯片厂商已经实现好啦
											 			|
			---------------------------------------------------------------
			i2c控制器驱动:
				1、ack    2、波特率   3、中断   4、地址  5、读写
					|
			i2c控制器(数据寄存器)

-----E2PROM----MMA7660----FT5X06—GSL680----
i2c0控制器(寄存器数据)->i2c_adapter

i2c1控制器(寄存器数据)->i2c_adapter

i2c2控制器(寄存器数据)->i2c_adapter

提示:在硬件上有多少个i2c设备,在内核里面就有多少个i2c_adaper结构体。实际运用上,当设备被挂到i2c总线上了,要用i2c-client和i2c-driver。[假如]想看门狗这类在芯片内部的设备就可以用platform总线即可

i2c_adapter : i2c控制器 --> 为每一个i2c-client分配一个i2c_adapter

369 /*
370  * i2c_adapter is the structure used to identify a physical i2c bus     along
371  * with the access algorithms necessary to access it.
372  */
373 struct i2c_adapter {
374     struct module *owner;
375     unsigned int class;       /* classes to allow probing for */
376     const struct i2c_algorithm *algo; /* the algorithm to access the bus */
377     void *algo_data;
378 
379     /* data fields that are valid for all devices   */
380     struct rt_mutex bus_lock;
381 
382     int timeout;            /* in jiffies */
383     int retries;
384     struct device dev;      /* the adapter device */
385 
386     int nr;
387     char name[48];//i2c 控制器的名字
388     struct completion dev_released;
389 
390     struct mutex userspace_clients_lock;
391     struct list_head userspace_clients;
392 };
393 #define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
394   

i2c_algorithm:i2c用来传输信息的结构体 --> 里面有收发数据的函数指针master_xfer —>i2c_tansfer()

master_xfer:飞利浦提出的i2c总线(一般我们只用这个方式)
smbus_xfer:inter简化飞利浦后的总线

352 struct i2c_algorithm {
353     /* If an adapter algorithm can't do I2C-level access, set master_xfer
354        to NULL. If an adapter algorithm can do SMBus access, set
355        smbus_xfer. If set to NULL, the SMBus protocol is simulated
356        using common I2C messages */
357     /* master_xfer should return the number of messages successfully
358        processed, or a negative value on error */
359     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
360                int num);//这个是个函数指针 --->i2c_tansfer()
361     int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
362                unsigned short flags, char read_write,
363                u8 command, int size, union i2c_smbus_data *data);
364 
365     /* To determine what the adapter supports */
366     u32 (*functionality) (struct i2c_adapter *);
367 };

i2c_client 内核会为每一个i2c设备创建一个设备结构体,并指向一个adapter控制器
eg:一个EEPROM设备结构体会指向一个adapter控制器

220 struct i2c_client {
221     unsigned short flags;       /* 用来表示这个地址是7位的,还是10位的   */
222     unsigned short addr;        /* chip address - NOTE: 7bit    */
223                     /* addresses are stored in the  */
224                     /* _LOWER_ 7 bits       */
225     char name[I2C_NAME_SIZE]; /* i2c设备的名字/sys/bus/i2c/device/xxxxx   */
226     struct i2c_adapter *adapter;    /* the adapter we sit on  表示当前这个client属于哪一个adapter(控制器)  */
227     struct i2c_driver *driver;  /* and our access routines 表示当前这个client与哪一个driver 去 probe */
228     struct device dev;      /* the device structure     */
229     int irq;            /* irq issued by device i2c中断线产生中断     */
230     struct list_head detected;
231 };
其他I2c驱动实现方式:
242 //从dev中提取出来数据
    static inline void *i2c_get_clientdata(const struct i2c_client *dev    )
243 {
244     return dev_get_drvdata(&dev->dev);
245 }
246 
    //把data数据保存到dev中
247 static inline void i2c_set_clientdata(struct i2c_client *dev, void     *data)
248 {
249     dev_set_drvdata(&dev->dev, data);
250 }

当我们去写i2c驱动的时候,不会去创建i2c_client,而是去创建一个i2c_board_info, struct i2c_client是内核创建的

这个结构体一般在板级目录下,像我的就在eg: xxx/arch/arm/plat-s5p6818/x6818/device.c

 395 #if defined(CONFIG_TOUCHSCREEN_GSLX680)
 396 #include <linux/i2c.h>
 397 #define GSLX680_I2C_BUS     (1)
 398 
 399 static struct i2c_board_info __initdata gslX680_i2c_bdi = {
 400     .type   = "gslX680",
 401     .addr   = (0x40),
 402     .irq    = PB_PIO_IRQ(CFG_IO_TOUCH_PENDOWN_DETECT),
 403 };
 404 #endif
273 struct i2c_board_info {
274     char        type[I2C_NAME_SIZE]; //kernel会把这个数据存放到i2c_client中name里
275     unsigned short  flags;	//kernel会把这个数据存放到i2c_client中flags里
276     unsigned short  addr;	//kernel会把这个数据存放到i2c_client中addr里
277     void        *platform_data;	//保存你自己想存入的一些数据
278     struct dev_archdata *archdata;
279     struct device_node *of_node;
280     int     irq;//kernel会把这个数据存放到i2c_client中irq里
      /*irq中断线:这个设备连的是哪个GPIO,然后把这个GPIO编号转化为中断线 -->GPIO_TO_IRQ()/PB_PIO_IRQ()*/
281 };

当我们创建完i2c_board_info后,内核会自动为我们创建i2c_client.内核会把我们写到i2c_board_info中的信息填入到i2c_client,eg:name\flag\addr\irq,继续往下看就知道为什么会这样

 22 struct i2c_devinfo {
 23     struct list_head    list;//表示这个结构体是一个节点
 24     int         busnum;
 25     struct i2c_board_info   board_info;
 26 }; 
  • i2c_devinfo表示在链表中的一个节点,每一个i2c_devinfo结构体都包含一个i2c_board_info,当内核启动时会遍历这个链表,把所有i2c_devinfo拿出来,再把board_info拿出来,把board_info的信息传入到i2c_client中,然后所有的i2c_client都创建出来了

     							      node                           node                   node
    
     	i2c_board_list:链---------(i2c_devinfo)------------(i2c_devinfo)-----------(i2c_devinfo)----------表
    
       				             |                                    |                 |
    
     		               i2c_board_info          	        i2c_board_info        	i2c_board_info
    

i2c设备要在内核启动的时候就给注册了,要是写成模块是不好用的

/把i2c_board_info注册到i2c_devinfo的list中,把你写的i2c_board_info结构体,写到内核启动就注册/

注册i2c_board_info

*i2c_register_board_info(int busnum, struct i2c_board_info const info, unsigned int len)

#busnum:在第几个i2c总线上面,比如i2c总线0-1-2-3-4…

#info:把你要注册的i2c_board_info写进来,可以传进去一个数组进去

#len:这个代表数组元素的个数

 64 int __init
 65 i2c_register_board_info(int busnum,
 66     struct i2c_board_info const *info, unsigned len)
 67 {
 68     int status;
 69 
 70     down_write(&__i2c_board_lock); //共享资源,加读写锁
 71 
 72     /* dynamic bus numbers will be assigned after the last static one */
 73     if (busnum >= __i2c_first_dynamic_bus_num)
 74         __i2c_first_dynamic_bus_num = busnum + 1;
 75 //遍历这个数组,把每一个board_info提取出来,依次放入到i2c_board_list上的每一个devinfo节点上,如上图所示
 76     for (status = 0; len; len--, info++) { 
 77         struct i2c_devinfo  *devinfo;
 78 
 79         devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
 80         if (!devinfo) {
 81             pr_debug("i2c-core: can't register boardinfo!\n");
 82             status = -ENOMEM;
 83             break;
 84         }
 85 
 86         devinfo->busnum = busnum;
 87         devinfo->board_info = *info;
 88         list_add_tail(&devinfo->list, &__i2c_board_list);
 89     }
 90 
 91     up_write(&__i2c_board_lock);
 92 
 93     return status;
 94 }

多说一句:
添加adapter:内核启动会自动注册每一个adapter

 883 /**
 884  * i2c_add_adapter - declare i2c adapter, use dynamic bus number
 885  * @adapter: the adapter to add
 886  * Context: can sleep
 887  *
 888  * This routine is used to declare an I2C adapter when its bus number
 889  * doesn't matter.  Examples: for I2C adapters dynamically added by
 890  * USB links or PCI plugin cards.
 891  *
 892  * When this returns zero, a new bus number was allocated and stored
 893  * in adap->nr, and the specified adapter became available for clients.
 894  * Otherwise, a negative errno value is returned.
 895  */
 896 int i2c_add_adapter(struct i2c_adapter *adapter)
 897 {   
 898     int id, res = 0;
 899 
 900 retry: 
 901     if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
 902         return -ENOMEM;
 903     
 904     mutex_lock(&core_lock);
 905     /* "above" here means "above or equal to", sigh */
 906     res = idr_get_new_above(&i2c_adapter_idr, adapter,
 907                 __i2c_first_dynamic_bus_num, &id);
 908     mutex_unlock(&core_lock);
 909     
 910     if (res < 0) { 
 911         if (res == -EAGAIN)
 912             goto retry;
 913         return res;
 914     }
 915     
 916     adapter->nr = id; //i2c总线的编号
 917     return i2c_register_adapter(adapter);
 918 }
 919 EXPORT_SYMBOL(i2c_add_adapter);

总结在应用大致分为两步:

1、写一个i2c_board_info结构体 2、把它注册到内核启动中

–/实际运用:/–

写一个我这边的触摸屏驱动:gslx680触摸屏硬件

重点要知道怎么去移植,这个驱动只是部分开源。

/GSLX680首先要知道:/

1、硬件位与那个i2c总线上面

2、irq 因为我们需要去申请中断线

3、wake 用于休眠唤醒的

 399 static struct i2c_board_info __initdata gslX680_i2c_bdi = {
 400     .type   = "gslX680",
 401     .addr   = (0x40), //设备地址,手册或厂家会告知
 402     .irq    = PB_PIO_IRQ(CFG_IO_TOUCH_PENDOWN_DETECT), //GPIOB30
 403 };
/*注册i2c_board_info结构体*/
/*上面有对这个结构体做说明 参数1:位与第几个i2c总线  参数2:一个装有i2c_board_info的数组,参数3:数组里面一共有几个结构体*/
/*当然这就说明在同一个i2c总线上面可以连接多个i2c设备*/
i2c_register_board_info(GLSX680_I2C_BUS, &glsX680_i2c_bdi, 1);
/*****************************构造多个设备*****************************/
static struct i2c_board_info __initdata gslX680_i2c_bdi[] = {
	{
    .type   = "gslX680",
    .addr   = (0x40), //设备地址,手册或厂家会告知
    .irq    = PB_PIO_IRQ(CFG_IO_TOUCH_PENDOWN_DETECT), //GPIOB30
    },
    {
    .type   = "EEPROM",
    .addr   = (0x32), //设备地址,手册或厂家会告知
        //假如不能参生中断
    },
};
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值