IIC总线只需要两根总线(串行数据线SDA,串行时钟线SCK)就可以完成读写操作,而且通信协议简单,一条总线上可以挂载多个设备,因此被广泛使用。但是IIC总线有一个缺点,就是传输速率比较低。SDA负责数据传输,SCL负责时钟同步。
这里简单地将I2C设备驱动的层次分为设备层、总线层。重点是理 解 4 个数据结构(i2c_driver、i2c_client、i2c_algorithm、i2c_adapter)。 i2c_driver、2c_client 属于设备层;i2c_algorithm、i2c_adapter 属于总线层。值得注意的是一个系统中可能有多个总线层, 也就包含多个总线控制器;也可能有多个设备层,包含不同的I2C设备。
i2c设备驱动层组件(i2c-dev.c)给用户提供调用接口,众所周知,用户实现策略,驱动实现的是机制。
而i2c核心层起到承上启下的作用,如下
struct bus_type:
struct bus_type i2c_bus_type = {
.name = "i2c",
.dev_attrs = i2c_dev_attrs,
.match = i2c_device_match,
.uevent = i2c_device_uevent,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
核心层,注册了i2c总线,以及驱动注册
struct i2c_driver:
struct i2c_driver
{
int id; //驱动标识ID
unsigned int class; //驱动的类型
int (*attach_adapter)(struct i2c_adapter *); //当检测到适配器时调用的函数
int (*detach_adapter)(struct i2c_adapter*); //卸载适配器时调用的函数
int (*detach_client)(struct i2c_client *) __deprecated; //卸载设备时调用的函数
//以下是一种新类型驱动需要的函数,这些函数支持IIC设备动态插入和拔出。如果不想支持只实现上面3个。要不实现上面3个。要么实现下面5个。不能同时定义
int (*probe)(struct i2c_client *,const struct i2c_device_id *); //新类型设备探测函数
int (*remove)(struct i2c_client *); //新类型设备的移除函数
void (*shutdown)(struct i2c_client *); //关闭IIC设备
int (*suspend)(struct i2c_client *,pm_messge_t mesg); //挂起IIC设备
int (*resume)(struct i2c_client *); //恢复IIC设备
int (*command)(struct i2c_client *client,unsigned int cmd,void *arg); //使用命令使设备完成特殊的功能。类似ioctl()函数
struct devcie_driver driver; //设备驱动结构体
const struct i2c_device_id *id_table; //设备ID表
int (*detect)(struct i2c_client *,int kind,struct i2c_board_info *); //自动探测设备的回调函数
const struct i2c_client_address_data *address_data; //设备所在的地址范围
struct list_head clients; //指向驱动支持的设备
};
struct i2c_client:
结构体i2c_driver和i2c_client的关系较为简单,其中i2c_driver表示一个IIC设备驱动,i2c_client表示一个IIC设备,关系如下
IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:
struct i2c_adapter:
struct i2c_adapter
{
struct module *owner; //模块计数
unsigned int id; //alogorithm的类型,定义于i2c_id.h中
unsigned int class; //允许探测的驱动类型
const struct i2c_algorithm *algo; //指向适配器的驱动程序
void *algo_data; //指向适配器的私有数据,根据不同的情况使用方法不同
int (*client_register)(struct i2c_client *); //设备client注册时调用
int (*client_unregister(struct i2c_client *); //设备client注销时调用
u8 level;
struct mutex bus_lock; //对总线进行操作时,将获得总线锁
struct mutex clist_lock ; //链表操作的互斥锁
int timeout; //超时
int retries; //重试次数
struct device dev; //指向 适配器的设备结构体
int nr ;
struct list_head clients; //连接总线上的设备的链表
char name[48]; //适配器名称
struct completion dev_released; //用于同步的完成量
};
这里重点是 const struct i2c_algorithm *algo(见下方); //指向适配器的驱动程序
IIC总线适配器就是一个IIC总线控制器,在物理上连接若干个IIC设备。IIC总线适配器本质上是一个物理设备,其主要功能是完成IIC总线控制器相关的数据通信:
struct i2c_algorithm:
每一个适配器对应一个驱动程序,该驱动程序描述了适配器与设备之间的通信方法:
struct i2c_algorithm
{
//传输函数指针,指向实现IIC总线通信协议的函数,用来确定适配器支持那些传输类型
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msg, int num);
int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);
//smbus方式传输函数指针,指向实现SMBus总线通信协议的函数。
//SMBus和IIC之间可以通过软件方式兼容,所以这里提供了一个函数,但是一般都赋值为NULL
u32 (*functionality)(struct i2c_adapter *); //返回适配器支持的功能
};
struct i2c_msg
struct i2c_board_info
先进入init入口函数,如下图所示:
在init函数中,注册了一个 “s3c2440-i2c”的platform_driver平台驱动,我们来看看probe函数做了些什么
s3c24xx_i2c_probe函数
struct i2c_adapter adap;
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
... ...
/*获取,使能I2C时钟*/
i2c->clk = clk_get(&pdev->dev, "i2c"); //获取i2c时钟
clk_enable(i2c->clk); //使能i2c时钟
... ....
/*获取资源*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c->regs = ioremap(res->start, (res->end-res->start)+1);
... ....
/*设置i2c_adapter适配器结构体, 将i2c结构体设为adap的私有数据成员*/
i2c->adap.algo_data = i2c; //i2c_adapter适配器指向s3c24xx_i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
/*初始化2440的I2C相关的寄存器*/
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
... ...
/*注册中断服务函数*/
ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,pdev->name, i2c);
... ...
/*注册i2c_adapter适配器结构体*/
ret = i2c_add_adapter(&i2c->adap);
... ...
}
其中i2c_adapter结构体是放在s3c24xx_i2c->adap下,如下图所示:(具体此结构体在上面已经介绍了,这里是直接调用)
接下来我们进入i2c_add_adapter()函数看看,到底如何注册的
int i2c_add_adapter(struct i2c_adapter *adapter)
{
int id, res = 0;
retry:
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) //调用idr_pre_get()为i2c_adapter预留内存空间
return -ENOMEM;
mutex_lock(&core_lists);
/* "above" here means "above or equal to", sigh */
res = idr_get_new_above(&i2c_adapter_idr, adapter,__i2c_first_dynamic_bus_num, &id);
//调用idr_get_new_above()将结构插入i2c_adapter_idr中,并将插入的位置赋给id,以后可以通过id在i2c_adapter_idr中找到相应的i2c_adapter结构体
mutex_unlock(&core_lists);
if (res < 0) {
if (res == -EAGAIN)
goto retry;
return res;
}
adapter->nr = id;
return i2c_register_adapter(adapter); //调用i2c_register_adapter()函数进一步来注册.
}
其中调用i2c_register_adapter(),函数代码如下所示:
static int i2c_register_adapter(struct i2c_adapter *adap)
{
struct list_head *item; //链表头,用来存放i2c_driver结构体的表头
struct i2c_driver *driver; //i2c_driver,用来描述一个IIC设备驱动
list_add_tail(&adap->list, &adapters); //添加到内核的adapter链表中
... ...
list_for_each(item,&drivers) { //for循环,从drivers链表里找到i2c_driver结构体的表头
driver = list_entry(item, struct i2c_driver, list); //通过list_head表头,找到i2c_driver结构体
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap); //调用i2c_driver的attach_adapter函数来看看,这个新注册的设配器是否支持i2c_driver }}
在i2c_register_adapter()函数里主要执行以下几步:
①将adapter放入i2c_bus_type的adapter链表
②将所有的i2c设备调出来,执行i2c_driver设备的attach_adapter函数来匹配
i2c_adapter表示物理上的一个i2C设备(适配器), 在i2c-s3c2410.c中,是存放在s3c24xx_i2c结构体下的(struct i2c_adapter adap)成员中
其中s3c24xx_i2c的结构体成员如下所示
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer, //主机传输
.functionality = s3c24xx_i2c_func,
};
static struct s3c24xx_i2c s3c24xx_i2c = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
.wait = __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
.tx_setup = 50, //用来延时,等待SCL被释放
.adap = { // i2c_adapter适配器结构体
.name = "s3c2410-i2c",
.owner = THIS_MODULE,
.algo = &s3c24xx_i2c_algorithm, //存放i2c_algorithm算法结构体
.retries = 2, //重试次数
.class = I2C_CLASS_HWMON,
},
};
显然这里是直接设置了i2c_adapter结构体,所以在s3c24xx_i2c_probe ()函数中没有分配i2c_adapter适配器结构体,
其中, i2c_adapter结构体的名称等于"s3c2410-i2c",它的通信方式等于s3c24xx_i2c_algorithm,重试次数等于2
PS:如果缺少i2c_algorithm的i2c_adapter什么也做不了,就只是个I2C设备,而没有通信方式
s3c24xx_i2c_algorithm中的关键函数master_xfer()就是用于产生i2c访问周期需要的start stop ack等信号
比如,在s3c24xx_i2c_algorithm(见上方)中的关键函数master_xfer()里,调用了:
s3c24xx_i2c_xfer -> s3c24xx_i2c_doxfer()->s3c24xx_i2c_message_start()
来启动传输message信息, 其中s3c24xx_i2c_message_start()函数代码如下:
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1; //IIC从设备地址的最低位为读写标志位
... ...
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN; //设置标志位启动IIC收发使能
if (msg->flags & I2C_M_RD) { //判断是读,还是写
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1; //设置从IIC设备地址为读标志
} else
stat |= S3C2410_IICSTAT_MASTER_TX;
s3c24xx_i2c_enable_ack(i2c); //使能ACK信号
iiccon = readl(i2c->regs + S3C2410_IICCON); //读出IICCON寄存器
writel(stat, i2c->regs + S3C2410_IICSTAT); //写入IICSTAT寄存器,使能IIC的读或写标志
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS); //将IIC从设备地址写入IICDS寄存器
/* delay here to ensure the data byte has gotten onto the bus
* before the transaction is started */
ndelay(i2c->tx_setup); //延时,等待SCL被释放,下面便可以发送起始信号+IIC设备地址值
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
//设置IICSTAT寄存器的bit5=1,开始发送起始信号+IIC从设备地址值,并回应ACK
}