I2C驅動client、driver簡單的匹配流程筆記(以ds1307時鐘芯片參考)

/********先引入client創建過程**************/

static int __init i2c_adap_imx_init(void)
{
return platform_driver_probe(&i2c_imx_driver, i2c_imx_probe);
}

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, 0);
if (!res) {
dev_err(&pdev->dev, "can't get device resources\n");
return -ENOENT;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
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, 0, 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(0, i2c_imx->base + IMX_I2C_I2CR);
writeb(0, i2c_imx->base + IMX_I2C_I2SR);


/* Add I2C adapter */
ret = i2c_add_numbered_adapter(&i2c_imx->adapter);//註冊adapter時會遍歷創建client。
if (ret < 0) {
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 0;   /* 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_add_numbered_adapter - declare i2c adapter, use static bus number
 * @adap: the adapter to register (with adap->nr initialized)
 * Context: can sleep
 *
 * This routine is used to declare an I2C adapter when its bus number
 * matters.  For example, use it for I2C adapters from system-on-chip CPUs,
 * or otherwise built in to the system's mainboard, and where i2c_board_info
 * is used to properly configure I2C devices.
 *
 * If no devices have pre-been declared for this bus, then be sure to
 * register the adapter before any dynamically allocated ones.  Otherwise
 * the required bus ID may not be available.
 *
 * When this returns zero, the specified adapter became available for
 * clients using the bus number provided in adap->nr.  Also, the table
 * of I2C devices pre-declared using i2c_register_board_info() is scanned,
 * and the appropriate driver model device nodes are created.  Otherwise, a
 * negative errno value is returned.
 */
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
int id;
int status;


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);//註冊adapter
return status;
}


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;


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);//開始掃描在放在該I2C BUS上的board info

/*以ds1307爲例:

kernel/arch/arm/mach-mx6/board-mx6q_sabresd.c:

static struct i2c_board_info mxc_i2c1_board_info[] __initdata = {
{
I2C_BOARD_INFO("mxc_hdmi_i2c", 0x50),
},
{
I2C_BOARD_INFO("ov5640_mipi", 0x3c),
.platform_data = (void *)&mipi_csi2_data,
},
{
I2C_BOARD_INFO("egalax_ts", 0x4),
.irq = gpio_to_irq(SABRESD_CAP_TCH_INT0),
},
{
I2C_BOARD_INFO("max11801", 0x48),
.platform_data = (void *)&max11801_mode,
.irq = gpio_to_irq(SABRESD_TS_INT),
},
//{
//I2C_BOARD_INFO("ds1672", 0x68),
//},
{
I2C_BOARD_INFO("ds1307", 0x68),
},
};

凡此類設備便會在註冊adapter的過程中會遍歷創建client

*/


/* Notify drivers */
mutex_lock(&core_lock);
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
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;
}


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, //此便是在遍歷過程中實體創建client的函數 ,由此,client已創建完畢。
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}

/*************************分割線*********************************/
/*************************上爲創建i2c client流程********************/

/*************************下爲註冊i2c driver,並匹配執行驅動*******************/
/*************************分割線*********************************/

static int __init i2c_init(void)
{
int retval;


retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
retval = i2c_add_driver(&dummy_driver);//此便爲實體註冊i2c driver函數,好像這個只是模擬註冊,並沒有實際用途
if (retval)
goto class_err;
return 0;


class_err:
#ifdef CONFIG_I2C_COMPAT
class_compat_unregister(i2c_adapter_compat_class);
bus_err:
#endif
bus_unregister(&i2c_bus_type);
return retval;
}



struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,//註冊i2c driver後,會首先執行該回調函數,對I2C bus list上的driver與client匹配
.probe = i2c_device_probe,//如果driver->id table和client->name匹配,則執行該回調函數。但是在哪裏調用的還沒不清楚,找到的MMM我下,謝謝~~
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.pm = &i2c_device_pm_ops,

};

//接下來看兩個回調函數


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;


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)//這就是了,匹配driver->id_table和client->name,成功則執行probe
return id;
id++;
}
return NULL;
}


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);//獲取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");


status = driver->probe(client, i2c_match_id(driver->id_table, client));//然後在這裏正式開始執行driver->probe 回調函數
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}

//到這裏,註冊了client,driver,並匹配執行probe

/*************************分割線*********************************/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章