Linux代码版本:linux3.0
开发板环境: tiny4412
导读:i2c控制器作为platform_device挂接在platform总线上,在《嵌入式软件开发之------浅谈linux驱动模型(四)device和driver》以i2c控制器为例,分析了
s3c_device_i2c1和s3c24xx_i2c_driver的注册过程,总结下来就是下图:

每个i2c控制器都是一个i2c 总线,i2c控制器即挂接在platform总线上,也为i2c提供总线。platform总线有platform_device和platform_driver,相应的i2c总线也有对应的device和driver,只不过是i2c_client和i2c_driver.
一、i2c_adapter的注册及 i2c_client 的实例化
下面看i2c_client,有没有觉得和platform_device很像?都是封装了devcie后添加了部分各自特点的成员:
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;
};
unsigned short flags;
标志,用 I2C_CLIENT_TEN 表示 10 bit 地址,用 I2C_CLIENT_PEC 表示SMBus的错误数据检测
unsigned short addr;
器件的地址
char name[I2C_NAME_SIZE];
设备的名字,在 new_device 属性文件中,低7位代表地址
struct i2c_adapter *adapter; /* the adapter we sit on */
虽然是挂接在I2C总线上的,client->dev.bus = &i2c_bus_type,可是 总要 指向所在的i2c控制器吗
struct i2c_driver *driver; /* and our access routines */
对应的驱动
struct device dev; /* the device structure */
封装的device结构体
int irq; /* irq issued by device */
用来表示此设备产生的IRQ
struct list_head detected;
用于插入 i2c_driver.clients 的节点,在遍历 i2c_drive r探测到实际挂接在 i2c_adapter 但又未注册
的 i2c 设备时,实例化成 i2c_client 后以此成员插入 i2c_driver.clients。
看到i2c_client就不得不看i2c设备的注册,这个时候可能有人会说,大概和platform设备注册差不多吧,确实 差不多,可还是有点区别下面就以mma7660为例,
static struct i2c_board_info i2c_devs3[] __initdata = {
{
I2C_BOARD_INFO("mma7660", 0x4c),
.platform_data = &mma7660_pdata,
},
};
展开
static struct i2c_board_info i2c_devs3[] __initdata = {
{
.type = "mma7660",
.addr = 0x4c,
.platform_data = &mma7660_pdata,
},
};
有没有发现什么不对?不是说i2c设备的结构体说 i2c_client ?咋变成了 i2c_board_info ?platform_device就是对应的啊。这里面肯定有蹊跷,i2c_board_info 最后肯定还得转成成 i2c_client,是的,这既是i2c设备的实例化,从定义的 i2c_board_info 组装成 i2c_client,这个过程将在代码中分析。接下来看一下 platform_device通过 platform_device_register来注册 ,那么 i2c_board_info 设备是通过i2c_register_board_info。
先看下 i2c_board_info 结构体
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
int irq;
};
再看一个结构体,下面会用到
struct i2c_devinfo {
struct list_head list;
int busnum;
struct i2c_board_info board_info;
};
下面再看 i2c_register_board_info 实例
i2c_register_board_info(3, i2c_devs3, ARRAY_SIZE(i2c_devs3));
{
int status;
down_write(&__i2c_board_lock);
/* dynamic bus numbers will be assigned after the last static one */
/*__i2c_first_dynamic_bus_num总是要比最大的bus号大1,后面会用到*/
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + 1;
for (status = 0; len; len--, info++) {
struct i2c_devinfo *devinfo;
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
if (!devinfo) {
pr_debug("i2c-core: can't register boardinfo!
");
status = -ENOMEM;
break;
}
/*将i2c_board_info的信息赋值给 devinfo ,然后将devinfo插入 __i2c_board_list ,所有的i2c设备都会插入
__i2c_board_list,所以一定要记住__i2c_board_list,
devinfo->busnum = 3
devinfo->board_info = &i2c_devs3 */
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
上面的知识将在下面的代码中用到,下面接着 分析 s3c24xx_i2c_probe,由于是在分析驱动框架,不是分析BSP,
硬件设备千变万化,看芯片手册写或者调试驱动是一个驱动工程师的基本功,所以对于硬件相关的设置将省略。
下面接着分析经过platform总线匹配后调用的 s3c24xx_i2c_probe
static int s3c24xx_i2c_probe(&s3c_device_i2c3)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
......
/*赋值 i2c->adap.name = "s3c2410-i2c",后面会用到*/
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
/*i2c->adap进行复制,又看到了 THIS_MODULE 只有当此驱动编译成module的时候指向此模块
,如果没有编译成module则为NULL,绝大多数owner都是 THIS_MODULE */
i2c->adap.owner = THIS_MODULE;
/*i2c->adap.algo,对应实际硬件的通信方法,控制硬件产生读写时序的函数,adapter是对硬件控制器的抽象,
实际产生硬件通信时序的s3c24xx_i2c_algorithm*/
i2c->adap.algo = &s3c24xx_i2c_algorithm;
/*通信失败重新尝试的次数*/
i2c->adap.retries = 2;
/*此adapter支持的硬件类型,公共也就是三种 I2C_CLASS_HWMON、I2C_CLASS_SPD 和 I2C_CLASS_DDC
相应的,i2c_client或者i2c_driver肯定也会有相应的class来和 i2c->adap.class 匹配 */
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
......
/* setup info block for the i2c core */
i2c->adap.algo_data = i2c;
/*i2c->adap.dev.parent 指向 s3c_device_i2c1 ,
意味着i2c->adap对应的目录会在/sys/devices/platform/s3c2440-i2c.3下*/
i2c->adap.dev.parent = &pdev->dev;
......
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
* the platform data, so if unset it will now default to always
* being bus 0.
*/
/*此adapter所对应的i2c总线号,这里是通过 paltform_device的私有结构体传递,实际还有另一个代表
/sys/devices/platform/s3c2440-i2c.3
i2c总线号的,还记得 s3c_device_i2c3.id = 3吗*/
i2c->adap.nr = pdata->bus_num;
/*注册adapter*/
ret = i2c_add_numbered_adapter(&i2c->adap);
{
int id;
int status;
if (adap->nr & ~MAX_ID_MASK)
return -EINVAL;
/*下面涉及idr机制,简单的说idr机制就是将一个整数id和特定指针映射起来,然后可以通过id号
找到对应的指针*/
retry:
/*为idr分配内存,通过idr获取id之前需要先分配内存*/
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
return -ENOMEM;
mutex_lock(&core_lock);
/* "above" here means "above or equal to", sigh;
* we need the "equal to" result to force the result
*/
/*分配id号并和adap指针关联,对于i2c adapter分配的id就是bus号,所以下面会有
id和adap->nr的判断*/
status = idr_get_new_above(&i2c_adapter_idr, adap, adap->nr, &id);
if (status == 0 && id != adap->nr) {
status = -EBUSY;
idr_remove(&i2c_adapter_idr, id);
}
mutex_unlock(&core_lock);
if (status == -EAGAIN)
goto retry;
if (status == 0)
/*开始注册adapter*/
status = i2c_register_adapter(adap);
{
int res = 0;
/* Can't register until after driver model init */
/*i2c被初始化过才能注册adapter,显然注册 i2c_bus_type 就已经初始化过*/
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
/*前面已经初始化 adap->name[0] = "s3c2410-i2c" */
if (unlikely(adap->name[0] == '