嵌入式软件开发之------浅析linux驱动模型(五)I2C驱动

2019-07-13 01:36发布

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] == '