linux下i2c体系结构分为三部分:
1. i2c core 层:提供设备驱动,总线驱动的接口API,i2c 通讯方法,探测设备。
2. i2c 总线驱动:实现i2c硬件体系结构中适配器(解决通讯中怎么发数据)。
3. i2c 设备驱动: 寻找i2c适配器与CPU交换数据(解决通讯中向谁发数据与发什么数据)。
框架图:
在linux源码 driver/i2c目录下:
i2c-core.c 实现i2c 核心层
i2c-dev.c 实现适配器设备功能
buses文件 实现主机控制器设备驱动
algos文件夹 实现总线适配器通讯方法。
## 几个重要的数据结构 ##
//i2c_adapter对应一个物理上的适配器
struct i2c_adapter {
struct module *owner;
unsigned int id;
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 list_head userspace_clients;
};
//对应一套i2c总线通讯方法
struct i2c_algorithm {
/* If an adapter algorithm can't do I2C-level access, set master_xfer
to NULL. If an adapter algorithm can do SMBus access, set
smbus_xfer. If set to NULL, the SMBus protocol is simulated
using common I2C messages */
/* master_xfer should return the number of messages successfully
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
//一个i2c通讯周期的信号
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
u32 (*functionality) (struct i2c_adapter *);
};
//对应一套驱动方法
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this if you can, it will probably
* be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
const struct i2c_device_id *id_table; //驱动支持i2c设备的id表
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
//对应一个真实的i2c物理设备
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消息结构体
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总线驱动是一个单独的驱动,使用platform总线,最终目的完成注册一个adapter
platform_device:
//plat-s2410.c
struct platform_device s3c_device_i2c2 = {
.name = "s3c2410-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource, //i2c寄存器与i2c中断号
};
//传给plat_driver的私有数据
static struct s3c2410_platform_i2c default_i2c_data2 __initdata = {
.flags = 0,
.bus_num = 2, //对应i2c-2
.slave_addr = 0x10,
.frequency = 100*1000,
.sda_delay = 100,
};
platform_driver:
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,
},
};
//match-id
static struct platform_device_id s3c24xx_driver_ids[] = { //id_table
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids);
probe函数初始化适配器硬件,时钟,中断等资源,最终注册一个适配器。
static int s3c24xx_i2c_probe(struct platform_device *pdev)
pdata = pdev->dev.platform_data; //获取私有数据
i2c->adap.algo = &s3c24xx_i2c_algorithm; //绑定信号传输函数
init_waitqueue_head(&i2c->wait); //申请等待队列
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); //获取i2c控制寄存器
i2c->regs = ioremap(res->start, resource_size(res));
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; //设置适配器的bus_name
ret = i2c_add_numbered_adapter(&i2c->adap);//add_adapter到总线,注册adapter
status = i2c_register_adapter(adap);
i2c_scan_static_board_info(adap); //扫描总线,适配产生i2c_client
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,&devinfo->board_info))
//信号传输函数
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
//发送iic信号帧
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
ret = s3c24xx_i2c_set_master(i2c);
i2c->msg = msgs;
i2c->msg_num = num;
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs); //发送start信号
unsigned int addr = (msg->addr & 0x7f) << 1; //注意msg的iic地址
timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); //等待中断完成
//中断到来且i2c数据帧传输完成,就是唤醒i2c-wait的时候
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
i2s_s3c_irq_nextbyte(i2c, status); //继续发送
.......//继续发送读写byte,继续中断
s3c24xx_i2c_stop(i2c, 0); //发送stop信号
s3c24xx_i2c_master_complete(i2c, ret);
wake_up(&i2c->wait); //唤醒i2c->wait
s3c24xx_i2c_disable_irq(i2c);
i2c_diver需要匹配i2c_client,那么i2c_client如何产生
//在注册i2c_adapter会扫描__i2c_board_list,根据i2c_devinfo信息 创建带adapter的适配器,供i2c_driver匹配。
//方法1.在bsp文件中注册i2c_register_board_info()
//方法2.如果内核支持设备树,在dts文件中添加一个i2c的节点
struct i2c_board_info {
char type[I2C_NAME_SIZE]; //名字如 lm75
unsigned short flags;
unsigned short addr; //7位地址
void *platform_data;
struct dev_archdata *archdata;
#ifdef CONFIG_OF
struct device_node *of_node;
#endif
int irq;
};
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list); //将i2c_board_info加入__i2c_board_list
在注册i2c_adapter时:
static int i2c_register_adapter(struct i2c_adapter *adap)
i2c_scan_static_board_info(adap);
list_for_each_entry(devinfo, &__i2c_board_list, list)
i2c_new_device(adapter,&devinfo->board_info)//产生依附adaper的client
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
client->adapter = adap; //放adapter到client
client->dev.platform_data = info->platform_data;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
status = device_register(&client->dev); //将生成的i2c_client放到i2c_bus上
//在写i2c_driver时,提供匹配的id_table
static struct i2c_device_id id_tables[] = {
{"lm75", 0x1111},
{"lm75a", 0x1112},
};
static struct i2c_driver lm75_drv = {
.probe = lm75_drv_probe, //在probe中注册一个fops,来对设备进行读写
.remove = lm75_drv_remove,
//必须要有
.driver = {
.name = "lm75",
},
.id_table = id_tables,
};