在内核中已经提供I2C子系统,在linux系统中,I2C驱动结构如下图所示:
其中从上图可以I2C由三大部分组成:
1、I2C核心:I2C核心提供了总线驱动和设备驱动的注册、注销的方法,I2C通信方法,与具体适配器无关的代码以及检测设备地址的代码等。
2、I2C总线驱动:对I2C硬件体系结构中适配器的实现,控制I2C总线驱动的代码,控制I2C适配器以主控方式产生开始位,停止位,读写以及设备读写方式,产生ack等。
3、I2C客户驱动程序:是对I2C硬件体系结构设备端得实现。
我们在分析其子系统之前,还是来看看其数据结构
i2c_adapter结构体表示一个物理的i2c总线控制器
- 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_algorithm,其对应一套通信方法
- struct i2c_algorithm {
- 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);//SMBUS传输函数指针
- u32 (*functionality) (struct i2c_adapter *);//返回适配器支持的功能
- };
一个i2c适配器需要i2c_algorithm中提供的通信函数来控制适配器产生特定的访问函数。其中提供了关键的函数master_xfer()用于产生i2c访问周期需要的信号,以i2c_msg为单位
- struct i2c_msg {
- __u16 addr; /* slave address */
- __u16 flags;
- __u16 len; /* msg length */
- __u8 *buf; /* pointer to msg data */
- };
i2c_driver代表i2c从设备驱动
- struct i2c_driver {
- unsigned int class;
- int (*attach_adapter)(struct i2c_adapter *) __deprecated;//适配器函数指针
- int (*detach_adapter)(struct i2c_adapter *) __deprecated;
- /* 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 *);
- void (*alert)(struct i2c_client *, unsigned int data);
- int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
- struct device_driver driver;
- const struct i2c_device_id *id_table;
- /* 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_client代表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_driver与i2c_client是一对多的关系,一个i2c_driver上可以支持多个同等类型的i2c_client。i2c_adapter与i2c_client的关系与i2c硬件体系中适配器和从设备的关系一致,i2c_client依附在i2c_adapter。
下面来看看内核提供任何去注册一个i2c的设备驱动
1.直接使用i2c_register_board_unfo完成设备注册,这种方法适合于i2c总线上预先已知设备,因此可以预先声明i2c设备在哪条总线上,在通过数组结构i2c_board_info,内核实例为arch\arm\mach-omap2\board-h4.c。
- static struct i2c_board_info __initdata h4_i2c_board_info[] = {
- {
- I2C_BOARD_INFO("isp1301_omap", 0x2d),
- .irq = OMAP_GPIO_IRQ(125),
- },
- { /* EEPROM on mainboard */
- I2C_BOARD_INFO("24c01", 0x52),
- .platform_data = &m24c01,
- },
- { /* EEPROM on cpu card */
- I2C_BOARD_INFO("24c01", 0x57),
- .platform_data = &m24c01,
- },
- };
- static void __init omap_h4_init(void)
- {
- (...)
- i2c_register_board_info(1, h4_i2c_board_info,
- ARRAY_SIZE(h4_i2c_board_info));
- (...)
- }
那么内核怎么完成匹配呢?首先来看看i2c_register_board_info()
- int __init
- i2c_register_board_info(int busnum,
- struct i2c_board_info const *info, unsigned len)
- {
- int status;
- down_write(&__i2c_board_lock);
- /* dynamic bus numbers will be assigned after the last static one */
- 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!\n");
- status = -ENOMEM;
- break;
- }
- devinfo->busnum = busnum;
- devinfo->board_info = *info;
- list_add_tail(&devinfo->list, &__i2c_board_list);
- }
- up_write(&__i2c_board_lock);
- return status;
- }
这上面只做了一个非常重要的,将i2c_board_info放入到__i2c_board_list链表,而这个info中存放的是i2c通信非常重要的,设备名字和设备地址。那么链表何时使用呢?这个在i2c_scan_static_board_info的时候会调用
- static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
- {
- struct i2c_devinfo *devinfo;
- down_read(&__i2c_board_lock);
- list_for_each_entry(devinfo, &__i2c_board_list, list) {
- if (devinfo->busnum == adapter->nr
- && !i2c_new_device(adapter,
- &devinfo->board_info))
- dev_err(&adapter->dev,
- "Can't create device at 0x%02x\n",
- devinfo->board_info.addr);
- }
- up_read(&__i2c_board_lock);
- }
该函数遍历挂载__i2c_board_list链表上面的i2c设备的信息,也就是我们在启动的时候指出的i2c设备的信息,如果指定设备位于adapter所在的i2c总线上,那么就调用i2c_new_device()。
2.直接使用2c_new_device, i2c_new_probed_device
- struct i2c_client *
- i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
- {
- struct i2c_client *client;
- int status;
- client = kzalloc(sizeof *client, GFP_KERNEL);
- if (!client)
- return NULL;
- client->adapter = adap;
- client->dev.platform_data = info->platform_data;
- if (info->archdata)
- client->dev.archdata = *info->archdata;
- client->flags = info->flags;
- client->addr = info->addr;
- client->irq = info->irq;
- strlcpy(client->name, info->type, sizeof(client->name));
- /* Check for address validity */
- status = i2c_check_client_addr_validity(client);
- if (status) {
- dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
- client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
- goto out_err_silent;
- }
- /* Check for address business */
- status = i2c_check_addr_busy(adap, client->addr);
- if (status)
- goto out_err;
- client->dev.parent = &client->adapter->dev;
- client->dev.bus = &i2c_bus_type;
- client->dev.type = &i2c_client_type;
- client->dev.of_node = info->of_node;
- /* For 10-bit clients, add an arbitrary offset to avoid collisions */
- dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
- client->addr | ((client->flags & I2C_CLIENT_TEN)
- ? 0xa000 : 0));
- status = device_register(&client->dev);
- if (status)
- goto out_err;
- dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
- client->name, dev_name(&client->dev));
- return client;
- out_err:
- dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
- "(%d)\n", client->name, client->addr, status);
- out_err_silent:
- kfree(client);
- return NULL;
- }
该函数只是device_register,但是上面出现了一个新的结构,i2c_client,其实它就是一个struct device的i2c设备的封装。在client里保存该设备的相关信息,client->adapter指向了它所在的adapter。clent->dev所在的bus为i2c_bus_type,在device_register注册的时候,会调用总线的match函数。
- static int i2c_device_match(struct device *dev, struct device_driver *drv)
- {
- struct i2c_client *client = i2c_verify_client(dev);
- struct i2c_driver *driver;
- if (!client)
- return 0;
- /* Attempt an OF style match */
- if (of_driver_match_device(dev, drv))
- return 1;
- driver = to_i2c_driver(drv);
- /* match on an id table if there is one */
- if (driver->id_table)
- return i2c_match_id(driver->id_table, client) != NULL;
- return 0;
- }
- static const struct i2c_device_id *i2c_match_id(const struct i2c_device_id *id,
- const struct i2c_client *client)
- {
- while (id->name[0]) {
- if (strcmp(client->name, id->name) == 0)
- return id;
- id++;
- }
- return NULL;
- }
上面是匹配driver的id_table的名字和client的名字是否相同,那么会调用驱动的probe函数。
下面在分析下i2c_new_probed_device有啥不一样,前面一个认为设备肯定存在,而后面的是对于已经识别出来的设备,才会创建。
- for (i = 0; addr_list[i] != I2C_CLIENT_END; i++) {
- /* Check address validity */
- if (i2c_check_addr_validity(addr_list[i]) < 0) {
- dev_warn(&adap->dev, "Invalid 7-bit address "
- "0x%02x\n", addr_list[i]);
- continue;
- }
- /* Check address availability */
- if (i2c_check_addr_busy(adap, addr_list[i])) {
- dev_dbg(&adap->dev, "Address 0x%02x already in "
- "use, not probing\n", addr_list[i]);
- continue;
- }
- /* Test address responsiveness */
- if (probe(adap, addr_list[i]))
- break;
- }
3.直接i2c_add_driver
前面的2中方法,都要实现确定适配器,如果我们不知道这个i2c设备在那个适配器上,怎么办?内核提供了一种去class表示在所有的适配器上查找一些i2c设备的地址。
- static struct i2c_driver at24_driver = {
- .driver = {
- .name = "at24",
- .owner = THIS_MODULE,
- },
- .probe = at24_probe,
- .remove = __devexit_p(at24_remove),
- .id_table = at24_ids,
- };
- static int __init at24_init(void)
- {
- if (!io_limit) {
- pr_err("at24: io_limit must not be 0!\n");
- return -EINVAL;
- }
- io_limit = rounddown_pow_of_two(io_limit);
- return i2c_add_driver(&at24_driver);
- }
而i2c_add_diver只是调用
i2c_register_driver了- int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
- {
- int res;
- /* Can't register until after driver model init */
- if (unlikely(WARN_ON(!i2c_bus_type.p)))
- return -EAGAIN;
- /* add the driver to the list of i2c drivers in the driver core */
- driver->driver.owner = owner;
- driver->driver.bus = &i2c_bus_type;
- /* When registration returns, the driver core
- * will have called probe() for all matching-but-unbound devices.
- */
- res = driver_register(&driver->driver);
- if (res)
- return res;
- /* Drivers should switch to dev_pm_ops instead. */
- if (driver->suspend)
- pr_warn("i2c-core: driver [%s] using legacy suspend method\n",
- driver->driver.name);
- if (driver->resume)
- pr_warn("i2c-core: driver [%s] using legacy resume method\n",
- driver->driver.name);
- pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
- INIT_LIST_HEAD(&driver->clients);
- /* Walk the adapters that are already present */
- i2c_for_each_dev(driver, __process_new_driver);
- return 0;
- }
上面比较主要的是driver_register,同事遍历driver->client链表,会调用driver->detect的函数。
阅读(526) | 评论(0) | 转发(0) |