qcom 8916平台的i2c init部分
2017-02-22 15:23
447 查看
DT_MACHINE_START(MSM8226_DT, "Qualcomm Technologies, Inc. MSM 8226 (Flattened Device Tree)") .map_io = msm_map_msm8226_io, .init_machine = msm8226_init, .dt_compat = msm8226_dt_match, .reserve = msm8226_reserve, .smp = &arm_smp_ops, void __init msm8226_init(void) { struct of_dev_auxdata *adata = msm8226_auxdata_lookup; /* * populate devices from DT first so smem probe will get called as part * of msm_smem_init. socinfo_init needs smem support so call * msm_smem_init before it. msm8226_init_gpiomux needs socinfo so * call socinfo_init before it. */ board_dt_populate(adata); msm_smem_init(); if (socinfo_init() < 0) pr_err("%s: socinfo_init() failed\n", __func__); msm8226_init_gpiomux(); msm8226_add_drivers(); } void __init msm8226_add_drivers(void) { msm_smd_init(); msm_rpm_driver_init(); msm_spm_device_init(); msm_pm_sleep_status_init(); rpm_smd_regulator_driver_init(); qpnp_regulator_init(); spm_regulator_init(); msm_gcc_8226_init(); msm_bus_fabric_init_driver(); qup_i2c_init_driver(); ncp6335d_regulator_init(); fan53555_regulator_init(); cpr_regulator_init(); } int __init qup_i2c_init_driver(void) { static bool initialized; if (initialized) return 0; else initialized = true; return platform_driver_register(&qup_i2c_driver); } static struct platform_driver qup_i2c_driver = { .probe = qup_i2c_probe, .remove = qup_i2c_remove, .driver = { .name = "qup_i2c", .owner = THIS_MODULE, .pm = &i2c_qup_dev_pm_ops, .of_match_table = i2c_qup_dt_match, }, }; static int qup_i2c_probe(struct platform_device *pdev) { struct qup_i2c_dev *dev; struct resource *qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *res; struct resource *in_irq, *out_irq, *err_irq; struct clk *clk, *pclk; int ret = 0; int i; int dt_gpios[I2C_GPIOS_DT_CNT]; bool use_device_tree = pdev->dev.of_node; struct msm_i2c_platform_data *pdata; gsbi_mem = NULL; dev_dbg(&pdev->dev, "qup_i2c_probe\n"); if (use_device_tree) { pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios); if (ret) goto get_res_failed; } else pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform data not initialized\n"); return -ENOSYS; } qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qup_phys_addr"); if (!qup_mem) { dev_err(&pdev->dev, "platform_get_resource_byname(qup_phys_addr) failed\n"); ret = -ENODEV; goto get_res_failed; } /* * We only have 1 interrupt for new hardware targets and in_irq, * out_irq will be NULL for those platforms */ in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "qup_in_intr"); out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "qup_out_intr"); err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "qup_err_intr"); if (!err_irq) { dev_err(&pdev->dev, "no error irq resource?\n"); ret = -ENODEV; goto get_res_failed; } /*请求资源,告诉其他驱动,该资源已经被占用*/ /*请求该内存资源*/ qup_io = request_mem_region(qup_mem->start, resource_size(qup_mem), pdev->name); if (!qup_io) { dev_err(&pdev->dev, "QUP region already claimed\n"); ret = -EBUSY; goto get_res_failed; } if (!pdata->use_gsbi_shared_mode) { /*如果该标志没有设置,则取出相应的资源*/ gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gsbi_qup_i2c_addr"); if (!gsbi_mem) { dev_dbg(&pdev->dev, "Assume BLSP\n"); /* * BLSP core does not need protocol programming so this * resource is not expected */ goto blsp_core_init; } gsbi_io = request_mem_region(gsbi_mem->start, resource_size(gsbi_mem), pdev->name); if (!gsbi_io) { dev_err(&pdev->dev, "GSBI region already claimed\n"); ret = -EBUSY; goto err_res_failed; } } blsp_core_init: /*如果该clk不为空,通过名字取出该clk*/ clk = clk_get(&pdev->dev, "core_clk"); if (IS_ERR(clk)) { dev_err(&pdev->dev, "Could not get core_clk\n"); ret = PTR_ERR(clk); goto err_clk_get_failed; } /*如果该pclk不为空,通过名字取出该clk*/ pclk = clk_get(&pdev->dev, "iface_clk"); if (IS_ERR(pclk)) { dev_err(&pdev->dev, "Could not get iface_clk\n"); ret = PTR_ERR(pclk); clk_put(clk); goto err_clk_get_failed; } /* We support frequencies upto FAST Mode(400KHz) */ if (pdata->clk_freq <= 0 || pdata->clk_freq > 400000) { dev_err(&pdev->dev, "clock frequency not supported\n"); ret = -EIO; goto err_config_failed; } /*分配qup_i2c_dev结构体*/ dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL); if (!dev) { ret = -ENOMEM; goto err_alloc_dev_failed; } dev->dev = &pdev->dev; if (in_irq) dev->in_irq = in_irq->start; if (out_irq) dev->out_irq = out_irq->start; dev->err_irq = err_irq->start; if (in_irq && out_irq) dev->num_irqs = 3; else dev->num_irqs = 1; dev->clk = clk; dev->pclk = pclk; /*映射请求到的内存*/ dev->base = ioremap(qup_mem->start, resource_size(qup_mem)); if (!dev->base) { ret = -ENOMEM; goto err_ioremap_failed; } /* Configure GSBI block to use I2C functionality */ if (gsbi_mem) { dev->gsbi = ioremap(gsbi_mem->start, resource_size(gsbi_mem)); if (!dev->gsbi) { ret = -ENOMEM; goto err_gsbi_failed; } } for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) { if (use_device_tree && i < I2C_GPIOS_DT_CNT) { dev->i2c_gpios[i] = dt_gpios[i]; } else { res = platform_get_resource_byname(pdev, IORESOURCE_IO, i2c_rsrcs[i]); dev->i2c_gpios[i] = res ? res->start : -1; } } /*初始化完dev后,把dev设置为pdev的dev的私有数据*/ platform_set_drvdata(pdev, dev); dev->one_bit_t = (USEC_PER_SEC/pdata->clk_freq) + 1; dev->pdata = pdata; dev->clk_ctl = 0; dev->pos = 0; ret = i2c_qup_clk_path_init(pdev, dev); if (ret) { dev_err(&pdev->dev, "Failed to init clock path-voting data structs. err:%d", ret); /* disable i2c_qup_clk_path_xxx() functionality */ dev->pdata->master_id = 0; } if (dev->pdata->src_clk_rate <= 0) { dev_info(&pdev->dev, "No src_clk_rate specified in platfrom data\n"); dev_info(&pdev->dev, "Using default clock rate %dHz\n", DEFAULT_CLK_RATE); dev->pdata->src_clk_rate = DEFAULT_CLK_RATE; } ret = clk_set_rate(dev->clk, dev->pdata->src_clk_rate); if (ret) dev_info(&pdev->dev, "clk_set_rate(core_clk, %dHz):%d\n", dev->pdata->src_clk_rate, ret); i2c_qup_clk_prepare_enable(dev, dev->clk); i2c_qup_clk_prepare_enable(dev, dev->pclk); /* * If bootloaders leave a pending interrupt on certain GSBI's, * then we reset the core before registering for interrupts. */ writel_relaxed(1, dev->base + QUP_SW_RESET); if (qup_i2c_poll_state(dev, 0, true) != 0) goto err_reset_failed; clk_disable_unprepare(dev->clk); clk_disable_unprepare(dev->pclk); /* * We use num_irqs to also indicate if we got 3 interrupts or just 1. * If we have just 1, we use err_irq as the general purpose irq * and handle the changes in ISR accordingly * Per Hardware guidelines, if we have 3 interrupts, they are always * edge triggering, and if we have 1, it's always level-triggering */ if (dev->num_irqs == 3) { ret = request_irq(dev->in_irq, qup_i2c_interrupt, IRQF_TRIGGER_RISING, "qup_in_intr", dev); if (ret) { dev_err(&pdev->dev, "request_in_irq failed\n"); goto err_request_irq_failed; } /* * We assume out_irq exists if in_irq does since platform * configuration either has 3 interrupts assigned to QUP or 1 */ ret = request_irq(dev->out_irq, qup_i2c_interrupt, IRQF_TRIGGER_RISING, "qup_out_intr", dev); if (ret) { dev_err(&pdev->dev, "request_out_irq failed\n"); free_irq(dev->in_irq, dev); goto err_request_irq_failed; } ret = request_irq(dev->err_irq, qup_i2c_interrupt, IRQF_TRIGGER_RISING, "qup_err_intr", dev); if (ret) { dev_err(&pdev->dev, "request_err_irq failed\n"); free_irq(dev->out_irq, dev); free_irq(dev->in_irq, dev); goto err_request_irq_failed; } } else { ret = request_irq(dev->err_irq, qup_i2c_interrupt, IRQF_TRIGGER_HIGH, "qup_err_intr", dev); if (ret) { dev_err(&pdev->dev, "request_err_irq failed\n"); goto err_request_irq_failed; } } disable_irq(dev->err_irq); if (dev->num_irqs == 3) { disable_irq(dev->in_irq); disable_irq(dev->out_irq); } /*把该dev挂载到dev->adapter的私有数据结构的driver data上*/ i2c_set_adapdata(&dev->adapter, dev) /*设置该adapter的algo算法结构*/ 其中该结构的初始化实例如下: static const struct i2c_algorithm qup_i2c_algo = { .master_xfer = qup_i2c_xfer,//这是具体的控制器传输方法 .functionality = qup_i2c_func,//返回该I2C总线所支持的功能 }; dev->adapter.algo = &qup_i2c_algo; /*为该adapter的name赋值*/ strlcpy(dev->adapter.name, "QUP I2C adapter", sizeof(dev->adapter.name)); /*为该adapter的编号赋值*/ dev->adapter.nr = pdev->id; dev->adapter.dev.parent = &pdev->dev; if (pdata->msm_i2c_config_gpio) /*为该控制器的配置gpio*/ pdata->msm_i2c_config_gpio(dev->adapter.nr, 1); mutex_init(&dev->mlock); /*初始化dev的部分变量*/ dev->pwr_state = MSM_I2C_PM_SUSPENDED; atomic_set(&dev->xfer_progress, 0); /* If the same AHB clock is used on Modem side * switch it on here itself and don't switch it * on and off during suspend and resume. */ if (dev->pdata->keep_ahb_clk_on) i2c_qup_clk_prepare_enable(dev, dev->pclk); pm_runtime_set_autosuspend_delay(&pdev->dev, MSEC_PER_SEC); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_enable(&pdev->dev); /*把该adapter加入到系统中*/ ret = i2c_add_numbered_adapter(&dev->adapter); if (ret) { dev_err(&pdev->dev, "i2c_add_adapter failed\n"); if (dev->num_irqs == 3) { free_irq(dev->out_irq, dev); free_irq(dev->in_irq, dev); } free_irq(dev->err_irq, dev); } else { if (dev->dev->of_node) { dev->adapter.dev.of_node = pdev->dev.of_node; of_i2c_register_devices(&dev->adapter); } return 0; } err_request_irq_failed: if (dev->gsbi) iounmap(dev->gsbi); err_reset_failed: clk_disable_unprepare(dev->clk); clk_disable_unprepare(dev->pclk); i2c_qup_clk_path_teardown(dev); err_gsbi_failed: iounmap(dev->base); err_ioremap_failed: kfree(dev); err_alloc_dev_failed: err_config_failed: clk_put(clk); clk_put(pclk); err_clk_get_failed: if (gsbi_mem) release_mem_region(gsbi_mem->start, resource_size(gsbi_mem)); err_res_failed: release_mem_region(qup_mem->start, resource_size(qup_mem)); get_res_failed: if (pdev->dev.of_node) devm_kfree(&pdev->dev, pdata); return ret; } 初始化dev,并把把该dev挂载到dev->adapter的私有数据结构的driver data上,然后设置adapter,把该adapter加入到系统中。 下面这个函数是用静态的bus number来向系统增加一个adapter。在kernel中提供了两个adapter注册接口,分别为i2c_add_adapter()和i2c_add_numbered_adapter().由于在系统中可能存在多个adapter,因为将每一条I2C总线对应一个编号,下文中称为I2C总线号。对于i2c_add_adapter()而言,它使用的是动态总线号,即由系统给其分配一个总线号,而i2c_add_numbered_adapter()则是自己指定总线号,如果这个总线号非法或者是被占用,就会注册失败。高通的adapter驱动使用了i2c_add_numbered_adapter()注册,总线号最初保存在platform_data中。 int i2c_add_numbered_adapter(struct i2c_adapter *adap) { if (adap->nr == -1) /* -1 means dynamically assign bus id */ return i2c_add_adapter(adap); return __i2c_add_numbered_adapter(adap); } ////////////////////////////////////////////////// int i2c_add_adapter(struct i2c_adapter *adapter) { struct device *dev = &adapter->dev; int id; if (dev->of_node) { //获取节点np对应的aliasid号 id = of_alias_get_id(dev->of_node, "i2c");//获取节点np对应的aliasid号 if (id >= 0) { adapter->nr = id; return __i2c_add_numbered_adapter(adapter); } } mutex_lock(&core_lock); id = idr_alloc(&i2c_adapter_idr, adapter, __i2c_first_dynamic_bus_num, 0, GFP_KERNEL); mutex_unlock(&core_lock); if (id < 0) return id; adapter->nr = id; return i2c_register_adapter(adapter); } /////////////////////////////////////////////////////////////// static int i2c_register_adapter(struct i2c_adapter *adap) { int res = 0; /* 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; /*初始化adapter dev成员,然后注册该dev*/ dev_set_name(&adap->dev, "i2c-%d", adap->nr); adap->dev.bus = &i2c_bus_type; //这个i2c_bus_type包含了i2c_device_match 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 /* bus recovery specific initialization */ if (adap->bus_recovery_info) { struct i2c_bus_recovery_info *bri = adap->bus_recovery_info; if (!bri->recover_bus) { dev_err(&adap->dev, "No recover_bus() found, not using recovery\n"); adap->bus_recovery_info = NULL; goto exit_recovery; } /* Generic GPIO recovery */ if (bri->recover_bus == i2c_generic_gpio_recovery) { if (!gpio_is_valid(bri->scl_gpio)) { dev_err(&adap->dev, "Invalid SCL gpio, not using recovery\n"); adap->bus_recovery_info = NULL; goto exit_recovery; } if (gpio_is_valid(bri->sda_gpio)) bri->get_sda = get_sda_gpio_value; else bri->get_sda = NULL; bri->get_scl = get_scl_gpio_value; bri->set_scl = set_scl_gpio_value; } else if (!bri->set_scl || !bri->get_scl) { /* Generic SCL recovery */ dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n"); adap->bus_recovery_info = NULL; } } exit_recovery: /* create pre-declared device nodes */ /*扫描属于该总线的板级的i2C设备*/ if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); /* Notify drivers */ mutex_lock(&core_lock); /*探测总线上的所有i2c设备驱动,同时完成client、driver、device、adapter的绑定*/ bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter); /*下面就是 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);//找到对应的driver 然后跑probe函数 klist_iter_exit(&i); return error; } */ mutex_unlock(&core_lock); return 0; out_list: mutex_lock(&core_lock); idr_remove(&i2c_adapter_idr, adap->nr); mutex_unlock(&core_lock); return res; } /////////////////////////////////////////////////////////////////////////// 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-boardinfo.c中,i2c_register_board_info()函数的for循环中,首先会申请I2C设备信息结构体,如果申请成功,将I2C总线号和设备信息赋值给设备信息结构体,并且将设备信息结构体的链表插入到__i2c_board_list中,此处尤为重要, 此函数就是通过__i2c_board_list链表找到上面注册的设备信息,结合gsc3280_i2c_devices_init()函数和i2c_devices_info结构体,此处for循环的len为3,即正常情况下需要创建三个devinfo结构体,for循环结束后,__i2c_board_list链表中也就有了三个I2C设备的链表项,在程序的其他地方如果需要使用这里注册的设备结构信息,只需要遍历链表__i2c_board_list,通过总线号即可找到相应的设备信息。 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)) //This returns the new i2c client在这里完成了kobj的注册 dev_err(&adapter->dev, "Can't create device at 0x%02x\n", devinfo->board_info.addr); } up_read(&__i2c_board_lock); } /////////////////////////////////////////////////////////////////// 到此位置,I2C总线驱动,I2C设备的注册和相应结构体的申请就已经完成了,接下来看下常用的I2C数据传输函数,I2C设备驱动主要调用这些数据传输接口完成数据的传输。
至于如果跑到match
首先看
#define module_i2c_driver(__i2c_driver) \ module_driver(__i2c_driver, i2c_add_driver, \ i2c_del_driver)
之后是
#define i2c_add_driver(driver) \ 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 */ i2c_for_each_dev(driver, __process_new_driver); return 0; }
然后
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); 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; } kobject_uevent(&drv->p->kobj, KOBJ_ADD); return ret; }
接着
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; klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers); if (drv->bus->p->drivers_autoprobe) { error = driver_attach(drv); if (error) goto out_unregister; } 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); } } 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); }这下熟悉了把
int bus_for_each_dev(struct bus_type *bus, struct device *start, void *data, int (*fn)(struct device *, void *)) { struct klist_iter i; struct device *dev; int error = 0; if (!bus || !bus->p) return -EINVAL; klist_iter_init_node(&bus->p->klist_devices, &i, (start ? &start->p->knode_bus : NULL)); while ((dev = next_device(&i)) && !error) error = fn(dev, data); klist_iter_exit(&i); return error; }
struct i2c_client { unsigned short flags; //I2C_CLIENT_TEN表示设备使用10bit从地址,I2C_CLIENT_PEC表示设备使用SMBus检错 unsigned short addr; //设备从地址,7bit。这里说一下为什么是7位,因为最后以为0表示写,1表示读,通过对这个7bit地址移位处理即可。addr<<1 & 0x0即写,addr<<1 | 0x01即读。 char name[I2C_NAME_SIZE]; //从设备名称 struct i2c_adapter *adapter; //此从设备依附于哪个adapter上 struct i2c_driver *driver; // 此设备对应的I2C驱动指针 struct device dev; // 设备模型 int irq; // 设备使用的中断号 struct list_head detected; //用于链表操作 }; #define to_i2c_client(d) container_of(d, struct i2c_client, dev) //通常使用device设备模型进行操作,可以通过to_i2c_client找到对应client指针 struct i2c_board_info { char type[I2C_NAME_SIZE]; //设备名,最长20个字符,最终安装到client的name上 unsigned short flags; //最终安装到client.flags unsigned short addr; //设备从地址slave address,最终安装到client.addr上 void *platform_data; //设备数据,最终存储到i2c_client.dev.platform_data上 struct dev_archdata *archdata; struct device_node *of_node; //OpenFirmware设备节点指针 struct acpi_dev_node acpi_node; int irq; //设备采用的中断号,最终存储到i2c_client.irq上 }; //可以看到,i2c_board_info基本是与i2c_client对应的。 #define I2C_BOARD_INFO(dev_type, dev_addr) \ .type = dev_type, .addr = (dev_addr) //通过这个宏定义可以方便的定义I2C设备的名称和从地址(别忘了是7bit的)
相关文章推荐
- 记一次qcom平台i2c和uart配置问题导致的系统重启
- 高通平台Camera框架部分浅谈--Camera C/S 的init流程
- android下面通过I2C TOOLS工具查看设备寄存器(S5PV210平台)
- 展讯智能机平台代码 i2c设备驱动解读(下)
- [转]SkyEye硬件模拟平台,第三部分: 硬件仿真实现之四
- X5平台开发的 团队看板 部分代码
- 浅析Linux初始化(init)系统,第 1 部分: sysvinit
- 【转】Android 平台下使用 i2c-tools
- I2C-Tools 4.0使用说明及android平台移植,提供源码下载路径
- i2c子系统之platform_driver初始化——i2c_adap_s3c_init()
- Qcom平台控制backlight亮度
- 浅析 Linux 初始化 init 系统,第 2 部分: UpStart
- 浅析 Linux 初始化 init 系统,第 1 部分: sysvinit 第 2 部分: UpStart 第 3 部分: Systemd
- Linux2.6.25平台下的I2C驱动架构分析
- x86平台下 Android 系统的 Linux 部分的重启分析
- SkyEye硬件模拟平台,第三部分: 硬件仿真实现之五
- QCom MSM平台显示屏Framebuffer设备注册过程
- 【工业串口和网络软件通讯平台(SuperIO)教程】二.架构和组成部分
- 关于高通8053平台i2c和spi配置的学习总结
- 一种新的基于Windows Mobile平台的移动开发N层架构(第八部分)