i2c sub system __i2c_board_list/klist_devices/klist_drivers
i2c_devinfo全局链表: __i2c_board_list 用来挂接 i2c_board_info,这个信息用来生成 i2c_client
i2c_client 链表: i2c_bus_type->p->klist_devices
i2c_driver 链表:i2c_bus_type->p->klist_drivers
硬件i2c控制器硬件初始化完成,注册 adapter时,依据__i2c_board_list 中信息生成i2c_client,并挂接在klist_devices链表上
在注册 adapter时,即:i2c_add_numbered_adapter( struct i2c_adapter*),会做三件事情:
1、遍历 __i2c_board_list 链表,查找和 adapter->nr 相同的 i2c_devinfo->busnum,查找成功就会生成 i2c_client
2、将生成的 i2c_client->dev 挂接到 klist_devices 链表
3、如果第一步生成 i2c_client 成功,那么遍历 klist_drivers 链表,查找和 i2c_client->name 相同的 i2c_driver->id_table->name,查找成功调用 i2c_driver->probe()
在注册i2c_driver时,即:i2c_add_driver(struct i2c_driver*),会做两件事情:
1、将 i2c_driver->drv 挂接到此链表
2、遍历 klist_devices 链表,查找与 i2c_driver->id_table->name 相同的 i2c_client->name。
如果查找成功调用 i2c_driver->probe()。
如果查找失败,有两个原因:
a、硬件i2c控制器尚未初始化,即 __i2c_board_list 尚未转化为 i2c_client;
b、 硬件i2c控制器已经初始化,但是 i2c_board_info 注册失败。i2c_board_info 注册失败又分两种:1、没有注册 2、注册了而且挂接到了__i2c_board_list,但是挂接时,硬件i2c控制器早就初始化完成,所以没有生成 i2c_client,klist_devices 链表中就不会有此i2c_client 信息。
误区:曾经以为 在 i2c_board_info 注册成功后,module_init()结束时,就一定会执行 i2c_driver->probe()。
现在看来,
1、i2c_add_driver()早于硬件i2c控制器硬件初始化,那么会在注册 adapter 时,i2c_client->name 匹配 i2c_driver->id_table->name 成功,执行 i2c_driver->probe()
2、i2c_add_driver()晚于硬件i2c控制器硬件初始化,那么会在i2c_add_driver()时,i2c_driver->id_table->name 匹配 i2c_client 成功,执行 i2c_driver->probe()
struct bus_type i2c_bus_type = {
.name= "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,
};
EXPORT_SYMBOL_GPL(i2c_bus_type);
struct bus_type {
const char *name;
const char *dev_name;
struct device *dev_root;
struct bus_attribute*bus_attrs;
struct device_attribute*dev_attrs;
struct driver_attribute*drv_attrs;
int (*match)(struct device *dev, struct device_driver *drv);
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
int (*probe)(struct device *dev);
int (*remove)(struct device *dev);
void (*shutdown)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct dev_pm_ops *pm;
struct iommu_ops *iommu_ops;
struct subsys_private *p;
};
struct subsys_private {
struct kset subsys;
struct kset *devices_kset;
struct list_head interfaces;
struct mutex mutex;
struct kset *drivers_kset;
struct klist klist_devices; // 挂接 i2c_client->dev 的链表
struct klist klist_drivers; // 挂接 i2c_driver->drv 的链表
struct blocking_notifier_head bus_notifier;
unsigned int drivers_autoprobe:1;
struct bus_type *bus; // 总线类型,此处为 &i2c_bus_type
struct kset glue_dirs;
struct class *class;
};
kernel/drivers/i2c/i2c-boardinfo.c
struct i2c_client 中的name/addr/irq 等信息是从 struct i2c_board_info得来的,关于如何得到见下文。
所有的i2c_board_info 都要添加到全局链表 __i2c_board_list 中,而且应该在硬件i2c控制器模块加载前添加,以便控制器模块加载时生成 i2c_client。生成的 i2c_client 通过其 struct device* 成员被添加到 i2c_bus_type 总线上。原因见下文。
/**
* struct i2c_board_info - template for device creation
* @type: chip type, to initialize i2c_client.name
* @flags: to initialize i2c_client.flags
* @addr: stored in i2c_client.addr
* @platform_data: stored in i2c_client.dev.platform_data
* @archdata: copied into i2c_client.dev.archdata
* @of_node: pointer to OpenFirmware device node
* @irq: stored in i2c_client.irq
*/
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned shortflags;
unsigned shortaddr;
void *platform_data;
struct dev_archdata*archdata;
struct device_node *of_node;
int irq;
};
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; //在硬件i2c控制器注册adapter时, 这个busnum 将和adapter->nr匹配,生成i2c_client。
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
在struct i2c_adapter 注册时,将链表 __i2c_board_list 中的 i2c_devinfo->busnum 和 i2c_adapter->nr 进行比较,相同则进行 i2c_client生成,并且通过 struct i2c_client的struct device* 成员添加到 i2c_bus_type->p->klist_devices 链表中。
那么在进行struct i2c_driver 注册时,就可以到 klist_devices 链表中查找 name 域和 i2c_driver->id_table->name 相同的 i2c_client 了。
如果查找到,就可以继续调用 struct i2c_driver->probe()函数了,并且将此 i2c_client 和 i2c_driver->id_table 作为参数。
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status;
printk("[i2c-core][i2c_add_numbered_adapter] start\n");
if (adap->nr == -1) /* -1 means dynamically assign bus id */
return i2c_add_adapter(adap);
if (adap->nr & ‾MAX_ID_MASK)
return -EINVAL;
retry:
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
*/
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)
status = i2c_register_adapter(adap);
return status;
}
EXPORT_SYMBOL_GPL(i2c_add_numbered_adapter);
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0;
printk("[i2c-core][i2c_register_adapter] start\n");
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
/* Sanity checks */
if (unlikely(adap->name[0] == '\0')) {
pr_err("i2c-core: Attempt to register an adapter with "
"no name!\n");
return -EINVAL;
}
if (unlikely(!adap->algo)) {
pr_err("i2c-core: Attempt to register adapter '%s' with "
"no algo!\n", adap->name);
return -EINVAL;
}
rt_mutex_init(&adap->bus_lock);
mutex_init(&adap->userspace_clients_lock);
INIT_LIST_HEAD(&adap->userspace_clients);
/* Set default timeout to 1 second if not already set */
if (adap->timeout == 0)
adap->timeout = HZ;
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
adap->dev.bus = &i2c_bus_type;
adap->dev.type = &i2c_adapter_type;
res = device_register(&adap->dev);
if (res)
goto out_list;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
#ifdef CONFIG_I2C_COMPAT
res = class_compat_create_link(i2c_adapter_compat_class, &adap->dev,
adap->dev.parent);
if (res)
dev_warn(&adap->dev,
"Failed to create compatibility class link\n");
#endif
/* create pre-declared device nodes */
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
mutex_unlock(&core_lock);
printk("[i2c-core][i2c_register_adapter] end\n");
return 0;
out_list:
。。。。。。。
}
遍历 __i2c_board_list 链表上的 i2c_board_info ,比较其 busnum 和 指定的 adapter->nr 是否相同,相同则生成 struct i2c_client ,并且将 i2c_client 的 struct device* 成员挂接到 i2c_bus_type->p->klist_devices 链表上,等到 struct i2c_driver 注册时,将会遍历此链表,寻找和 i2c_driver->id_table->name 相同的 i2c_client。
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
struct i2c_devinfo*devinfo;
printk("[i2c-core][i2c_scan_static_board_info]\n");
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))
printk(
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
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; // 生成 struct i2c_client
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); // 将 i2c_client 的struct device* 成员挂接到 i2c_bus_type->p->klist_devices 链表上。
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:
。。。。。。。
}
EXPORT_SYMBOL_GPL(i2c_new_device);
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct kobject *kobj;
struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev)
goto done;
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
/*
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
*/
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/* Notify clients of device addition. This call must come
* after dpm_sysfs_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev); // 如果i2c_driver的注册先于硬件i2c控制器模块的加载,这时 __i2c_board_list里面的信息 还没有生成 i2c_client,i2c_driver->probe() 就要等到此时执行。
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
done:
。。。。。。。。。。。
}
int bus_add_device(struct device *dev)
{
struct bus_type *bus = bus_get(dev->bus);
int error = 0;
if (bus) {
pr_debug("bus: '%s': add device %s\n", bus->name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
goto out_put;
error = sysfs_create_link(&bus->p->devices_kset->kobj,
&dev->kobj, dev_name(dev));
if (error)
goto out_id;
error = sysfs_create_link(&dev->kobj,
&dev->bus->p->subsys.kobj, "subsystem");
if (error)
goto out_subsys;
klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices); // 将 i2c_client 挂接到 i2c_bus_type->p->klist_devices 上。
}
return 0;
out_subsys:
sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev));
out_id:
device_remove_attrs(bus, dev);
out_put:
bus_put(dev->bus);
return error;
}
void bus_probe_device(struct device *dev)
{
struct bus_type *bus = dev->bus;
struct subsys_interface *sif;
int ret;
if (!bus)
return;
if (bus->p->drivers_autoprobe) {
ret = device_attach(dev);
WARN_ON(ret < 0);
}
mutex_lock(&bus->p->mutex);
list_for_each_entry(sif, &bus->p->interfaces, node)
if (sif->add_dev)
sif->add_dev(dev, sif);
mutex_unlock(&bus->p->mutex);
}
int device_attach(struct device *dev)
{
int ret = 0;
device_lock(dev);
if (dev->driver) {
if (klist_node_attached(&dev->p->knode_driver)) {
ret = 1;
goto out_unlock;
}
ret = device_bind_driver(dev);
if (ret == 0)
ret = 1;
else {
dev->driver = NULL;
ret = 0;
}
} else {
pm_runtime_get_noresume(dev);
ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
pm_runtime_put_sync(dev);
}
out_unlock:
device_unlock(dev);
return ret;
}
EXPORT_SYMBOL_GPL(device_attach);
遍历 i2c_bus_type->p->klist_drivers 链表,对比 i2c_driver->id_table->name 和 i2c_client->name,相同则调用 i2c_driver->probe()。
int bus_for_each_drv(struct bus_type *bus, struct device_driver *start,
void *data, int (*fn)(struct device_driver *, void *))
{
struct klist_iter i;
struct device_driver *drv;
int error = 0;
if (!bus)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_drivers, &i,
start ? &start->p->knode_bus : NULL);
while ((drv = next_driver(&i)) && !error)
error = fn(drv, data);
klist_iter_exit(&i);
return error;
}
EXPORT_SYMBOL_GPL(bus_for_each_drv);
static int __device_attach(struct device_driver *drv, void *data)
{
struct device *dev = data;
if (!driver_match_device(drv, dev)) // ->i2c_device_match() ->i2c_match_id()
return 0;
return driver_probe_device(drv, dev); // ->i2c_device_probe() ->i2c_driver->probe(client, i2c_match_id(driver->id_table, client))
}
注册 struct i2c_driver 。
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, 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 */
int ret =0;
ret = i2c_for_each_dev(driver, __process_new_driver);
printk("[i2c-core][i2c_register_driver] ret:%d driver-name:%s \n",ret, driver->driver.name);
return 0;
}
EXPORT_SYMBOL(i2c_register_driver);
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other =driver_find(drv->name, drv->bus); //查看此i2c_driver是否已经注册过
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret =bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret)
bus_remove_driver(drv);
return ret;
}
EXPORT_SYMBOL_GPL(driver_register);
int bus_add_driver(struct device_driver *drv)
{
struct bus_type *bus;
struct driver_private *priv;
int error = 0;
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
pr_debug("bus: '%s': add driver %s\n", bus->name, drv->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_bus;
}
klist_init(&priv->klist_devices, NULL, NULL);
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv); // 遍历 i2c_bus_type->p->klist_devices 链表上的 device ,查看是否可以找到和 i2c_driver->id_table->name 相同的 i2c_client->name 。有,则i2c_driver->probe()。
if (error)
goto out_unregister;
}
klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); //将 struct i2c_driver 的 struct device_driver* 成员挂接到 i2c_bus_type->p->klist_drivers 上。
module_add_driver(drv->owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR "%s: uevent attr (%s) failed\n",
__func__, drv->name);
}
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
__func__, drv->name);
}
if (!drv->suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
__func__, drv->name);
}
}
kobject_uevent(&priv->kobj, KOBJ_ADD);
return 0;
out_unregister:
kobject_put(&priv->kobj);
kfree(drv->p);
drv->p = NULL;
out_put_bus:
bus_put(bus);
return error;
}
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv,__driver_attach); //遍历 i2c_bus_type->p->devices 链表上的 struct device,并以此获得 i2c_client,作为 //__driver_attach() 的参数
}
static int __driver_attach(struct device *dev, void *data)
{
struct device_driver *drv = data;
/*
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
*
* driver_probe_device() will spit a warning if there
* is an error.
*/
if (!driver_match_device(drv, dev))
return 0;
if (dev->parent)/* Needed for USB */
device_lock(dev->parent);
device_lock(dev);
if (!dev->driver)
driver_probe_device(drv, dev);
device_unlock(dev);
if (dev->parent)
device_unlock(dev->parent);
return 0;
}
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1; // i2c_device_match()->i2c_match_id()
}
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;
printk("[i2c-core][i2c_device_match] end\n");
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;
}
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
ret = really_probe(dev, drv);
pm_runtime_put_sync(dev);
return ret;
}
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = 0;
atomic_inc(&probe_count);
pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
drv->bus->name, __func__, drv->name, dev_name(dev));
WARN_ON(!list_empty(&dev->devres_head));
dev->driver = drv;
if (driver_sysfs_add(dev)) {
printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
__func__, dev_name(dev));
goto probe_failed;
}
if (dev->bus->probe) {
ret = dev->bus->probe(dev); // i2c_device_probe()->i2c_driver->probe()
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
driver_bound(dev);
ret = 1;
pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
probe_failed:
。。。。。。。。。。。。。。
}
static int i2c_device_probe(struct device *dev)
{
struct i2c_client*client = i2c_verify_client(dev);
struct i2c_driver*driver;
int status;
if (!client)
return 0;
driver = to_i2c_driver(dev->driver);
if (!driver->probe || !driver->id_table)
return -ENODEV;
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
printk("[i2c-core][i2c_device_probe] \n");
status = driver->probe(client,i2c_match_id(driver->id_table,client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
硬件i2c控制器模块加载,共有3个;通过 module_init() 加载,顺序为6,所以 i2c_board_info 应该在这之前注册。
加载时会初始化硬件,初始化完成后才能进行 i2c_driver 的注册,否则在 i2c_driver->probe() 时进行 i2c 通信会失败。
static struct platform_device mt_device_i2c[] = {
{
.name = "mt-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(mt_resource_i2c0),
.resource = mt_resource_i2c0,
},
{
.name = "mt-i2c",
.id = 1,
.num_resources = ARRAY_SIZE(mt_resource_i2c1),
.resource = mt_resource_i2c1,
},
{
.name = "mt-i2c",
.id = 2,
.num_resources = ARRAY_SIZE(mt_resource_i2c2),
.resource = mt_resource_i2c2,
},
};
__init int mt_board_init(void)
{
for (i = 0; i < ARRAY_SIZE(mt_device_i2c); i++){
retval = platform_device_register(&mt_device_i2c[i]);
if (retval != 0){
return retval;
}
}
。。。。。。。。。
}
static U32 mt_i2c_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm mt_i2c_algorithm = {
.master_xfer = mt_i2c_transfer,
.smbus_xfer = NULL,
.functionality = mt_i2c_functionality,
};
static inline void mt_i2c_init_hw(mt_i2c *i2c)
{
i2c_writel(i2c,OFFSET_SOFTRESET, 0x0001);
i2c_writel(i2c,OFFSET_DCM_EN, 0x0);
}
static S32 mt_i2c_probe(struct platform_device *pdev)
{
S32 ret, irq;
mt_i2c *i2c = NULL;
struct resource *res;
/* Request platform_device IO resource*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if (res == NULL || irq < 0)
return -ENODEV;
/* Request IO memory */
if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
return -ENOMEM;
}
if (NULL == (i2c = kzalloc(sizeof(mt_i2c), GFP_KERNEL)))
return -ENOMEM;
/* initialize mt_i2c structure */
i2c->id = pdev->id;
i2c->base = IO_PHYS_TO_VIRT(res->start);
//i2c->base = 0x11011000;
i2c->irqnr = irq;
#if (defined(CONFIG_MT_I2C_FPGA_ENABLE))
i2c->clk = I2C_CLK_RATE;
#else
i2c->clk = mt_get_bus_freq();// is not ready
switch(i2c->id){
case 0:
i2c->pdn = MT_CG_PERI_I2C0;
break;
case 1:
i2c->pdn = MT_CG_PERI_I2C1;
break;
case 2:
i2c->pdn = MT_CG_PERI_I2C2;
break;
default:
dev_err(&pdev->dev, "Error id %d\n", i2c->id);
break;
}
#endif
i2c->dev = &i2c->adap.dev;
i2c->adap.dev.parent = &pdev->dev;
i2c->adap.nr = i2c->id;
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &mt_i2c_algorithm; // i2c通信算法的具体实现
i2c->adap.algo_data = NULL;
i2c->adap.timeout = 2 * HZ; /*2s*/
i2c->adap.retries = 1; /*DO NOT TRY*/
snprintf(i2c->adap.name, sizeof(i2c->adap.name), I2C_DRV_NAME);
i2c->pdmabase = AP_DMA_BASE + 0x200 + (0x80*(i2c->id));
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
ret = request_irq(irq, mt_i2c_irq, IRQF_TRIGGER_LOW, I2C_DRV_NAME, i2c);
if (ret){
dev_err(&pdev->dev, "Can Not request I2C IRQ %d\n", irq);
goto free;
}
mt_i2c_init_hw(i2c); // 硬件i2c控制器的初始化
i2c_set_adapdata(&i2c->adap, i2c);
ret = i2c_add_numbered_adapter(&i2c->adap); // 遍历 __i2c_board_list 链表,生成 i2c_client ,并且挂接到 i2c_bus_type->p->klist_devices 上。
if (ret){
dev_err(&pdev->dev, "failed to add i2c bus to i2c core\n");
goto free;
}
platform_set_drvdata(pdev, i2c);
#ifdef I2C_DEBUG_FS
ret = device_create_file(i2c->dev, &dev_attr_debug);
if ( ret ){
/*Do nothing*/
}
#endif
printk("[i2c][mt_i2c_probe] i2c->adap.nr:%d \n", i2c->adap.nr);
return ret;
free:
mt_i2c_free(i2c);
return ret;
}
static struct platform_driver mt_i2c_driver = {
.probe = mt_i2c_probe,
.remove = mt_i2c_remove,
.suspend = mt_i2c_suspend,
.resume = mt_i2c_resume,
.driver = {
.name = I2C_DRV_NAME,
.owner = THIS_MODULE,
},
};
static S32 __init mt_i2c_init(void)
{
return platform_driver_register(&mt_i2c_driver);
}
static void __exit mt_i2c_exit(void)
{
platform_driver_unregister(&mt_i2c_driver);
}
module_init(mt_i2c_init);
module_exit(mt_i2c_exit);
i2c sub system __i2c_board_list/klist_devices/klist_drivers的更多相关文章
- linux下i2c驱动笔记 转
1. 几个基本概念 1.1. 设备模型 由 总线(bus_type) + 设备(device) + 驱动(device_driver) 组成,在该模型下,所有的设备通过总线连接起来,即使有些设备没有连 ...
- 最新内核3.4)Linux 设备树加载I2C client adapter 的流程(内核3.4 高通)【转】
转自:https://blog.csdn.net/lsn946803746/article/details/52515225 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转 ...
- Linux内核中SPI/I2c子系统剖析
Linux内核中,SPI和I2C两个子系统的软件架构是一致的,且Linux内核的驱动模型都以bus,driver,device三种抽象对象为基本元素构建起来.下文的分析将主要用这三种抽象对象的创建过程 ...
- 在android下使用i2c tools
在android使用i2c tools访问i2c,很方便,可以在https://launchpad.net/ubuntu/+source/i2c-tools 下载最新的i2c tools. 把i2c- ...
- 【.NET 与树莓派】气压传感器——BMP180
BMP180 是一款数字气压计传感器,实际可读出温度和气压值.此模块使用 IIC(i2c)协议.模块体积很小,比老周的大拇指指甲还小:也很便宜,一般是长这样的.螺丝孔只开一个,也有开两个孔的. 这货基 ...
- am335x system upgrade kernel i2c rtc eeprom(六)
1 Scope of Document This document describes i2c bus hardware design and support i2c-devices: ee ...
- I.MX6 Linux I2C device& driver hacking
/******************************************************************************************* * I.MX6 ...
- i2c驱动程序全面分析,从adapter驱动程序到设备驱动程序
开发板 :mini2440 内核版本:linux2.6.32.2 驱动程序参考:韦东山老师毕业班i2c 内容概括: 1.adapter client 简介 2.adapter 驱动框架 ...
- I2C总线、设备、驱动
I2C总线.设备.驱动 框架 I2C驱动框架可分为3个部分,分别是:I2C核心层.I2C总线驱动层(适配器层)以及I2C设备驱动层: I2C核心层 提供了统一的I2C操作函数,主要有两套函数smbus ...
随机推荐
- 搭建maven项目简介
http://jingyan.baidu.com/album/9f7e7ec0b714ae6f29155465.html?picindex=1 Maven学习 (一) 搭建Maven环境 http:/ ...
- 静默安装ORACLE【weber出品必属精品】
安装配置系统环境安装linux ,所有服务都不选择,只是选择安装开发工具,不要安装防火墙(当然也可以在后面关闭) 打开终端,执行如下命令,检查安装包,没有的都要安装 make, glibc, liba ...
- xib添加手势后报错:-[UITapGestureRecognizer setFrame:]: unrecognized selector sent to instance xxx
主要原因如下: + (instancetype)mineHeaderView { return [[NSBundle mainBundle] loadNibNamed:@"DDMineHea ...
- JS中window.showModalDialog()详解 HTML DOM open() 方法
window.showModalDialog()方法用来创建一个显示HTML内容的模态对话框. window.showModelessDialog()方法用来创建一个显示HTML内容的非模态对话框. ...
- MySQL FROM 子查询
FROM 子句中的子查询 MySQL FROM 子查询是指 FROM 的子句作为子查询语句,主查询再到子查询结果中获取需要的数据.FROM 子查询语法如下: SELECT ... FROM (subq ...
- 使用 pm2 来守护 NoderCMS
pm2 是一个带有负载均衡功能的Node应用的进程管理器,使用 pm2 可以帮助你守护和监控 NoderCMS 的正常运行, 基于Node.js+MongoDB的轻量级内容管理系统NoderCMS ...
- 1.3.1 switch 语句中的 String
switch语句是一种高效的多路语句,可以省掉很多繁杂的嵌套if判断: 在Java 6及之前,case语句中的常量只能是byte.char.short和int(也可以是对应的封装类)或枚举常量,在Ja ...
- 无缝滚动js (手写通俗易懂)
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- setTintColor
[self.button:[UIColor whiteColor]]; 初步研究 这个是决定的 btn的 外边框的颜色
- HADOOP在处理HIVE时权限错误的解决办法
今天,小乔操作时发现问题: org.apache.hadoop.security.AccessControlException: Permission denied: user=root, acces ...