一、创建 _i2c_board_list 和 platform_device

开始前先熟悉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.addri2c_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;

   //012 总线上的设备信息注册完后,__i2c_first_dynamic_bus_num3该变量会在以后注册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_deviceplatform_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.0sc8810-i2c.1sc8810-i2c.2sc8810-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;

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值