/*******************************************************************************************
* I.MX6 Linux I2C device& driver hacking
* 声明:
* 1. 本文主要是对Linux I2C驱动进行代码跟踪,主要是为了能够对I2C驱动框架有个全面的了解;
* 2. 本文源代码来自myzr_android4_2_2_1_1_0.tar.bz2;
* 3. 如果你有兴趣,请尽量自己去对代码进行跟踪,这样自己会对I2C有一个框架结构上的理解;
*
* 2015-6-4 晴 深圳 南山平山村 曾剑锋
******************************************************************************************/ \\\\\\\\\\\\\\\\\-*- 目录 -*-/////////////////
| 一、跟踪板级文件:
| 二、跟踪mx6q_sabresd_i2c_data参数:
| 三、跟踪imx6q_add_imx_i2c()函数:
| 四、跟踪imx6q_imx_i2c_data参数:
| 五、跟踪imx_add_imx_i2c()函数:
| 六、跟踪mxc_i2c0_board_info参数:
| 七、跟踪i2c_register_board_info()函数:
| 八、I2C adapter(适配器)跟踪:
| 九、I2C设备追踪:
| 十、跟踪__process_new_driver参数:
| 十一、跟踪i2c_for_each_dev()函数:
| 十二、跟踪max6875_probe()函数:
\\\\\\\\\\\\\\\\\\\\\\\/////////////////////// 一、跟踪板级文件:
. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
......
/*
* initialize __mach_desc_MX6Q_SABRESD data structure.
*/
MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.boot_params = MX6_PHYS_OFFSET + 0x100,
.fixup = fixup_mxc_board,
.map_io = mx6_map_io,
.init_irq = mx6_init_irq,
.init_machine = mx6_sabresd_board_init, //跟踪板级初始化函数
.timer = &mx6_sabresd_timer,
.reserve = mx6q_sabresd_reserve,
MACHINE_END . cat arch/arm/mach-mx6/board-mx6q_sabresd.c
......
/*!
* Board specific initialization.
*/
static void __init mx6_sabresd_board_init(void)
{
......
/**
* 接下来我们需要向以下4个方向去跟踪代码:
* 1. 跟踪mx6q_sabresd_i2c_data参数;
* 2. 跟踪imx6q_add_imx_i2c()函数;
* 3. 跟踪mxc_i2c0_board_info参数,在里面继续跟踪wm8962_config_data,
* 因为下面这两行代码修改了mxc_i2c0_board_info第一个元素;
* 4. 跟踪i2c_register_board_info()函数;
*/
strcpy(mxc_i2c0_board_info[].type, "wm8962");
mxc_i2c0_board_info[].platform_data = &wm8962_config_data; //跟踪wm8962_config_data /**
* 这里相当于注册I2C控制器
*/
imx6q_add_imx_i2c(, &mx6q_sabresd_i2c_data); //跟踪参数,函数
imx6q_add_imx_i2c(, &mx6q_sabresd_i2c_data);
imx6q_add_imx_i2c(, &mx6q_sabresd_i2c_data); /**
* 这里相当于注册I2C设备
*/
i2c_register_board_info(, mxc_i2c0_board_info, //跟踪参数,函数
ARRAY_SIZE(mxc_i2c0_board_info));
i2c_register_board_info(, mxc_i2c1_board_info,
ARRAY_SIZE(mxc_i2c1_board_info));
i2c_register_board_info(, mxc_i2c2_board_info,
ARRAY_SIZE(mxc_i2c2_board_info));
......
}
...... 二、跟踪mx6q_sabresd_i2c_data参数:
. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
......
static struct imxi2c_platform_data mx6q_sabresd_i2c_data = { //跟踪结构体
.bitrate = ,
};
...... . cat arch/arm/plat-mxc/include/mach/i2c.h
......
struct imxi2c_platform_data {
int (*init)(struct device *dev);
void (*exit)(struct device *dev);
int bitrate;
};
...... 三、跟踪imx6q_add_imx_i2c()函数:
cat arch/arm/mach-mx6/devices-imx6q.h
......
/**
* 这里需要跟踪2个方向:
* 1. 跟踪imx6q_imx_i2c_data参数;
* 2. 跟踪imx_add_imx_i2c()函数;
*/
extern const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst;
#define imx6q_add_imx_i2c(id, pdata) \
imx_add_imx_i2c(&imx6q_imx_i2c_data[id], pdata) //跟踪结构体,函数
...... 四、跟踪imx6q_imx_i2c_data参数:
. cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
......
#ifdef CONFIG_SOC_IMX6Q
const struct imx_imx_i2c_data imx6q_imx_i2c_data[] __initconst = {
#define imx6q_imx_i2c_data_entry(_id, _hwid) \
imx_imx_i2c_data_entry(MX6Q, _id, _hwid, SZ_4K)
imx6q_imx_i2c_data_entry(, ), //跟踪这个宏
imx6q_imx_i2c_data_entry(, ),
imx6q_imx_i2c_data_entry(, ),
};
#endif /* ifdef CONFIG_SOC_IMX6Q */
...... . cat arch/arm/plat-mxc/include/mach/devices-common.h
......
#include <mach/i2c.h>
struct imx_imx_i2c_data {
int id;
resource_size_t iobase;
resource_size_t iosize;
resource_size_t irq;
};
...... . cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
......
/**
* 1. 如果传入参数是:
* 1. soc = MX6Q;
* 2. _id = 0;
* 3. _hwid = 1;
* 4. size = SZ_4K;
* 2. .iobase = soc ## _I2C ## _hwid ## _BASE_ADDR合成结果:
* .iobase = MX6Q_I2C1_BASE_ADDR = 0x021A0000 (看后面的跟踪代码推演计算)
* 3. .irq = soc ## _INT_I2C ## _hwid合成结果:
* .irq = MX6Q_INT_I2C1 = 68
*/
#define imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size) \
{ \
.id = _id, \
.iobase = soc ## _I2C ## _hwid ## _BASE_ADDR, \ //跟踪合成后的宏
.iosize = _size, \
.irq = soc ## _INT_I2C ## _hwid, \
} #define imx_imx_i2c_data_entry(soc, _id, _hwid, _size) \
[_id] = imx_imx_i2c_data_entry_single(soc, _id, _hwid, _size)
...... . cat arch/arm/plat-mxc/include/mach/mx6.h
......
/**
* 1. I2C1首地址推演计算:
* MX6Q_I2C1_BASE_ADDR = (AIPS2_OFF_BASE_ADDR + 0x20000)
* MX6Q_I2C1_BASE_ADDR = ((ATZ2_BASE_ADDR + 0x80000) + 0x20000)
* MX6Q_I2C1_BASE_ADDR = ((AIPS2_ARB_BASE_ADDR + 0x80000) + 0x20000)
* MX6Q_I2C1_BASE_ADDR = ((0x02100000 + 0x80000) + 0x20000)
* MX6Q_I2C1_BASE_ADDR = (0x02100000 + 0x80000 + 0x20000)
* MX6Q_I2C1_BASE_ADDR = (0x02100000 + 0x80000 + 0x20000)
* MX6Q_I2C1_BASE_ADDR = 0x021A0000 (符合下面参考书给出的首地址)
* 2. 参考书IMX6DQRM_revC.pdf给出的I2C1的首地址:
* -------------------------------------------------------------
* | Start Address | End Address | Region | Allocation | Size |
* +---------------+-------------+--------+-------------+------+
* | 021A_0000 | 021A_3FFF | AIPS-2 | I2C1 | 16KB |
* -------------------------------------------------------------
*/
#define MX6Q_I2C1_BASE_ADDR (AIPS2_OFF_BASE_ADDR + 0x20000)
......
/* ATZ#2- Off Platform */
#define AIPS2_OFF_BASE_ADDR (ATZ2_BASE_ADDR + 0x80000)
......
#define ATZ2_BASE_ADDR AIPS2_ARB_BASE_ADDR
......
#define AIPS2_ARB_BASE_ADDR 0x02100000
...... . cat arch/arm/plat-mxc/include/mach/mx6.h
......
/**
* 1. 参考书IMX6DQRM_revC.pdf给出的I2C1的中断号:
* ---------------------------------------------
* |IRQ | Interrupt | Interrupt Description |
* | | Source | |
* +----+------------+-------------------------+
* |68 | ECSPI1I2C1 | I2C1 interrupt request. |
* ---------------------------------------------
* 2. 非常精准的符合参好书 :)
*/
#define MX6Q_INT_I2C1 68
...... 五、跟踪imx_add_imx_i2c()函数:
. cat arch/arm/plat-mxc/devices/platform-imx-i2c.c
......
struct platform_device *__init imx_add_imx_i2c(
const struct imx_imx_i2c_data *data,
const struct imxi2c_platform_data *pdata)
{
//利用传入参数合成平台资源数据
struct resource res[] = {
{
.start = data->iobase,
.end = data->iobase + data->iosize - ,
.flags = IORESOURCE_MEM,
}, {
.start = data->irq,
.end = data->irq,
.flags = IORESOURCE_IRQ,
},
}; /**
* 从这里可以知道设备匹配时候的名字,跟踪该函数
*/
return imx_add_platform_device("imx-i2c", data->id,
res, ARRAY_SIZE(res),
pdata, sizeof(*pdata));
}
...... . cat arch/arm/plat-mxc/include/mach/devices-common.h
......
static inline struct platform_device *imx_add_platform_device(
const char *name, int id,
const struct resource *res, unsigned int num_resources,
const void *data, size_t size_data)
{
//跟踪该函数
return imx_add_platform_device_dmamask(
name, id, res, num_resources, data, size_data, );
}
...... . cat arch/arm/plat-mxc/devices.c
......
struct platform_device *__init imx_add_platform_device_dmamask(
const char *name, int id,
const struct resource *res, unsigned int num_resources,
const void *data, size_t size_data, u64 dmamask)
{
int ret = -ENOMEM;
struct platform_device *pdev; pdev = platform_device_alloc(name, id);
if (!pdev)
goto err;
......
if (res) {
ret = platform_device_add_resources(pdev, res, num_resources);
if (ret)
goto err;
} if (data) {
ret = platform_device_add_data(pdev, data, size_data);
if (ret)
goto err;
} ret = platform_device_add(pdev); //设备注册
if (ret) {
err:
if (dmamask)
kfree(pdev->dev.dma_mask);
platform_device_put(pdev);
return ERR_PTR(ret);
} return pdev;
}
...... 六、跟踪mxc_i2c0_board_info参数:
. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
......
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = { //跟踪结构体
/**
* 这是板级初始化函数中的另一部分:
* strcpy(mxc_i2c0_board_info[0].type, "wm8962");
* mxc_i2c0_board_info[0].platform_data = &wm8962_config_data; //跟踪目标wm8962_config_data
*
* 通过这里可以知道wm8962的I2C地址是:0x1a
*/
{
I2C_BOARD_INFO("wm89**", 0x1a),
},
{
I2C_BOARD_INFO("ov564x", 0x3c),
.platform_data = (void *)&camera_data,
},
{
I2C_BOARD_INFO("mma8451", 0x1d),
.platform_data = (void *)&mma8451_position,
},
{
I2C_BOARD_INFO("isl1208", 0x6f),
},
};
...... . cat include/linux/i2c.h
......
struct i2c_board_info {
char type[I2C_NAME_SIZE];
unsigned short flags;
unsigned short addr;
void *platform_data;
struct dev_archdata *archdata;
struct device_node *of_node;
int irq;
};
...... . cat arch/arm/mach-mx6/board-mx6q_sabresd.c
......
static struct wm8962_pdata wm8962_config_data = { //跟踪结构体
.gpio_init = {
[] = WM8962_GPIO_FN_DMICCLK,
[] = 0x8000 | WM8962_GPIO_FN_DMICDAT,
},
};
...... . cat include/sound/wm8962.h
......
struct wm8962_pdata {
int gpio_base;
u32 gpio_init[WM8962_MAX_GPIO]; /* Setup for microphone detection, raw value to be written to
* R48(0x30) - only microphone related bits will be updated.
* Detection may be enabled here for use with signals brought
* out on the GPIOs. */
u32 mic_cfg; bool irq_active_low; bool spk_mono; /* Speaker outputs tied together as mono */
};
...... 七、跟踪i2c_register_board_info()函数:
. cat 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 assigned after the last static one */
if (busnum >= __i2c_first_dynamic_bus_num)
__i2c_first_dynamic_bus_num = busnum + ; for (status = ; 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); //跟踪__i2c_board_list
} up_write(&__i2c_board_lock); return status;
}
...... . cat drivers/i2c/i2c-boardinfo.c
......
LIST_HEAD(__i2c_board_list); 一个公用的I2C链表头
EXPORT_SYMBOL_GPL(__i2c_board_list);
...... 八、I2C adapter(适配器)跟踪:
. cat drivers/i2c/busses/i2c-imx.c
......
/* This will be the driver name the kernel reports */
#define DRIVER_NAME "imx-i2c" //adapter和I2C控制器匹配的名字 /* Default value */
#define IMX_I2C_BIT_RATE 100000 /* 100kHz */ static struct platform_driver i2c_imx_driver = {
.remove = __exit_p(i2c_imx_remove),
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
}
}; static int __init i2c_adap_imx_init(void)
{
return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe); //跟踪函数,参数
}
subsys_initcall(i2c_adap_imx_init); static void __exit i2c_adap_imx_exit(void)
{
platform_driver_unregister(&i2c_imx_driver);
}
module_exit(i2c_adap_imx_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Darius Augulis");
MODULE_DESCRIPTION("I2C adapter driver for IMX I2C bus"); //这里说明了这个驱动的作用
MODULE_ALIAS("platform:" DRIVER_NAME); . cat drivers/base/platform.c
......
int __init_or_module platform_driver_probe(struct platform_driver *drv,
int (*probe)(struct platform_device *))
{
int retval, code; /* make sure driver won't have bind/unbind attributes */
drv->driver.suppress_bind_attrs = true; /* temporary section violation during probe() */
/**
* 主要注意下面这行代码
*/
drv->probe = probe;
retval = code = platform_driver_register(drv); /*
* Fixup that section violation, being paranoid about code scanning
* the list of drivers in order to probe new devices. Check to see
* if the probe was successful, and make sure any forced probes of
* new devices fail.
*/
spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);
drv->probe = NULL;
if (code == && list_empty(&drv->driver.p->klist_devices.k_list))
retval = -ENODEV;
drv->driver.probe = platform_drv_probe_fail;
spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock); if (code != retval)
platform_driver_unregister(drv);
return retval;
}
EXPORT_SYMBOL_GPL(platform_driver_probe);
...... . cat drivers/i2c/busses/i2c-imx.c
......
static int __init i2c_imx_probe(struct platform_device *pdev)
{
struct imx_i2c_struct *i2c_imx;
struct resource *res;
struct imxi2c_platform_data *pdata;
void __iomem *base;
resource_size_t res_size;
int irq;
int ret; dev_dbg(&pdev->dev, "<%s>\n", __func__); res = platform_get_resource(pdev, IORESOURCE_MEM, );
if (!res) {
dev_err(&pdev->dev, "can't get device resources\n");
return -ENOENT;
}
irq = platform_get_irq(pdev, );
if (irq < ) {
dev_err(&pdev->dev, "can't get irq number\n");
return -ENOENT;
} pdata = pdev->dev.platform_data; if (pdata && pdata->init) {
ret = pdata->init(&pdev->dev);
if (ret)
return ret;
} res_size = resource_size(res); if (!request_mem_region(res->start, res_size, DRIVER_NAME)) {
ret = -EBUSY;
goto fail0;
} base = ioremap(res->start, res_size);
if (!base) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -EIO;
goto fail1;
} i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL);
if (!i2c_imx) {
dev_err(&pdev->dev, "can't allocate interface\n");
ret = -ENOMEM;
goto fail2;
} /* Setup i2c_imx driver structure */
strcpy(i2c_imx->adapter.name, pdev->name);
i2c_imx->adapter.owner = THIS_MODULE;
i2c_imx->adapter.algo = &i2c_imx_algo;
i2c_imx->adapter.dev.parent = &pdev->dev;
i2c_imx->adapter.nr = pdev->id;
i2c_imx->irq = irq;
i2c_imx->base = base;
i2c_imx->res = res; /* Get I2C clock */
i2c_imx->clk = clk_get(&pdev->dev, "i2c_clk");
if (IS_ERR(i2c_imx->clk)) {
ret = PTR_ERR(i2c_imx->clk);
dev_err(&pdev->dev, "can't get I2C clock\n");
goto fail3;
} /* Request IRQ */
ret = request_irq(i2c_imx->irq, i2c_imx_isr, , pdev->name, i2c_imx);
if (ret) {
dev_err(&pdev->dev, "can't claim irq %d\n", i2c_imx->irq);
goto fail4;
} /* Init queue */
init_waitqueue_head(&i2c_imx->queue); /* Set up adapter data */
i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); /* Set up clock divider */
if (pdata && pdata->bitrate)
i2c_imx_set_clk(i2c_imx, pdata->bitrate);
else
i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE); /* Set up chip registers to defaults */
writeb(, i2c_imx->base + IMX_I2C_I2CR);
writeb(, i2c_imx->base + IMX_I2C_I2SR); /* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
if (ret < ) {
dev_err(&pdev->dev, "registration failed\n");
goto fail5;
} /* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx); dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", i2c_imx->irq);
dev_dbg(&i2c_imx->adapter.dev, "device resources from 0x%x to 0x%x\n",
i2c_imx->res->start, i2c_imx->res->end);
dev_dbg(&i2c_imx->adapter.dev, "allocated %d bytes at 0x%x \n",
res_size, i2c_imx->res->start);
dev_dbg(&i2c_imx->adapter.dev, "adapter name: \"%s\"\n",
i2c_imx->adapter.name);
dev_dbg(&i2c_imx->adapter.dev, "IMX I2C adapter registered\n"); return ; /* Return OK */ fail5:
free_irq(i2c_imx->irq, i2c_imx);
fail4:
clk_put(i2c_imx->clk);
fail3:
kfree(i2c_imx);
fail2:
iounmap(base);
fail1:
release_mem_region(res->start, resource_size(res));
fail0:
if (pdata && pdata->exit)
pdata->exit(&pdev->dev);
return ret; /* Return error number */
}
...... 九、I2C设备追踪:
. cat drivers/misc/eeprom/max6875.c
......
static const struct i2c_device_id max6875_id[] = {
{ "max6875", },
{ }
}; static struct i2c_driver max6875_driver = {
.driver = {
.name = "max6875",
},
.probe = max6875_probe, //跟踪函数
.remove = max6875_remove,
.id_table = max6875_id,
}; static int __init max6875_init(void)
{
return i2c_add_driver(&max6875_driver); //跟踪函数
} static void __exit max6875_exit(void)
{
i2c_del_driver(&max6875_driver);
} MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
MODULE_DESCRIPTION("MAX6875 driver");
MODULE_LICENSE("GPL"); module_init(max6875_init);
module_exit(max6875_exit); . cat include/linux/i2c.h
......
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver); //跟踪函数
}
...... . cat drivers/i2c/i2c-core.c
......
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
......
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); //这里就跟踪到这里结束了 ...... /* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver); //跟踪这个函数 return ;
}
EXPORT_SYMBOL(i2c_register_driver);
...... . cat drivers/i2c/i2c-core.c
......
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);
...... 十、跟踪__process_new_driver参数:
. cat drivers/i2c/i2c-core.c
......
static int __process_new_driver(struct device *dev, void *data)
{
if (dev->type != &i2c_adapter_type)
return ;
//跟踪to_i2c_adapter参数,部队i2c_do_add_adapter()函数进行跟踪,到这里结束
return i2c_do_add_adapter(data, to_i2c_adapter(dev));
}
...... . cat include/linux/i2c.h
......
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
...... 十一、跟踪i2c_for_each_dev()函数:
. cat drivers/i2c/i2c-core.c
......
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
int res; mutex_lock(&core_lock);
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn); //跟踪函数
mutex_unlock(&core_lock); return res;
}
EXPORT_SYMBOL_GPL(i2c_for_each_dev);
...... . cat drivers/base/bus.c
......
/**
* 这一部分只跟踪到这里,因为从函数名,你可以知道没必要再跟下去了
*/
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
/**
*
* struct bus_type {
* ......
*
* struct subsys_private *p;
* };
*
* struct subsys_private {
* ......
* struct klist klist_devices;
* struct klist klist_drivers;
* ......
* };
*
* struct device {
* ......
* struct device_private *p;
* ......
* };
*
* struct device_private {
* ......
* struct klist_node knode_bus;
* void *driver_data;
* struct device *device;
* };
*/
struct klist_iter i;
struct device *dev;
...... 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;
}
EXPORT_SYMBOL_GPL(bus_for_each_dev);
...... 十二、跟踪max6875_probe()函数:
cat drivers/misc/eeprom/max6875.c
......
static int max6875_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = client->adapter;
struct max6875_data *data;
int err; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE_DATA
| I2C_FUNC_SMBUS_READ_BYTE))
return -ENODEV; /* Only bind to even addresses */
if (client->addr & )
return -ENODEV; if (!(data = kzalloc(sizeof(struct max6875_data), GFP_KERNEL)))
return -ENOMEM; /* A fake client is created on the odd address */
data->fake_client = i2c_new_dummy(client->adapter, client->addr + );
if (!data->fake_client) {
err = -ENOMEM;
goto exit_kfree;
} /* Init real i2c_client */
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock); err = sysfs_create_bin_file(&client->dev.kobj, &user_eeprom_attr);
if (err)
goto exit_remove_fake; return ; exit_remove_fake:
i2c_unregister_device(data->fake_client);
exit_kfree:
kfree(data);
return err;
}

