LINUX平台下IIC子系统的经典分布图如下:
下面主要针对内核中IIC新模式(new style)进行分析.
下面以S3C2440平台搭载的24C08进行整个LINUX IIC子系统的分析.
1.如何生成用户空间的设备节点:
1-1.24c08驱动端:
24c08的驱动代码位于drivers/misc/eeprom/at24.c
入口函数:
module_init(at24_init);
static int __init at24_init(void)
{
return i2c_add_driver(&at24_driver);
}
在LINUX内核中,每个IIC驱动对应的结构体为struct i2c_driver.24c08也不例外:
static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};
这里有一个很重要的域:id_table:
static const struct i2c_device_id at24_ids[] = {
/* needs 8 addresses as A0-A2 are ignored */
{ "24c00", AT24_DEVICE_MAGIC(128 / 8, AT24_FLAG_TAKE8ADDR) },
/* old variants can't be handled with this generic entry! */
{ "24c01", AT24_DEVICE_MAGIC(1024 / 8, 0) },
{ "24c02", AT24_DEVICE_MAGIC(2048 / 8, 0) },
/* spd is a 24c02 in memory DIMMs */
{ "spd", AT24_DEVICE_MAGIC(2048 / 8,
AT24_FLAG_READONLY | AT24_FLAG_IRUGO) },
{ "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) },
/* 24rf08 quirk is handled at i2c-core */
{ "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) },
{ "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) },
{ "24c32", AT24_DEVICE_MAGIC(32768 / 8, AT24_FLAG_ADDR16) },
{ "24c64", AT24_DEVICE_MAGIC(65536 / 8, AT24_FLAG_ADDR16) },
{ "24c128", AT24_DEVICE_MAGIC(131072 / 8, AT24_FLAG_ADDR16) },
{ "24c256", AT24_DEVICE_MAGIC(262144 / 8, AT24_FLAG_ADDR16) },
{ "24c512", AT24_DEVICE_MAGIC(524288 / 8, AT24_FLAG_ADDR16) },
{ "24c1024", AT24_DEVICE_MAGIC(1048576 / 8, AT24_FLAG_ADDR16) },
{ "at24", 0 },
{ /* END OF LIST */ }
};
id_table域是探测此驱动支持哪些设备的依据,下面便是整个系统的调用流程追踪,主要是函数i2c_add_driver(&at24_driver):
static inline int i2c_add_driver(struct i2c_driver *driver)
{
return i2c_register_driver(THIS_MODULE, driver);
}
-->
/*
* An i2c_driver is used with one or more i2c_client (device) nodes to access
* i2c slave chips, on a bus instance associated with some i2c_adapter.
*/
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;
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
INIT_LIST_HEAD(&driver->clients);
/* Walk the adapters that are already present */
mutex_lock(&core_lock);
bus_for_each_dev(&i2c_bus_type, NULL, driver, __attach_adapter);
mutex_unlock(&core_lock);
return 0;
}
linux每个IIC驱动都是挂在IIC这条总线上的,见上面代码:
driver->driver.bus = &i2c_bus_type;
类似usb、spi等子系统一样,每个驱动都会标明其所属的总线类型.这种驱动依附总线的一个重要意义在于匹配条件:IIC有IIC的匹配条件、USB和USB的匹配条件等.在这里需要铭记我们这个24c08属于iic driver,而iic driver是隶属IIC BUS的.下面继承跟踪域id_table的意义,在函数i2c_register_driver()函数里面第二个关注的地方语句是:
res = driver_register(&driver->driver);
其实,这是LINUX驱动里面一个"核心基类"函数,所有的隶属总线的驱动(像uart、spi、usb等)均路由此函数,因此,可以独立来理解,因为此函数与子系统无关.其最重大的意义在于回调到具体调用此函数的总线的match函数的执行.下面通过代码验证这一点:
int driver_register(struct device_driver *drv)
{
... ...;
ret = bus_add_driver(drv);
... ...;
}
展开函数bus_add_driver():
int bus_add_driver(struct device_driver *drv)
{
... ...;
if (drv->bus->p->drivers_autoprobe) {
error = driver_attach(drv);
if (error)
goto out_unregister;
}
... ...;
}
展开函数driver_attach():
int driver_attach(struct device_driver *drv)
{
return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
函数bus_for_each_dev会遍历挂在此总线上的设备,再根据一定的"匹配条件"(主要通过回调函数__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)
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;
}
下面我们来看函数__driver_attach():
static int __driver_attach(struct device *dev, void *data)
{
if (!driver_match_device(drv, dev))
return 0;
if (!dev->driver)
driver_probe_device(drv, dev);
}
这里有两个比较重要的函数:driver_match_device()和driver_probe_device().其中和id_table域相关的函数是driver_match_device()函数;而后者是device和driver相关的函数.
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
return drv->bus->match ? drv->bus->match(dev, drv) : 1;
}
可以看到,这里将会回调总线上的match函数,对于IIC总线而言,其match函数是:i2c_device_match()
struct bus_type i2c_bus_type = {
.name = "i2c",
.match = i2c_device_match,
.probe = i2c_device_probe,
.remove = i2c_device_remove,
.shutdown = i2c_device_shutdown,
.suspend = i2c_device_suspend,
.resume = i2c_device_resume,
};
展开函数i2c_device_match():
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
if (driver->id_table)
return i2c_match_id(driver->id_table, client) != NULL;
return 0;
}
继续展开函数i2c_match_id():
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)
return id;
id++;
}
return NULL;
}
因此,对于iic设备驱动而言,其匹配的条件是id_table域.这时候一个结构体struct i2c_client横空出世了.这货是从哪来的呢?struct i2c_client其实是表征一个iic设备.下面以mini2440平台来追踪此函数的来龙去脉.
1-2.24c08设备端:
arch/arm/mach-s3c2440/mach-mini2440.c:
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c24xx_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
这个函数在系统启动过程中必须得被调用,留意到mini2440_init函数:
static void __init mini2440_init(void)
{
i2c_register_board_info(0, mini2440_i2c_devs,
ARRAY_SIZE(mini2440_i2c_devs));
}
mini2440_init()函数里面有对板载iic设备进行注册,见函数i2c_register_board_info().其中有个参数mini2440_i2c_devs便是与我们这个24c08相关的.如下:
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("24c08", 0x50),
.platform_data = &at24c08,
},
};
展开函数i2c_register_board_info():
int __init i2c_register_board_info(int busnum,struct i2c_board_info const *info, unsigned len)
{
devinfo = kzalloc(sizeof(*devinfo), GFP_KERNEL);
devinfo->busnum = busnum;
devinfo->board_info = *info;
list_add_tail(&devinfo->list, &__i2c_board_list);
}
这里需要注意两点:
1).把我们这个24c08这个iic设备信息存放到devinfo结构体里面;
2).把devinfo连到全局链表头__i2c_board_list里面.
1-2-1.主控端
要想追踪上述的关于24c08对应的i2c_client的生成,得从主控端的适配器(adapter)代码看起,关于mini2440平台iic适配器端代码位于drivers/i2c/busses/i2c-s3c2410.c:
static int __init i2c_adap_s3c_init(void)
{
return platform_driver_register(&s3c24xx_i2c_driver);
}
subsys_initcall(i2c_adap_s3c_init);
这里把iic适配器驱动注册成隶属于平台总线的驱动(平台总线是内核虚拟的一条总线,其地位与iic总线是平级的地位).s3c2440的iic适配器关于平台驱动的信息如下:
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,
},
};
分析函数platform_driver_register()和上述的i2c_register_driver()有很大的思维模式类似之处.下面分析之:
int platform_driver_register(struct platform_driver *drv)
{
drv->driver.bus = &platform_bus_type;
if (drv->probe)
drv->driver.probe = platform_drv_probe;
if (drv->remove)
drv->driver.remove = platform_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = platform_drv_shutdown;
return driver_register(&drv->driver);
}
在上述分析i2c_register_driver()函数时,已经明确:函数driver_register()是LINUX驱动的"核心基类",不隶属于任何子系统,其重大意义在于回调驱动所隶属的总线的match函数.因此,不再累赘分析其过程(上面已经有所分析).这里直接跳到platform_bus_type的match函数:
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
};
展开platform_match()函数:
static int platform_match(struct device *dev, struct device_driver *drv)
{
struct platform_device *pdev = to_platform_device(dev);
struct platform_driver *pdrv = to_platform_driver(drv);
/* match against the id table first */
if (pdrv->id_table)
return platform_match_id(pdrv->id_table, pdev) != NULL;
/* fall-back to driver name match */
return (strcmp(pdev->name, drv->name) == 0);
}
如果我们的平台驱动有定义id_table域,那么匹配依据是id_table;否则就只需要匹配隶属于平台总线的驱动名字和设备名字即可.在这里,s3c24xx_i2c_driver有对id_table域有作定义,因此,这里匹配的依据是id_table:
static struct platform_device_id s3c24xx_driver_ids[] = {
{
.name = "s3c2410-i2c",
.driver_data = TYPE_S3C2410,
}, {
.name = "s3c2440-i2c",
.driver_data = TYPE_S3C2440,
}, { },
};
展开函数platform_match_id():
static const struct platform_device_id *platform_match_id(
struct platform_device_id *id,
struct platform_device *pdev)
{
while (id->name[0]) {
if (strcmp(pdev->name, id->name) == 0) {
pdev->id_entry = id;
return id;
}
id++;
}
return NULL;
}
这里还是匹配驱动名字和设备名字.我们这里的驱动名为"s3c2440-i2c",那么,隶属于平台总线的同样命名为"s3c2440-i2c"的平台设备是何时注册进平台总线的呢?见源码文件
arch/arm/mach-s3c2440/mach-mini2440.c有如下代码:
MACHINE_START(MINI2440, "MINI2440")
/* Maintainer: Michel Pollet */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.map_io = mini2440_map_io,
.init_machine = mini2440_init,
.init_irq = s3c24xx_init_irq,
.timer = &s3c24xx_timer,
MACHINE_END
其中,语句:
mini2440_init
此函数的调用里面,完成了所有平台设备的注册(此函数如何被调用暂不作细究).展开如下:
static void __init mini2440_init(void)
{
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
}
其中参数mini2440_devices为mini2440整个开发板的平台设备数组.如下:
static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_wdt,
/* &s3c_device_adc,*/ /* ADC doesn't like living with touchscreen ! */
&s3c_device_i2c0,
&s3c_device_rtc,
&s3c_device_usbgadget,
&mini2440_device_eth,
&mini2440_led1,
&mini2440_led2,
&mini2440_led3,
&mini2440_led4,
&mini2440_button_device,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_iis,
&mini2440_audio,
/* &s3c_device_timer[0],*/ /* buzzer pwm, no API for it */
/* remaining devices are optional */
};
其中关于iic适配器的见上述数组的s3c_device_i2c0:
struct platform_device s3c_device_i2c0 = {
.name = "s3c2410-i2c",
#ifdef CONFIG_S3C_DEV_I2C1
.id = 0,
#else
.id = -1,
#endif
.num_resources = ARRAY_SIZE(s3c_i2c_resource),
.resource = s3c_i2c_resource,
};
见到此iic适配器的设备名字为"s3c2410-i2c",和我们之前的适配器的驱动名一样.平台设备的注册由函数platform_add_devices()批量完成:
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i < num; i++) {
ret = platform_device_register(devs[i]);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs[i]);
break;
}
}
return ret;
}
回到函数platform_match()中,可见是匹配成功的.上述的函数__driver_attach()只分析到driver_match_device(),这里得出的结论是驱动所隶属的总线的match函数被调用.如下:
static int __driver_attach(struct device *dev, void *data)
{
if (!driver_match_device(drv, dev))
return 0;
if (!dev->driver)
driver_probe_device(drv, dev);
}
接下来我们继续分析函数driver_probe_device()函数:
int driver_probe_device(struct device_driver *drv, struct device *dev)
{
ret = really_probe(dev, drv);
}
在函数really_probe()函数里面,将会导致总线/驱动的probe函数的被调用:
static int really_probe(struct device *dev, struct device_driver *drv)
{
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret)
goto probe_failed;
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret)
goto probe_failed;
}
}
在这里的iic适配器这个设备里面,并没有定义probe域,因此,执行的是驱动的probe函数.为了方便记忆,这里重复把s3c24xx_i2c_driver粘贴在这里:
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,
},
};
展开函数s3c24xx_i2c_probe():
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
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;
}
}
展开函数i2c_add_numbered_adapter():
int i2c_add_numbered_adapter(struct i2c_adapter *adap)
{
status = i2c_register_adapter(adap);
}
在这里完成适配器的注册.
static int i2c_register_adapter(struct i2c_adapter *adap)
{
if (adap->nr < __i2c_first_dynamic_bus_num)
i2c_scan_static_board_info(adap);
}
展开函数i2c_scan_static_board_info(),完成iic设备的注册:
static void i2c_scan_static_board_info(struct i2c_adapter *adapter)
{
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);
}
}
核心函数i2c_new_device()是动态生成一个iic设备的.而我们在分析24c08的设备端时,分配的devinfo便以链表的方式记录在__i2c_board_list,这里通过链表的方式把具体的iic设备信息抓出来进行iic设备(struct i2c_client)的初始化:
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 business */
status = i2c_check_addr(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;
dev_set_name(&client->dev, "%d-%04x", i2c_adapter_id(adap),
client->addr);
return client;
}
先回顾我们上述的24c08的设备端:
static struct i2c_board_info mini2440_i2c_devs[] __initdata = {
{
I2C_BOARD_INFO("24c08", 0x50),
.platform_data = &at24c08,
},
};
I2C_BOARD_INFO展开:
#define I2C_BOARD_INFO(dev_type, dev_addr) \
.type = dev_type, .addr = (dev_addr)
见函数i2c_new_device()语句:
strlcpy(client->name, info->type, sizeof(client->name));
因此,对于我们这个24c02来说,此client->name域是"24c08".并且这个i2c_client是被挂载在IIC BUS上的:
client->dev.bus = &i2c_bus_type;
至此,回溯到之前的i2c_device_match()函数.match成功.为了思维的连续性,下面继续从at24.c到当前的过程再撸一遍,当然,省略struct i2c_client的生成部分:
module_init(at24_init);->i2c_add_driver(&at24_driver);->...->__driver_attach():
接着上面我们的分析断点处:
static int __driver_attach(struct device *dev, void *data)
{
if (!driver_match_device(drv, dev))
return 0;
if (!dev->driver)
driver_probe_device(drv, dev);
}
又见probe,只不过这里对应的是i2c_device_probe()函数:
static int i2c_device_probe(struct device *dev)
{
status = driver->probe(client, i2c_match_id(driver->id_table, client));
}
总线的probe又引发了driver下的probe的执行,在这里是at24_probe()函数:
/*-------------------------------------------------------------------------*/
static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
at24->bin.attr.name = "eeprom";
at24->bin.attr.mode = chip.flags & AT24_FLAG_IRUGO ? S_IRUGO : S_IRUSR;
at24->bin.read = at24_bin_read;
at24->bin.size = chip.byte_len;
at24->macc.read = at24_macc_read;
err = sysfs_create_bin_file(&client->dev.kobj, &at24->bin);
if (err)
goto err_clients;
}
可见,关于24c08暴露给上层用户空间的并不是/dev/下的设备节点,而是/sys/目录下的文件.对/sys/目录下的文件操作即引发其相关联的读写函数的执行.如上述的函数:
at24->bin.read = at24_bin_read;
at24->bin.write = at24_bin_write;
2.用户空间如何实现和内核驱动的数据交互?
上述分析,用户空间通过/sys/下的文件(属性)实现和底层交互.以写24c08函数追踪其流程.
static ssize_t at24_bin_write(struct kobject *kobj, struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct at24_data *at24;
at24 = dev_get_drvdata(container_of(kobj, struct device, kobj));
return at24_write(at24, buf, off, count);
}
buf即为用户层要写入到24c08的数据,展开函数at24_write():
static ssize_t at24_write(struct at24_data *at24, const char *buf, loff_t off,
size_t count)
{
status = at24_eeprom_write(at24, buf, off, count);
}
核心函数为at24_eeprom_write().展开如下:
static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,unsigned offset, size_t count)
{
client = at24_translate_offset(at24, &offset);
if (!at24->use_smbus) {
int i = 0;
msg.addr = client->addr;
msg.flags = 0;
/* msg.buf is u8 and casts will mask the values */
msg.buf = at24->writebuf;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msg.buf[i++] = offset >> 8;
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
}
status = i2c_transfer(client->adapter, &msg, 1);
}
我们要对一个IIC设备操作,是通过适配器完成的,只需要在msg里面包含这个IIC设备的信息(如IIC设备地址、要读写的数据)即可.那么,这个msg是如何根据我们的24c08完成这个iic消息的构造的呢?这里分两步走,一步是msg的构造,一步是msg是如何在linux iic子系统里面流窜的.
2-1.msg的构造:
上述函数at24_eeprom_write()中对msg的构造语句如下:
if (!at24->use_smbus) {
int i = 0;
msg.addr = client->addr;
msg.flags = 0;
/* msg.buf is u8 and casts will mask the values */
msg.buf = at24->writebuf;
if (at24->chip.flags & AT24_FLAG_ADDR16)
msg.buf[i++] = offset >> 8;
msg.buf[i++] = offset;
memcpy(&msg.buf[i], buf, count);
msg.len = i + count;
}
语句msg.addr = client->addr来标明此msg地址,即这个msg将流向哪个iic设备,而每一个iic设备用一个struct i2c_client来表征.在上述的函数i2c_new_device()有下面语句:
client->addr = info->addr;
info->addr即0x50.见上述函数i2c_register_board_info()及i2c_adap_s3c_init().
用户空间传送的数据被缓存在msg.buf,数据量被记录在msg.len.
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
ret = adap->algo->master_xfer(adap, msgs, num);
}
在这里需要重点关注下结构体adap,它表征一个适配器(即IIC的MASTER).我们要操作一个IIC设备时,构造好msg,然后通过adap对这个msg解析,实现到指定iic设备的数据交互.关于这个adap的来龙去脉下面继续分析:
i2c-s3c2410.c:
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);
}
subsys_initcall(i2c_adap_s3c_init);
如何从i2c_adap_s3c_init()到s3c24xx_i2c_probe()函数的调度流程见上述的分析.下面直接看函数s3c24xx_i2c_probe():
static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
struct s3c24xx_i2c *i2c;
i2c = kzalloc(sizeof(struct s3c24xx_i2c), GFP_KERNEL);
if (!i2c) {
dev_err(&pdev->dev, "no memory for state\n");
return -ENOMEM;
}
strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name));
i2c->adap.owner = THIS_MODULE;
i2c->adap.algo = &s3c24xx_i2c_algorithm;
i2c->adap.retries = 2;
i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD;
i2c->tx_setup = 50;
i2c->adap.algo_data = i2c;
i2c->adap.dev.parent = &pdev->dev;
ret = i2c_add_numbered_adapter(&i2c->adap);
}
见结构体struct s3c24xx_i2c,里面封装了结构体adap,可见,adap是平台无关的.特定的平台只需要用特定平台的信息去初始化相应的adap即可:
struct s3c24xx_i2c {
struct i2c_msg *msg;
struct i2c_adapter adap;
};
上述函数s3c24xx_i2c_probe()便是对24xx平台的adap初始化.其中语句:
i2c->adap.algo = &s3c24xx_i2c_algorithm;
在上述的i2c_transfer()函数中,将调用这个域的函数master_xfer来实现IIC设备的通讯:
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
展开函数s3c24xx_i2c_xfer():
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg *msgs, int num)
{
ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
}
展开函数:
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,struct i2c_msg *msgs, int num)
{
ret = s3c24xx_i2c_set_master(i2c);//判断IIC BUS是否处于繁忙状态,结合源码和datasheet.
if (ret != 0) {
dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
ret = -EAGAIN;
goto out;
}
i2c->msg = msgs;
i2c->msg_num = num;
i2c->msg_ptr = 0;
i2c->msg_idx = 0;
i2c->state = STATE_START;
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs);//IIC BUS的协议信号,如开始、停止等.此函数和IIC协议密切相关.
}
其中函数s3c24xx_i2c_message_start()和IIC协议密切相关,如开始信号、设备地址、结束信号、是否需要ACK应答等.如下:
/* s3c24xx_i2c_message_start
*
* put the start of a message onto the bus
*/
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
struct i2c_msg *msg)
{
unsigned int addr = (msg->addr & 0x7f) << 1;
unsigned long stat;
unsigned long iiccon;
stat = 0;
stat |= S3C2410_IICSTAT_TXRXEN;
if (msg->flags & I2C_M_RD) {
stat |= S3C2410_IICSTAT_MASTER_RX;
addr |= 1;
} else
stat |= S3C2410_IICSTAT_MASTER_TX;
if (msg->flags & I2C_M_REV_DIR_ADDR)
addr ^= 1;
/* todo - check for wether ack wanted or not */
s3c24xx_i2c_enable_ack(i2c);
iiccon = readl(i2c->regs + S3C2410_IICCON);
writel(stat, i2c->regs + S3C2410_IICSTAT);
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
writeb(addr, i2c->regs + S3C2410_IICDS);
/* delay here to ensure the data byte has gotten onto the bus
* before the transaction is started */
ndelay(i2c->tx_setup);
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
writel(iiccon, i2c->regs + S3C2410_IICCON);
stat |= S3C2410_IICSTAT_START;
writel(stat, i2c->regs + S3C2410_IICSTAT);
}
具体可结合IIC协议、源码和CPU手册去理解.对于写动作,此函数代码主要实现:
1).发起IIC起始信号;
2).根据iic msg的地址域信息配置iic方向,是读iic设备还是写iic设备.
那么,我们实际的数据是在哪写进iic设备的呢?在S3C2440的IIC中断里面实现.见函数s3c24xx_i2c_probe():
ret = request_irq(i2c->irq, s3c24xx_i2c_irq, IRQF_DISABLED,
dev_name(&pdev->dev), i2c);
在函数s3c24xx_i2c_doxfer():
s3c24xx_i2c_enable_irq(i2c);
s3c24xx_i2c_message_start(i2c, msgs);
使能了iic中断,展开中断处理函数:
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
{
struct s3c24xx_i2c *i2c = dev_id;
i2s_s3c_irq_nextbyte(i2c, status);
}
在注册中断函数时,我们把i2c作为参数传递给中断处理函数的.i2c中的相关信息被上层用户传递的信息给封装.通过函数 i2s_s3c_irq_nextbyte(i2c, status);推进:
/* i2s_s3c_irq_nextbyte
*
* process an interrupt and work out what to do
*/
static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
{
unsigned long tmp;
unsigned char byte;
int ret = 0;
switch (i2c->state) {
case STATE_IDLE:
dev_err(i2c->dev, "%s: called in STATE_IDLE\n", __func__);
goto out;
break;
case STATE_STOP:
dev_err(i2c->dev, "%s: called in STATE_STOP\n", __func__);
s3c24xx_i2c_disable_irq(i2c);
goto out_ack;
case STATE_START:
/* last thing we did was send a start condition on the
* bus, or started a new i2c message
*/
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
/* ack was not received... */
dev_dbg(i2c->dev, "ack was not received\n");
s3c24xx_i2c_stop(i2c, -ENXIO);
goto out_ack;
}
if (i2c->msg->flags & I2C_M_RD)
i2c->state = STATE_READ;
else
i2c->state = STATE_WRITE;
/* terminate the transfer if there is nothing to do
* as this is used by the i2c probe to find devices. */
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
s3c24xx_i2c_stop(i2c, 0);
goto out_ack;
}
if (i2c->state == STATE_READ)
goto prepare_read;
/* fall through to the write state, as we will need to
* send a byte as well */
case STATE_WRITE:
/* we are writing data to the device... check for the
* end of the message, and if so, work out what to do
*/
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
if (iicstat & S3C2410_IICSTAT_LASTBIT) {
dev_dbg(i2c->dev, "WRITE: No Ack\n");
s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
goto out_ack;
}
}
retry_write:
if (!is_msgend(i2c)) {
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
/* delay after writing the byte to allow the
* data setup time on the bus, as writing the
* data to the register causes the first bit
* to appear on SDA, and SCL will change as
* soon as the interrupt is acknowledged */
ndelay(i2c->tx_setup);
} else if (!is_lastmsg(i2c)) {
/* we need to go to the next i2c message */
dev_dbg(i2c->dev, "WRITE: Next Message\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
/* check to see if we need to do another message */
if (i2c->msg->flags & I2C_M_NOSTART) {
if (i2c->msg->flags & I2C_M_RD) {
/* cannot do this, the controller
* forces us to send a new START
* when we change direction */
s3c24xx_i2c_stop(i2c, -EINVAL);
}
goto retry_write;
} else {
/* send the new start */
s3c24xx_i2c_message_start(i2c, i2c->msg);
i2c->state = STATE_START;
}
} else {
/* send stop */
s3c24xx_i2c_stop(i2c, 0);
}
break;
case STATE_READ:
/* we have a byte of data in the data register, do
* something with it, and then work out wether we are
* going to do any more read/write
*/
byte = readb(i2c->regs + S3C2410_IICDS);
i2c->msg->buf[i2c->msg_ptr++] = byte;
prepare_read:
if (is_msglast(i2c)) {
/* last byte of buffer */
if (is_lastmsg(i2c))
s3c24xx_i2c_disable_ack(i2c);
} else if (is_msgend(i2c)) {
/* ok, we've read the entire buffer, see if there
* is anything else we need to do */
if (is_lastmsg(i2c)) {
/* last message, send stop and complete */
dev_dbg(i2c->dev, "READ: Send Stop\n");
s3c24xx_i2c_stop(i2c, 0);
} else {
/* go to the next transfer */
dev_dbg(i2c->dev, "READ: Next Transfer\n");
i2c->msg_ptr = 0;
i2c->msg_idx++;
i2c->msg++;
}
}
break;
}
/* acknowlegde the IRQ and get back on with the work */
out_ack:
tmp = readl(i2c->regs + S3C2410_IICCON);
tmp &= ~S3C2410_IICCON_IRQPEND;
writel(tmp, i2c->regs + S3C2410_IICCON);
out:
return ret;
}
我们的数据被保存在i2c msg的buf域里面.在函数i2s_s3c_irq_nextbyte()里面,往iic设备写数据的代码如下:
byte = i2c->msg->buf[i2c->msg_ptr++];
writeb(byte, i2c->regs + S3C2410_IICDS);
3.小结:
为了对上述脉络有一个更清晰的架构理解,并对过程中一些比较重要的数据结构进行归纳,从内核软件层的先后顺序进行小结:
3-1.iic控制器与"运算器":
iic控制器主要是指与特定平台相关的iic控制器,如这里的struct s3c24xx_i2c;而运算器则指上述的"适配器"--struct i2c_adapter.其中struct i2c_adapter是与具体平台无关的的,它位于linux i2c的核心层.具体的i2c_adapter需要被具体平台的iic控制器初始化.这一步实现了具体平台的iic控制器与软件的i2c_adapter实现了关联;
而adapter与一般的总线设备注册进内核的方式无异.
3-2.iic设备:
linux中每一个iic设备用一个struct i2c_client来表征,里面包含了如iic设备的地址等信息.它是动态创建,并通过内核链表的方式去挂载到指定的adapter上;
3-3.struct i2c_msg
在数据的交互过程中,流窜于整个内核iic子系统的媒体就是"struct i2c_msg",里面至少包含了两个信息:一是这个iic消息的流向(即目标iic设备地址);二是和iic设备交互的内容.
其中获取iic设备地址的过程中,需要借助上述动态创建的表征一个iic设备的结构体struct i2c_client.
3-4.和用户空间交互
如果采用标准的内核iic子系统,有可能是/sys/下的属性文件,如这里的24c08.当然也可以改写成/dev/下的字符设备.当然还有另外两种存在方式,一种是不用内核iic子系统,只是简单的字符设备,把相关的iic 子系统里面实现iic通讯即可;第二种就是用户空间的iic操作.