开始前先熟悉i2c_board_info 结构体以及 I2C_BOARD_INFO 宏。
/**
* struct i2c_board_info - template for devicecreation
* @type: 芯片类型,用于初始化i2c_client.name
* @flags: 用于初始化i2c_client.flags
* @addr: 存储于i2c_client.addr
* @platform_data: 存储于i2c_client.dev.platform_data
* @archdata: 拷贝至i2c_client.dev.archdata
* @irq: 存储于i2c_client.irq
*
* i2c_board_info用于构建信息表来列出存在的I2C设备。这一信息用于增长新型I2C驱动的驱动模型树。对于主板,它使用i2c_register_board_info()来静态创建。对于子板,利用已知的适配器使用i2c_new_device()动态创建。
*/
//kernel/include/linux/i2c.h
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsignedshort flags;
unsignedshort addr;
void *platform_data;
structdev_archdata *archdata;
#ifdef CONFIG_OF
structdevice_node *of_node;
#endif
int irq;
};
/**
* I2C_BOARD_INFO - macro used to list an i2cdevice and its address
* @dev_type: identifies the device type
* @dev_addr: the device's address on the bus.
*
* 用于初始化 i2c_client.name 及 i2c_client.addr,i2c_client.name会在以后注册i2c_driver的时候用到
*/
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type= dev_type, .addr = (dev_addr)
展讯智能机平台 I2C 架构的初始化从 sprd_i2c_init() 函数开始。
//kernel/archarm/mach-sc8810/board-sp8810/i2c_cfg.c
static structi2c_board_info __initdata i2c_boardinfo[] = {
{I2C_BOARD_INFO("al3006_pls",0x1c),},
};
//i2c pad: the high two bit of the addr is the padcontrol bit
static structi2c_board_info __initdata i2c_boardinfo1[] = {
{I2C_BOARD_INFO(SENSOR_MAIN_I2C_NAME,SENSOR_MAIN_I2C_ADDR),},
{I2C_BOARD_INFO(SENSOR_SUB_I2C_NAME,SENSOR_SUB_I2C_ADDR),},
};
static structi2c_board_info __initdata i2c_boardinfo2[] = {
{I2C_BOARD_INFO("pixcir_ts",0x5C),},
};
int __init sprd_i2c_init(void)
{
sprd8810_i2c2pin_config();
sprd_register_i2c_bus(0, i2c_boardinfo,
ARRAY_SIZE(i2c_boardinfo));
sprd_register_i2c_bus(1, i2c_boardinfo1,
ARRAY_SIZE(i2c_boardinfo1));
sprd_register_i2c_bus(2, i2c_boardinfo2,
ARRAY_SIZE(i2c_boardinfo2));
sprd_register_i2c_bus(3, NULL, 0);
return 0;
}
/**
* SPRD_register_i2c_bus - register I2C buswith device descriptors
* @bus_id: bus id counting from number 0
* @info: pointer into I2C device descriptortable or NULL
* @len: number of descriptors in the table
*
* Returns 0 on success or an error code.
*/
//kernel/arch/arm/mach-sc8810/i2c.c
int __init sprd_register_i2c_bus(int bus_id,
struct i2c_board_info const *info,
unsigned len)
{
int err;
BUG_ON(bus_id < 0 || bus_id > 3);
if (info) {
//当info非空时,注册i2c_board_info
err = i2c_register_board_info(bus_id, info, len);
if (err)
return err;
}
//I2C 总线注册。I2C3 总线注册以及当0,1,2总线注册i2c_board_info成功时会调用此函数
return sprd_i2c_add_bus(bus_id);
}
/**
* i2c_register_board_info - statically declareI2C devices
* @busnum: identifies the bus to which thesedevices belong
* @info: vector of i2c device descriptors
* @len: how many descriptors in the vector;may be zero to reserve
* thespecified bus number.
**/
//kernel/drivers/i2c/i2c-boardinfo.c
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 assignedafter the last static one */
if (busnum >=__i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num =busnum + 1;
//0、1、2 总线上的设备信息注册完后,__i2c_first_dynamic_bus_num为3,该变量会在以后注册i2c_client时用到
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;
//将 devinfo 添加到全局链表__i2c_board_list中,以后会扫描该链表并对每个dev创建i2c-client
list_add_tail(&devinfo->list,&__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
//kernel/arch/arm/mach-sc8810/i2c.c
static int __init sprd_i2c_add_bus(int bus_id)
{
struct platform_device *pdev;
pdev = &sprd_i2c_devices[bus_id];
/*由 staticconst char name[] = "sc8810-i2c" 知,pdev->name="sc8810-i2c"
这一步很关键,以后在 platform_driver_register()函数中会进行 driver_match_device,只有当 platform_device和platform_driver->device_driver结构体中的name 相等时,才会进入 sc8810_i2c_probe 函数,进行 I2C Adapter 以及 I2CClient 的创建。
*/
return platform_device_register(pdev);
}
//kernel/drivers/base/platform.c
int platform_device_register(struct platform_device *pdev)
{
device_initialize(&pdev->dev);
return platform_device_add(pdev);
}
/**
* platform_device_add - add a platform deviceto device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 ofplatform_device_register(), though may be called
* separately _iff_ pdev was allocated byplatform_device_alloc().
*/
//kernel/drivers/base/platform.c
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
//设置设备的母节点, struct device platform_bus = {.init_name ="platform",};
pdev->dev.parent =&platform_bus;
//设置设备的总线类型
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1)
//设置platform_device里面device的名字
//分别为:sc8810-i2c.0、sc8810-i2c.1、sc8810-i2c.2、sc8810-i2c.3
dev_set_name(&pdev->dev,"%s.%d", pdev->name, pdev->id);
else
dev_set_name(&pdev->dev,"%s", pdev->name);
for (i = 0; i <pdev->num_resources; i++) {
struct resource *p, *r =&pdev->resource[i];
if (r->name == NULL)
r->name =dev_name(&pdev->dev);
p = r->parent;
if (!p) {
if (resource_type(r) ==IORESOURCE_MEM)
p =&iomem_resource;
else if (resource_type(r)== IORESOURCE_IO)
p =&ioport_resource;
}
if (p &&insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource%d\n",
dev_name(&pdev->dev), i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platformdevice '%s'. Parent at %s\n",
dev_name(&pdev->dev),dev_name(pdev->dev.parent));
//注册设备pdev->dev。在sys/devices/platform 目录下生成相应的文件夹。
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0) {
struct resource *r =&pdev->resource[i];
unsigned long type =resource_type(r);
if (type == IORESOURCE_MEM || type== IORESOURCE_IO)
release_resource(r);
}
return ret;
}
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), thoughmay be called
* separately _iff_ device_initialize() hasbeen called separately.
*
* This adds @dev to the kobject hierarchy viakobject_add(), adds it
* to the global and sibling lists for thedevice, then
* adds it to the other relevant subsystems ofthe driver model.
*
* NOTE: _Never_ directly free @dev aftercalling this function, even
* if it returned an error! Always useput_device() to give up your
* reference instead.
*/
//kernel/drivers/base/core.c
int device_add(struct device *dev)
{
struct device *parent = NULL;
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;
}
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n",dev_name(dev), __func__);
parent = get_device(dev->parent);
setup_parent(dev, parent);
/* 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 deviceaddition. This call must come
*after dpm_sysf_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);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->class_mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->class_devices);
/* notify any interfaces that thedevice is here */
list_for_each_entry(class_intf,
&dev->class->p->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev,class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
DPMError:
bus_remove_device(dev);
BusError:
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev,&devt_attr);
ueventattrError:
device_remove_file(dev,&uevent_attr);
attrError:
kobject_uevent(&dev->kobj,KOBJ_REMOVE);
kobject_del(&dev->kobj);
Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}