I.MX6 Linux I2C device& driver hacking的更多相关文章

  1. I.MX6 ar1020 SPI device driver hacking

    /************************************************************************************ * I.MX6 ar1020 ...

  2. OK335xS I2C device registe hacking

    /*************************************************************************** * OK335xS I2C device re ...

  3. I.MX6 Linux Serial Baud Rate hacking

    /******************************************************************************** * I.MX6 Linux Seri ...

  4. [platform]linux platform device/driver(三)--Platform Device和Platform_driver注册过程之代码对比

    转自:http://blog.csdn.net/thl789/article/details/6723350 Linux 2.6的设备驱动模型中,所有的device都是通过Bus相连.device_r ...

  5. [platform]linux platform device/driver(二)--Platform Device和Platform_driver注册过程之详细代码

    转自:http://www.cnblogs.com/haimeng2010/p/3582403.html 目录: 1.platform_device注册过程 2.platform_driver注册过程 ...

  6. [platform]linux platform device/driver(一)--Driver是如何找到对应的device

    1.platform device是怎么"自动"关联到platform driver上的? 转向linux driver有些时间了,前段时间碰到个问题,在Linux kernel ...

  7. Linux I2C驱动

    Linux I2C 驱动结构 i2c体系结构由三部分组成 i2C core i2c core提供了i2c 总线驱动 和 设备驱动的注册,注销方法 i2C and SMBus protocol 实现 i ...

  8. I.MX6 AD7606-4 device driver registe hacking

    /********************************************************************** * I.MX6 AD7606-4 device driv ...

  9. I.MX6 bq27441 driver hacking

    /************************************************************************* * I.MX6 bq27441 driver ha ...

随机推荐

  1. python 插值查找

    def binary_search(lis, key): low = 0 high = len(lis) - 1 time = 0 while low < high: time += 1 # m ...

  2. TTFB

    TTFB    Time To First Byte 1.含义 最初的网络请求被发起”到“从服务器接收到第一个字节前”所花费的毫秒数 包含了 TCP连接时间,发送HTTP请求时间和获得响应消息第一个字 ...

  3. 20170813xlVBA跨表筛选数据

    一.数组方案 Sub CustomFilter() Dim Rng As Range, Arr As Variant Dim EndRow As Long, EndCol As Long Dim i ...

  4. ACM-ICPC Beijing Online A The Book List

    比赛的时候一眼就看出是字典树+DFS了,然而这题题意比较难理解,还有不少WA点.所以很快敲完之后和队友反复斟酌题意,修改代码.结果还是交了3发WA.最后猜测目录和书在同一个母目录域下同名是不同的,增加 ...

  5. PHP函数总结 (一)

    <?php /** * 原理: * 函数不调用不执行,定义函数时,会将 * 函数放到内存中代码段,当调用函数时去内存 * 中函数名称所在位置中执行函数体,执行完后 * 将控制权移交回给调用函数的 ...

  6. 在centos7上安装gcc、node.js(源码下载)

    一.在centos7中安装node.js https://www.cnblogs.com/lpbottle/p/7733397.html 1.从源码下载Nodejs cd /usr/local/src ...

  7. Python并行(parallel)之谈

    简介 可以先看看并发Concurrent与并行Parallel的区别 在谈并行前,头脑中总会浮出多线程.多进程.线程/进程同步.线程/进程通信等词语. 那为什么需要同步.通信,它们之间的作用是怎样的呢 ...

  8. OAF 获取页面路径

    --模糊查询某个页面 SELECT * FROM JDR_PATHS jp WHERE JP.PATH_NAME LIKE '%XXXX%'; --精确的查找过程 -- DocID --参数通过关于此 ...

  9. spark submit 入门

    spark dirver本质是一个spark集群的驱动程序,你要调用spark集群的计算功能,必须要通过它! from pyspark import SparkConf, SparkContext c ...

  10. storage路径问题

    1 概念总述 android开发中,关于存储路径,我们经常听到以下几个概念:内存.内部存储和外部存储,现在我们就来详细说说这三者区别与联系. 内存:英文中记为memory,即RAM 内部存储:英文记为 ...