注:这是我在自己学习I2C驱动框架时候记录的笔记,不喜勿喷
一、内核中实现I2C驱动框架和一个实例注册相关的文件
1./dricer/i2c/i2c-core.c
2./arch/arm/mach-s3c2440/mach-smdk2440.c
3.driver/i2c/buses/i2c-s3c2410.c
4./driver/i2c/i2c-dev.c
以后分析的代码就来源于这几个文件中
二、几个相关的结构体
1.相关的结构体
(1)struct i2c_adapter 主芯片的I2C适配器
(2)struct i2c_algorthm I2C算法
(3)struct i2c_client I2C(从设备)客户端(信息)
(4)struct i2c_driver I2C(从设备)驱动
struct i2c_adapter {
struct module *owner;
//id号
unsigned int id;
unsigned int class; /* classes to allow probing for */
//I2C通信相关的算法。注意是指针,也就是说一个adapter可以使用多种I2C通信算法
const struct i2c_algorithm *algo; /* the algorithm to access the bus */
//算法相关数据
void *algo_data;
/* data fields that are valid for all devices */
//I2C总线相关的互斥锁
struct rt_mutex bus_lock;
int timeout; /* in jiffies */
//尝试重新连接的发送数据的次数
int retries;
struct device dev; /* the adapter device */
int nr;
//名字
char name[48];
struct completion dev_released;
struct list_head userspace_clients;
};
struct i2c_algorithm {
//发送数据的函数指针
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
/* To determine what the adapter supports */
//决定支持什么类型的adapter的函数指针
u32 (*functionality) (struct i2c_adapter *);
};
struct i2c_client {
unsigned short flags; /* div., see below */
//低7位的I2C设备地址
unsigned short addr;
//名字
char name[I2C_NAME_SIZE];
//相对的adapter
struct i2c_adapter *adapter;
//相对的驱动
struct i2c_driver *driver; /* and our access routines */
//实体I2C设备
struct device dev; /* the device structure */
//中断号
int irq; /* irq issued by device */
struct list_head detected;
};
//i2C相关驱动所需的函数指针的封装
struct i2c_driver {
unsigned int class;
/* Notifies the driver that a new bus has appeared or is about to be
* removed. You should avoid using this if you can, it will probably
* be removed in a near future.
*/
int (*attach_adapter)(struct i2c_adapter *);
int (*detach_adapter)(struct i2c_adapter *);
/* Standard driver model interfaces */
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
/* driver model interfaces that don't relate to enumeration */
void (*shutdown)(struct i2c_client *);
int (*suspend)(struct i2c_client *, pm_message_t mesg);
int (*resume)(struct i2c_client *);
/* Alert callback, for example for the SMBus alert protocol.
* The format and meaning of the data value depends on the protocol.
* For the SMBus alert protocol, there is a single bit of data passed
* as the alert response's low bit ("event flag").
*/
void (*alert)(struct i2c_client *, unsigned int data);
/* a ioctl like command that can be used to perform specific functions
* with the device.
*/
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
struct device_driver driver;
//用来machdevice和driver的idtable
const struct i2c_device_id *id_table;
/* Device detection callback for automatic device creation */
int (*detect)(struct i2c_client *, struct i2c_board_info *);
const unsigned short *address_list;
struct list_head clients;
};
三、I2C框架的注册
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,
};
static int __init i2c_init(void)
{
int retval;
//在BUS总线下注册I2C总线
retval = bus_register(&i2c_bus_type);
if (retval)
return retval;
#ifdef CONFIG_I2C_COMPAT
//在class下面创建"i2c-adapter"目录
i2c_adapter_compat_class = class_compat_register("i2c-adapter");
if (!i2c_adapter_compat_class) {
retval = -ENOMEM;
goto bus_err;
}
#endif
//添加一个dummy_driver,具体没看代码
retval = i2c_add_driver(&dummy_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;
}
框架注册总结:实际在BUS总线下注册了I2C总线,重要的还是2c_bus_type结构体,具体我们看两个重要的函数指针.match和.prob。
//mach函数用来匹配client和driver,具体的匹配方式是通过driver中的idtable和client中的name来匹配的。具体函数看i2c_match_id(driver->id_table, client)
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;
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 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中是否提供prob和id_table
if (!driver->probe || !driver->id_table)
return -ENODEV;
//client的driver和驱动关联
client->driver = driver;
if (!device_can_wakeup(&client->dev))
device_init_wakeup(&client->dev,
client->flags & I2C_CLIENT_WAKE);
dev_dbg(dev, "probe\n");
//调用驱动中写的driver,这个函数是我们自己实现的prob
status = driver->probe(client, i2c_match_id(driver->id_table, client));
if (status) {
client->driver = NULL;
i2c_set_clientdata(client, NULL);
}
return status;
}
注册到这里就完成了,到时候我们自己写的驱动和设备mach之后会自动调用prob函数执行。
i2c-core.c中提供了一些驱动工程师可以调用的接口来帮助写设备驱动,这些接口在用到的时候会列出源代码
四、实例分析
1.我们先看i2c的device的注册
(1)具体的代码在mach-xxx.c里面
这里只列出相关的函数调用(并且只举例了一个i2c0)
smdkc110_machine_init
platform_add_devices(smdkc110_devices, ARRAY_SIZE(smdkc110_devices));
platform_device_register(&s3c_device_i2c0);
s3c_i2c0_set_platdata(NULL);
i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0));
/*******************相关注册device的结构体**********************/
static struct s3c2410_platform_i2c default_i2c_data0 __initdata = {
.flags = 0,
.slave_addr = 0x10,
.frequency = 400*1000,
.sda_delay = S3C2410_IICLC_SDA_DELAY15 | S3C2410_IICLC_FILTER_ON,
};
static struct resource s3c_i2c_resource[] = {
[0] = {
.start = S3C_PA_IIC,
.end = S3C_PA_IIC + SZ_4K - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_IIC,
.end = IRQ_IIC,
.flags = IORESOURCE_IRQ,
},
};
//这里的name必须和driver里面的id_table的name一致
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
.id = 0,
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
/*******************相关注册device的结构体**********************/
//传参为NULL
void __init s3c_i2c0_set_platdata(struct s3c2410_platform_i2c *pd)
{
struct s3c2410_platform_i2c *npd;
if (!pd)
pd = &default_i2c_data0;
npd = kmemdup(pd, sizeof(struct s3c2410_platform_i2c), GFP_KERNEL);
if (!npd)
printk(KERN_ERR "%s: no memory for platform data\n", __func__);
else if (!npd->cfg_gpio)
npd->cfg_gpio = s3c_i2c0_cfg_gpio;
s3c_device_i2c0.dev.platform_data = npd;
}
static struct i2c_board_info i2c_devs0[] __initdata = {
#ifdef CONFIG_SND_SOC_WM8580
{
I2C_BOARD_INFO("wm8580", 0x1b),
},
#endif
};
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;
//最重要的就是这句,把devinfo注册到__i2c_board_list中,到时候在注册adapter的时候才能找到I2C设备
list_add_tail(&devinfo->list, &__i2c_board_list);
}
up_write(&__i2c_board_lock);
return status;
}
到这I2C设备注册也就结束了,相关结构体之间的关联,我懒得画图,我也是借用大神的图来参考的。
这两张图片对应上面的函数,可以自己分析。
五、驱动实例的分析
1.参考i2c-s3c2410.c来分析,它采用的平台驱动模型来写的I2C驱动
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
static struct platform_driver s3c24xx_i2c_driver = {
.probe = s3c24xx_i2c_probe,
.remove = s3c24xx_i2c_remove,
.id_table = s3c24xx_driver_ids,
.driver = {
.owner = THIS_MODULE,
.name = "s3c-i2c",
.pm = S3C24XX_DEV_PM_OPS,
},
};
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
我们知道在platform平台驱动,中只要driver和device相遇匹配之后就会执行prob函数。所以接下来我们看prob函数
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
struct s3c2410_platform_i2c *pdata;
struct resource *res;
int ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data\n");
return -EINVAL;
}
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
//设置I2C的名字
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
//填充I2C中adap的通信算法,这个算法我不太懂。
i2c->adap.algo = &s3c24xx_i2c_algorithm;
//填充重连次数
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
spin_lock_init(&i2c->lock);
init_waitqueue_head(&i2c->wait);
/* find the clock and enable it */
i2c->dev = &pdev->dev;
i2c->clk = clk_get(&pdev->dev, "i2c");
if (IS_ERR(i2c->clk)) {
dev_err(&pdev->dev, "cannot get clock\n");
ret = -ENOENT;
goto err_noclk;
}
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
clk_enable(i2c->clk);
/* map the registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "cannot find IO resource\n");
ret = -ENOENT;
goto err_clk;
}
i2c->ioarea = request_mem_region(res->start, resource_size(res),
pdev->name);
if (i2c->ioarea == NULL) {
dev_err(&pdev->dev, "cannot request IO\n");
ret = -ENXIO;
goto err_clk;
}
i2c->regs = ioremap(res->start, resource_size(res));
if (i2c->regs == NULL) {
dev_err(&pdev->dev, "cannot map IO\n");
ret = -ENXIO;
goto err_ioarea;
}
dev_dbg(&pdev->dev, "registers %p (%p, %p)\n",
i2c->regs, i2c->ioarea, res);
/* setup info block for the i2c core */
//相互关联
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
/* initialise the i2c controller */
//具体的寄存器配置就在这个函数里面
ret = s3c24xx_i2c_init(i2c);
if (ret != 0)
goto err_iomap;
/* find the IRQ for this unit (note, this relies on the init call to
* ensure no current IRQs pending
*/
i2c->irq = ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
dev_err(&pdev->dev, "cannot find IRQ\n");
goto err_iomap;
}
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
if (ret != 0) {
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
goto err_iomap;
}
ret = s3c24xx_i2c_register_cpufreq(i2c);
if (ret < 0) {
dev_err(&pdev->dev, "failed to register cpufreq notifier\n");
goto err_irq;
}
/* Note, previous versions of the driver used i2c_add_adapter()
* to add the bus at any number. We now pass the bus number via
* the platform data, so if unset it will now default to always
* being bus 0.
*/
i2c->adap.nr = pdata->bus_num;
//这个函数才是最重要的,也是i2c-core.c提供的接口,来注册i2c设备。
ret = i2c_add_numbered_adapter(&i2c->adap);
if (ret < 0) {
dev_err(&pdev->dev, "failed to add bus to i2c core\n");
goto err_cpufreq;
}
platform_set_drvdata(pdev, i2c);
clk_disable(i2c->clk);
dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev));
return 0;
err_cpufreq:
s3c24xx_i2c_deregister_cpufreq(i2c);
err_irq:
free_irq(i2c->irq, i2c);
err_iomap:
iounmap(i2c->regs);
err_ioarea:
release_resource(i2c->ioarea);
kfree(i2c->ioarea);
err_clk:
clk_disable(i2c->clk);
clk_put(i2c->clk);
err_noclk:
kfree(i2c);
return ret;
}
调用层次(注意:从这里开始就是i2c-core.c提供的接口了)
i2c_add_numbered_adapter
i2c_register_adapter
static int i2c_register_adapter(struct i2c_adapter *adap)
{
int res = 0, dummy;
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p))) {
res = -EAGAIN;
goto out_list;
}
rt_mutex_init(&adap->bus_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的名字
dev_set_name(&adap->dev, "i2c-%d", adap->nr);
//设置dev.bus
adap->dev.bus = &i2c_bus_type;
//设置dev.type
adap->dev.type = &i2c_adapter_type;
//注册adap->dev
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 */
//重要点。根据分析会调用i2c_scan_static_board_info函数
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
/* Notify drivers */
mutex_lock(&core_lock);
//找到相应的driver对应adapter
dummy = 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);
//注意__i2c_board_list链表在I2C设备注册时候就已经填充了,接着调用i2c_new_device函数
list_for_each_entry(devinfo, &__i2c_board_list, list) {
if (devinfo->busnum == adapter->nr
&& !i2c_new_device(adapter,
&devinfo->board_info))
dev_err(&adapter->dev,
"Can't create device at 0x%02x\n",
devinfo->board_info.addr);
}
up_read(&__i2c_board_lock);
}
//这个函数作用:创建并且填充client,还绑定了相对的adapter
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
{
struct i2c_client *client;
int status;
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client->adapter = adap;
client->dev.platform_data = info->platform_data;
if (info->archdata)
client->dev.archdata = *info->archdata;
client->flags = info->flags;
client->addr = info->addr;
client->irq = info->irq;
strlcpy(client->name, info->type, sizeof(client->name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
goto out_err_silent;
}
/* Check for address business */
status = i2c_check_addr_busy(adap, client->addr);
if (status)
goto out_err;
client->dev.parent = &client->adapter->dev;
client->dev.bus = &i2c_bus_type;
client->dev.type = &i2c_client_type;
#ifdef CONFIG_OF
client->dev.of_node = info->of_node;
#endif
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr);
//在这里device_register在I2C总线下添加了client
status = device_register(&client->dev);
if (status)
goto out_err;
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
return client;
out_err:
dev_err(&adap->dev, "Failed to register i2c client %s at 0x%02x "
"(%d)\n", client->name, client->addr, status);
out_err_silent:
kfree(client);
return NULL;
}
bus_for_each_drv(&i2c_bus_type, NULL, adap, __process_new_adapter);
//这里的参数是简化过的,自己分析下就明白了
__process_new_adapter(drv, data);
i2c_do_add_adapter(to_i2c_driver(d), data);
static int i2c_do_add_adapter(struct i2c_driver *driver,
struct i2c_adapter *adap)
{
//这是老版本使用的,所以在这里直接返回了。
i2c_detect(adap, driver);
/* Let legacy drivers scan this bus for matching devices */
//driver->attach_adapter函数指针在i2c-dev.c里面被赋值
if (driver->attach_adapter) {
/* We ignore the return code; if it fails, too bad */
driver->attach_adapter(adap);
}
return 0;
}
到这里基本的初始化已经完成了。