linux下I2C驱动

来源:互联网 发布:sql over 编辑:程序博客网 时间:2024/05/17 01:22

默认情况下,I2C以从模式工作。接口在生成起始位后,会自动由从模式切换到主模式,并在仲裁丢失或生成停止位时从主模式切换为主模式。

在主模式下,I2C接口会启动数据传输并生成时钟信号。

任何能够进行发送和接收的设备都可以成为主设备。一个主控制器能控制信号的传输和时钟频率,当然在任何时间点上只能有一个主控制器。

空闲状态:I2C总线的SDA和SCL两条信号线同时处于高电平。

进行数据传输时,在SCL呈现高电平期间,SDA上电平必须保持稳定,低电平为数据0,高电平为数据1.

I2C总线上所有数据都是以8字节传送的,发送器每发送一个字节,就在时钟脉冲期间释放数据线,由接收器反馈一个应答信号。

I2C核心:I2C提供了I2C总线驱动和设备驱动的注册和注销方法,I2C通信方法(algorithm) 上层的,与具体适配器无关的代码以及探测设备,检测设备地址的上层代码

I2C总线:I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,其可直接集成在CPU内部。

一个I2C适配器需要i2c_algorithm中提供的通信函数来控制适配器上产生的特定的访问周期。

master_xfer();用于产生i2c访问周期需要的start,stop,ack信号.

i2c_scan_static_borad_info()为整个I2C子系统的核心,会去遍历一个由I2C设备组成的双向循环链表,并完成所有I2C从设备的i2c_client的注册。

i2c从设备主要完成三大任务

系统初始化时添加i2c_board_info为结构的i2c从设备的信息

在i2c从设备驱动里使用i2c_adapter提供的算法实现i2c通信

将i2c从设备特有数据结构挂载到i2c_client->driver_data


i2c_adapter对应i2c总线控制器

 /*  * i2c_adapter is the structure used to identify a physical i2c bus along  * with the access algorithms necessary to access it.  */ struct i2c_adapter {         struct module *owner;         unsigned int class;               /* classes to allow probing for */         const struct i2c_algorithm *algo; /* the algorithm to access the bus */         void *algo_data;          /* data fields that are valid for all devices   */         struct rt_mutex bus_lock;          int timeout;                    /* in jiffies */         int retries;         struct device dev;              /* the adapter device */          int nr;         char name[48];         struct completion dev_released;          struct mutex userspace_clients_lock;         struct list_head userspace_clients; };
i2c_client对应真实的i2c物理设备 

 /**  * struct i2c_client - represent an I2C slave device  * @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;  *      I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking  * @addr: Address used on the I2C bus connected to the parent adapter.  * @name: Indicates the type of the device, usually a chip name that's  *      generic enough to hide second-sourcing and compatible revisions.  * @adapter: manages the bus segment hosting this I2C device  * @driver: device's driver, hence pointer to access routines  * @dev: Driver model device node for the slave.  * @irq: indicates the IRQ generated by this device (if any)  * @detected: member of an i2c_driver.clients list or i2c-core's  *      userspace_devices list  *  * An i2c_client identifies a single device (i.e. chip) connected to an  * i2c bus. The behaviour exposed to Linux is defined by the driver  * managing the device.  */ struct i2c_client {         unsigned short flags;           /* div., see below              */         unsigned short addr;            /* chip address - NOTE: 7bit    */                                         /* addresses are stored in the  */                                         /* _LOWER_ 7 bits               */         char name[I2C_NAME_SIZE];         struct i2c_adapter *adapter;    /* the adapter we sit on        */         struct i2c_driver *driver;      /* and our access routines      */         struct device dev;              /* the device structure         */         int irq;                        /* irq issued by device         */         struct list_head detected; };

i2c_driver和i2c_client发生关联的时刻在i2c_driver的i2c_attach_adapter()函数运行时。

当一个具体的client被侦测到并被关联的时候,设备和sysfs文件将被注册。

说起Linux下的子系统,I2C子系统已经算是比较简单的框架了。

  首先相关代码在driver/i2c下

.built-in.o.cmd       .i2c-dev.o.cmd        algos/                i2c-boardinfo.c       i2c-core.h            i2c-dev.o     muxes/.i2c-boardinfo.o.cmd  Kconfig               built-in.o            i2c-boardinfo.o       i2c-core.o            i2c-mux.c           .i2c-core.o.cmd       Makefile              busses/               i2c-core.c            i2c-dev.c             i2c-smbus.c    

i2c-dev是用户态直接相关联的,里面有相关文件(linux中设备即文件)操作的函数,最后依然会调用到i2c-core(是硬件层与系统调用层的中间缓冲层)的函数(i2c_master_send,i2c_master_recv最终还是调用i2c_transfer),i2c默认的主设备号为89.

 static int __init i2c_dev_init(void) {         int res;          printk(KERN_INFO "i2c /dev entries driver\n");          res = register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);         if (res)                 goto out;         i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");         if (IS_ERR(i2c_dev_class)) {                 res = PTR_ERR(i2c_dev_class);                 goto out_unreg_chrdev;         }          /* Keep track of adapters which will be added or removed later */         res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);         if (res)                 goto out_unreg_class;          /* Bind to already existing adapters right away */         i2c_for_each_dev(NULL, i2cdev_attach_adapter);          return 0;  out_unreg_class:         class_destroy(i2c_dev_class); out_unreg_chrdev:         unregister_chrdev(I2C_MAJOR, "i2c"); out:         printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);         return res; }
在i2c-core中最后还是调用我们在busses/下写的i2c控制器模块中的传输函数(回调) ,在xxx_i2c_probe中,初始化一些硬件环境(中断,内存空间,时钟),i2c控制器抽象成i2c_adapter,最后调用i2c_add_numbered_adapter().注册到i2c子系统中。在该函数中调用i2c_register_adapter(),最终还是调用device_register().创建设备节点。其中之前一直很让我困惑的是节点设备的名字,原来在子系统中已经将设备节点的名字封装好,不需要我们在去命名,注意这里的节点设备名字和platform_device的名字是可以不相同的。

如果register成功就调用另一个核心的函数:

          /* create pre-declared device nodes */          if (adap->nr < __i2c_first_dynamic_bus_num)                  i2c_scan_static_board_info(adap);

扫描之前静态注册的I2C控制器设备,代码在(arch/xxx_cpu/xxx_sys/i2c_board_info.c)中:

关键函数在:

  static int __init xxx_i2c_board_init(void)  {          i2c_register_board_info(0,xxx_i2c_1_board_info,ARRAY_SIZE(xxx_i2c_1_board_info));          i2c_register_board_info(1,xxx_i2c_2_board_info,ARRAY_SIZE(xxx_i2c_2_board_info));          i2c_register_board_info(2,xxx_i2c_3_board_info,ARRAY_SIZE(xxx_i2c_3_board_info));          i2c_register_board_info(3,xxx_i2c_4_board_info,ARRAY_SIZE(xxx_i2c_4_board_info));          return 0;  }
如果注册总线号和i2c控制器的number相同则又要创建一个注册新的i2c设备i2c_new_device()//??也不知道为什么


最关键的是是i2c通信过程。主要在xxx_i2c_probe()中dev->adap->algo = &i2c_xxx_algo;

 static struct i2c_algorithm i2c_xxx_algo = {         .master_xfer    = i2c_xxx_xfer,         .functionality  = i2c_xxx_func, };
最终i2c-core中的i2c_transfer()会调用i2c_xxx_xfer();里面主要对I2C控制器芯片的寄存器操作。

信息的内容被封装成i2c_message;

 struct i2c_msg {         __u16 addr;     /* slave address                        */         __u16 flags;             #define I2C_M_TEN               0x0010  /* this is a ten bit chip address */ #define I2C_M_RD                0x0001  /* read data, from slave to master */ #define I2C_M_NOSTART           0x4000  /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_REV_DIR_ADDR      0x2000  /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK        0x1000  /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NO_RD_ACK         0x0800  /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_RECV_LEN          0x0400  /* length will be first received byte */         __u16 len;              /* msg length                           */         __u8 *buf;              /* pointer to msg data                  */ };
接下来就是I2C测试程序的编写了,我们使用的是eeprom,因为是支持i2c接口,所以向i2c从设备地址写入若干数值,再从该地址读取数值,如果写入和读出是相同的,那么就代表i2c驱动是对的。


编写具体的i2c驱动

提供i2c适配器的硬件驱动,探测,初始化i2c适配器(申请i2c,I/O地址和中断号),驱动cpu控制的i2c适配器从硬件上产生。

提供i2c控制的algorithm(xxx_tranfer)

实现i2c设备驱动中的i2c_driver接口,只是实现设备与总线的连接

实现i2c设备对应类型的具体驱动

0 0