注意:
1. LINUX-2.6.20的内核
2. CPU是AT91SAM9260
3. PCF8563的I2C驱动
大体过程:
1. 为什么内核要有这么多模型
2. platform总线、设备、驱动模型,简单的介绍
3. I2C模型所涉及到的程序文件位置及简介
4. 关键数据结构
5. I2C驱动模型流程图
6. 按流程图顺序分析代码
7. RTC模型简单介绍
8. PCF8563设备操作驱动
从整体上描述,大概模型就如下图所示:
从上图中可以看出,Linux设备模型就是"总线、设备、驱动、类"这四个概念之前的相互关系;这也是Linux2.6内核抽象出来的用于管理系统中所有设备的模型图;
简单地描述设备模型的层次关系如下:
1、驱动核心中可以注册多种类型的总线(bus_type);
2、每一种类型的总线下面可以挂载许多设备(kset,device);
3、每一种类型的总线可以使用很多设备驱动(kset,device_driver);
4、每一个驱动程序可以管理一组设备;
这种基本关系的建立源于实际系统中各种总线、设备、驱动、类结构的抽象;
由上图可知,LINUX是模型是树型,呈金字塔型,图中的Hub就类似于I2C模型的适配器,这就如同我们硬件上实现使用的电源适配器一样,内核可以使用此模拟适配器进行电源管理。
时通过platform device 提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独
立性,这样拥有更好的可移植性。platform 机制的本身使用并不复杂,由两部分组成:
platform_device 和platfrom_driver。Platform driver 通过platform bus 获取platform_device。
通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源
(地址总线和IRQs),都可以用 platform_driver 来管理,而timer,irq 等小系统之内的设备
则最好不用platfrom_driver 机制。
platform_device 最大的特定是CPU 直接寻址设备的寄存器空间,即使对于其他总线设备,
设备本身的寄存器无法通过CPU 总线访问,但总线的controller 仍然需要通过platform bus
来管理。
总之,platfrom_driver 的根本目的是为了统一管理系统的外设资源,为驱动程序提供统一的
接口来访问系统资源,将驱动和资源分离,提高程序的可移植性。
1. driver/base/platform.c :注册platform设备总线
2. arch\arm\mach-at91rm9200\board-sam9260k.c :添加I2C总线设备到platform总线上
3. arch\arm\mach-at91rm9200\at91sam9260_devices.c :platform片上设备文件定义,board-sam9260.c中调用
4. driver\i2c\busses\i2c-at91.c :I2C总线设备驱动注册到platform总线
5. driver\i2c\i2c-core.c :I2C模型使用的API,i2c-at91.c中调用,rtc-pcf8563.c调用
6. driver\i2c\algos :此目录为操作I2C适配器的方法,即操作I2C控制器的方法,我所用的内核将这些函数放到了i2c-at91.c中
7. drivers\i2c\i2c-dev.c:内核提供的一接口,可以让用户层编写控制I2C控制方法,我们没有使用
8. drivers\rtc\rtc-pcf8563.c :pcf8563设备驱动
9. drivers\rtc\class.c :RTC设备注册API,RTC模型一部分
以下是I2C模型框架图,更多的内容请参考:
http://hi.baidu.com/kebey2004/item/a7f08c4554607caa60d7b9f2
I2C总线驱动是对适配器端的实现,其含有适配器数据结构struct i2c_adapter,适配器算法数据结构struct i2c_algorithm。I2C设备驱动是对设备端的实现和控制,其含有设备驱动结构i2c_driver和设备客户端结构struct i2c_client。
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
struct i2c_algorithm *algo;//总线通讯数据结构体
void *algo_data; //用于保存包含适配器的私有数据结构
int (*client_register)(struct i2c_client *);//客户端注册函数
int (*client_unregister)(struct i2c_client *);//客户端注销函数
struct semaphore bus_lock;
struct semaphore clist_lock;
int timeout;
int retries; //重试次数
struct device dev; //适配器设备
struct class_device class_dev; //类设备
int nr;
struct list_head clients; //客户端链表
struct list_head list; //适配器链表
char name[I2C_NAME_SIZE]; //适配器名称
struct completion dev_released;
struct completion class_dev_released;
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
int num); //i2c总线传输函数
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data); //smbus总线传输函数
int (*slave_send)(struct i2c_adapter *,char*,int); //适配器为主模式时发送函数
int (*slave_recv)(struct i2c_adapter *,char*,int); //适配器为主模式时接收函数
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
u32 (*functionality) (struct i2c_adapter *); //适配器支持功能
};以上两个数据结构为总线驱动需要设置并初始化。
struct i2c_driver {
struct module *owner;
char name[32]; //驱动名称
int id;
unsigned int class;
unsigned int flags; //I2C_DF_NOTIFY用于当设备依附或脱离时通知总线
int (*attach_adapter)(struct i2c_adapter *);//依附适配器函数
int (*detach_adapter)(struct i2c_adapter *);//脱离适配器函数
int (*detach_client)(struct i2c_client *); //脱离客户端函数
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
struct device_driver driver; //设备驱动结构体
struct list_head list; //链表头
};
struct i2c_client {
unsigned int flags;
unsigned short addr; // 低7为设备地址
struct i2c_adapter *adapter; //依附的适配器
struct i2c_driver *driver; //依附的驱动结构
int usage_count;
struct device dev;
struct list_head list; //客户端链表
char name[I2C_NAME_SIZE];//客户端名称
struct completion released;
};以上两个数据结构为设备驱动需要设置并初始化。
Intel制定了SMBus标准用于低速通讯。SMBus二线接口与I2C接口非常相似。SMBus也使用一条数据线(SMBDATA)和一条时钟线(SMBCLK)实现通讯。I2C接口和SMBus接口的主要区别是最大和最小时钟速度。SMBCLK必须在10kHz和100kHz之间。SMBCLK和SMBDATA线也需要上拉电阻。3V供电时上拉电阻大于8.5k ,5V供电时上拉电阻大于14k 。SMBus工作电压范围在3V和5V之间,大于2.1V为高电平,低于0.8V为低电平。struct i2c_adapter对应于物理上的一个适配器,而struct i2c_algorithm对应于一套通讯方法。i2c_algorithm提供一些控制适配器发送或接收函数,对于i2c总线需要初始化master_xfer函数,对于smbus总线需要初始化smbus_xfer函数。master_xfer函数是以i2c_msg为单位进行控制的,其结构如下:
struct i2c_msg {
__u16 addr; //从设备地址
__u16 flags; //动作标志:读或写
#define I2C_M_TEN 0x10 //器件地址是10Bit
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000 //意味当前i2c_msg不发送start信号
#define I2C_M_REV_DIR_ADDR 0x2000 //把读写标志位反转
#define I2C_M_IGNORE_NAK 0x1000//当前i2c_msg忽略I2C器件的ack和nack信号。
#define I2C_M_NO_RD_ACK 0x0800 //表示在正行读操作时不去ACK
__u16 len; //信息长度
__u8 *buf; //信息缓冲区首地址
};
i2c_driver和i2c_client用于控制设备驱动方面的结构。当i2c_driver->attach_adapter探测到物理设备后,因为i2c_client对应一个真实的物理设备则把探测到的i2c_client->adapter指向其依附的适配器的struct i2c_adapter 结构,把i2c_client->driver指向其依附的 i2c_driver结构.其注册和注销的函数分别为 i2c_attach_client和i2c_detach_client.
总线驱动需要定义一个包含 struct i2c_adapter的私有数据结构,用 i2c_adapter->algo_data指向它即可。
在总线驱动中需要探测并初始化适配器,分配一下IO地址和中断资源。
定义并初始化i2c_algorithm,依据总线类型为i2c或是smbus定义 master_xfer或smbus_xfer函数。
struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //总线match 设备和驱动
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
struct device platform_bus = {
.bus_id = "platform",
};
//平台总线注册到内核
int __init platform_bus_init(void)
{
device_register(&platform_bus); //平台总线设备注册到内核
return bus_register(&platform_bus_type); //总线注册
}
②I2C总线设备添加platform总线
arch\arm\mach-at91rm9200\board-sam9260k.c :添加I2C总线设备到platform总线上
static void __init ek_board_init(void)
{
/* Serial */
at91_add_device_serial();
/* USB Host */
…
…
at91_add_device_i2c(); //添加I2C设备
…
}
arch\arm\mach-at91rm9200\at91sam9260_devices.c :platform片上设备文件定义
static struct platform_device at91sam9260_twi_device = {
.name = "at91_i2c",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
};
void __init at91_add_device_i2c(void)
{
/* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA23, 1);
at91_set_A_periph(AT91_PIN_PA24, 0); /* TWCK */
at91_set_multi_drive(AT91_PIN_PA24, 1);
//添加I2C总线设备到platform总线上
platform_device_register(&at91sam9260_twi_device);
}
③driver\i2c\busses\i2c-at91.c :I2C总线设备驱动注册到platform总线
static struct platform_driver at91_i2c_driver = {
.probe = at91_i2c_probe,
.remove = __devexit_p(at91_i2c_remove),
.suspend = at91_i2c_suspend,
.resume = at91_i2c_resume,
.driver = {
.name = "at91_i2c",
.owner = THIS_MODULE,
},
};
static int __init at91_i2c_init(void)
{
// I2C总线设备驱动注册到platform总线
return platform_driver_register(&at91_i2c_driver);
}
执行此处时,platform的match将I2C总线设备和I2C总线设备驱动连接起来,并调用I2C设备驱动的probe,即at91_i2c_probe函数
④I2C 总线PROBE,设置并添加适配器
driver\i2c\busses\i2c-at91.c
//适配器通讯方法,定义
static struct i2c_algorithm at91_algorithm = {
.master_xfer = at91_xfer,
.functionality = at91_func,
};
static int __devinit at91_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct resource *res;
int rc;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c"))
return -EBUSY;
twi_base = ioremap(res->start, res->end - res->start + 1);
if (!twi_base) {
rc = -ENOMEM;
goto fail0;
}
twi_clk = clk_get(NULL, "twi_clk");
if (IS_ERR(twi_clk)) {
dev_err(&pdev->dev, "no clock defined\n");
rc = -ENODEV;
goto fail1;
}
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
if (adapter == NULL) {
dev_err(&pdev->dev, "can't allocate inteface!\n");
rc = -ENOMEM;
goto fail2;
}
/*-------------设置适配器成员--------------*/
sprintf(adapter->name, "AT91");
//设置适配器通讯方法
adapter->algo = &at91_algorithm;
adapter->class = I2C_CLASS_HWMON;
adapter->dev.parent = &pdev->dev;
/*-----------------------------------------------*/
platform_set_drvdata(pdev, adapter);
clk_enable(twi_clk); /* enable peripheral clock */
//TWI控制器初始化,使能,中断,频率等
at91_twi_hwinit(); /* initialize TWI controller */
//添加适配器
rc = i2c_add_adapter(adapter);
if (rc) {
dev_err(&pdev->dev, "Adapter %s registration failed\n",
adapter->name);
goto fail3;
}
dev_info(&pdev->dev, "AT91 i2c bus driver.\n");
…
}
driver\i2c\i2c-core.c
static LIST_HEAD(adapters); //全局适配器队列
static LIST_HEAD(drivers); //全局适配器驱动队列
int i2c_add_adapter(struct i2c_adapter *adap)
{
int id, res = 0;
struct list_head *item;
struct i2c_driver *driver;
mutex_lock(&core_lists);
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {
res = -ENOMEM;
goto out_unlock;
}
//获得idr号,之后用idr号得到参数adap指针地址
res = idr_get_new(&i2c_adapter_idr, adap, &id);
if (res < 0) {
if (res == -EAGAIN)
res = -ENOMEM;
goto out_unlock;
}
//将idr的ID赋值到adap->nr指针中
adap->nr = id & MAX_ID_MASK;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
//添加此适配器指针到全局adapters队列中
list_add_tail(&adap->list,&adapters);
INIT_LIST_HEAD(&adap->clients);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
printk(KERN_WARNING "**WARNING** I2C adapter driver [%s] "
"forgot to specify physical device; fix it!\n",
adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
/*------------------sysfs 文件系统相关---------------*/
res = device_register(&adap->dev);
if (res)
goto out_list;
res = device_create_file(&adap->dev, &dev_attr_name);
if (res)
goto out_unregister;
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
res = class_device_register(&adap->class_dev);
if (res)
goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/*--------------------------------sysfs end-------------------------*/
/* inform drivers of new adapters */
//遍历全局适配器驱动队列,调用每个驱动的attach_adapter,即pcf8563_attach()
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
//和 i2c_register_driver中调用的是同一个函数
driver->attach_adapter(adap);
}
…
}
platform总线注册到adapter添加过程结束。
⑤添加I2C适配器驱动
drivers\rtc\rtc-pcf8563.c
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "pcf8563",
},
.id = I2C_DRIVERID_PCF8563,
.attach_adapter = &pcf8563_attach, //添加适配器或驱动时调用
.detach_client = &pcf8563_detach,
};
static int __init pcf8563_init(void)
{
return i2c_add_driver(&pcf8563_driver);
}
driver\i2c\i2c-core.c
static inline int i2c_add_driver(struct i2c_driver *driver)
{
//注册 I2C适配器驱动
return i2c_register_driver(THIS_MODULE, driver);
}
static LIST_HEAD(adapters); //全局适配器队列
static LIST_HEAD(drivers); //全局适配器驱动队列
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
struct list_head *item;
struct i2c_adapter *adapter;
int res;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lists);
//添加驱动到全局适配器驱动队列
list_add_tail(&driver->list,&drivers);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
/* now look for instances of driver on our adapters */
//遍历全局适配器队列,为每个适配器调用attach_adapter,即pcf8563_attach()
if (driver->attach_adapter) {
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
//和i2c_add_adapter调用同一个函数
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
⑥driver->attach_adapter(adapter)调用到我们的pcf8563_attach()
drivers\rtc\rtc-pcf8563.c
static unsigned short normal_i2c[] = {0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
.forces = forces,
};
static int pcf8563_attach(struct i2c_adapter *adapter)
{
//addr_data存着I2C从设备的,即pcf8563的地址
return i2c_probe(adapter, &addr_data, pcf8563_probe);
}
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int i, err;
int adap_id = i2c_adapter_id(adapter);
/*---------------一些地址检查-------------*/
…
…
/* Normal entries are done last, unless shadowed by an ignore entry */
//检索从设备地址
for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
int j, ignore;
ignore = 0;
for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
j += 2) {
if ((address_data->ignore[j] == adap_id ||
address_data->ignore[j] == ANY_I2C_BUS)
&& address_data->ignore[j + 1]
== address_data->normal_i2c[i]) {
dev_dbg(&adapter->dev, "found ignore "
"parameter for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->ignore[j + 1]);
ignore = 1;
break;
}
}
if (ignore)
continue;
/*---------------------end-----------------------*/
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->normal_i2c[i]);
// address_data->normal_i2c[i] = 0x51, 从设备的地址
err = i2c_probe_address(adapter, address_data->normal_i2c[i],
-1, found_proc);
if (err)
return err;
}
return 0;
}
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int err;
/*--------------------------地址检查---------------------*/
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
/*--------------------------end----------------------------*/
/*----------------------检测确实有设备存在于这个地址------------------------*/
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
/*--------------------------------------------end-------------------------------------------------*/
/* Finally call the custom detection function */
// 传入的pcf8563_probe()调用
err = found_proc(adapter, addr, kind);
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
if (err == -ENODEV)
err = 0;
if (err)
dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
addr, err);
return err;
}
⑦pcf8563_probe()调用,设置client,绑定adapter
drivers\rtc\rtc-pcf8563.c
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct rtc_device *rtc;
int err = 0;
dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
/*-------设置client--------*/
//之后适配器的方法调用及从设备操作都是依靠这几个值
client->addr = address;
client->driver = &pcf8563_driver;
client->adapter = adapter;
strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE);
/*-------------end-----------*/
/* Verify the chip is really an PCF8563 */
if (kind < 0) {
if (pcf8563_validate_client(client) < 0) {
err = -ENODEV;
goto exit_kfree;
}
}
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
// RTC设备注册,参数pcf8563_rtc_ops为PCF8563实现的设备操作
rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev,
&pcf8563_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
i2c_set_clientdata(client, rtc);
return 0;
}
drivers\i2c\i2c-core.c
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
res = -EBUSY;
goto out_unlock;
}
//设置adapter绑定client,重要
list_add_tail(&client->list,&adapter->clients);
client->usage_count = 0;
/*-----------sysfs 相关--------------*/
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
res = device_register(&client->dev);
if (res)
goto out_list;
res = device_create_file(&client->dev, &dev_attr_client_name);
if (res)
goto out_unregister;
mutex_unlock(&adapter->clist_lock);
/*----------------------end------------------*/
/*------------适配器对client的额外设置,adapter->client_register,未定义------------*/
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x\n",
client->name, client->addr);
}
}
/*------------------------------------------------end--------------------------------------------------*/
…
}
到此I2C框架代码搭建完成,我们可以完成设备操作函数,即pcf8563_rtc_ops
以上内容来至下面的文章,具体参考下面的文章,RTC模型介绍很详细
http://blog.youkuaiyun.com/yaozhenguo2006/article/details/6824970
//buf是缓冲区,count是发送的字节数
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
/*------------------设置i2c结构数据包-----------------*/
//从设备地址
msg.addr = client->addr;
//写标志位设置,保留10地址位模式
msg.flags = client->flags & I2C_M_TEN;
//数据字节数
msg.len = count;
//发送的缓冲
msg.buf = (char *)buf;
/*---------------------------end----------------------------*/
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
mutex_lock_nested(&adap->bus_lock, adap->level);
//重点,在添加适配器时设置过algo成员,此时正式使用适配器的algo成员的master_xfer方法发送数据到从设备,master_xfer方法一般定义在driver/i2c/algos/目录下
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
②PCF8563寄存器地址
#define PCF8563_REG_SC 0x02 秒
#define PCF8563_REG_MN 0x03 分
#define PCF8563_REG_HR 0x04 小时
#define PCF8563_REG_DM 0x05 日
#define PCF8563_REG_DW 0x06 星期(非BCD)
#define PCF8563_REG_MO 0x07 月
#define PCF8563_REG_YR 0x08 年
③RTC设备操作函数
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
④设置时间操作
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
int i, err;
unsigned char buf[9];
unsigned char data[2];
/* hours, minutes and seconds */
/*-------------------将用户传入的datetime值转完成BCD值--------------------*/
buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
/* month, 1 - 12 */
buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
/* year and century */
buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
if (tm->tm_year < 100)
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
/*------------------------------------------end------------------------------------------------*/
/* write register's data */
//设置PCF8563七个时间寄存器
for (i = 0; i < 7; i++) {
data[0] = PCF8563_REG_SC + i;
data[1] = buf[PCF8563_REG_SC + i] ;
//分别设置PCF8563各寄存器器
err = i2c_master_send(client, data, sizeof(data));
if (err != sizeof(data)) {
dev_err(&client->dev,
"%s: err=%d addr=%02x, data=%02x\n",
__FUNCTION__, err, data[0], data[1]);
return -EIO;
}
};
return 0;
}
⑤获得时间操作
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
unsigned char buf[13] = { PCF8563_REG_ST1 };
struct i2c_msg msgs[] = {
//设置PCF8563寄存器指针为0,即第一次写地址,以后的读取PCF8563的地址会自动递增
{ client->addr, 0, 1, buf }, /* setup read ptr */
//从PCF8563中读13字节数据
{ client->addr, I2C_M_RD, 13, buf }, /* read status + date */
};
/* read registers */
//使用适配器方法发送,I2C数据格式包
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
//低电压检测位,判断VDD脚电压,首次使用芯片时,此位为1
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
dev_info(&client->dev,
"low voltage detected, date/time is not reliable.\n");
/*---------------------将PCF8563的寄存器值转成二进制传入tm结构------------------*/
tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
+ (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 0 : 100);
/*-------------------------------------------end------------------------------------------------------*/
//判断取出的值是否是合法的时间值
if (rtc_valid_tm(tm) < 0)
dev_err(&client->dev, "retrieved date/time is not valid.\n");
return 0;
}
1. LINUX-2.6.20的内核
2. CPU是AT91SAM9260
3. PCF8563的I2C驱动
大体过程:
1. 为什么内核要有这么多模型
2. platform总线、设备、驱动模型,简单的介绍
3. I2C模型所涉及到的程序文件位置及简介
4. 关键数据结构
5. I2C驱动模型流程图
6. 按流程图顺序分析代码
7. RTC模型简单介绍
8. PCF8563设备操作驱动
为什么内核要有这么多模型
linux 2.6内核引入了设备模型,因为随着linux支持的设备越来越多,拓扑结构越来越复杂,其实另一个目的是为了能够对设备进行有效管理。其实,建立设备模型的初衷很直白——为了省电。即是为了实现一些动态管理电源的功能。从整体上描述,大概模型就如下图所示:

从上图中可以看出,Linux设备模型就是"总线、设备、驱动、类"这四个概念之前的相互关系;这也是Linux2.6内核抽象出来的用于管理系统中所有设备的模型图;
简单地描述设备模型的层次关系如下:
1、驱动核心中可以注册多种类型的总线(bus_type);
2、每一种类型的总线下面可以挂载许多设备(kset,device);
3、每一种类型的总线可以使用很多设备驱动(kset,device_driver);
4、每一个驱动程序可以管理一组设备;
这种基本关系的建立源于实际系统中各种总线、设备、驱动、类结构的抽象;

由上图可知,LINUX是模型是树型,呈金字塔型,图中的Hub就类似于I2C模型的适配器,这就如同我们硬件上实现使用的电源适配器一样,内核可以使用此模拟适配器进行电源管理。
platform总线、设备、驱动模型,简单的介绍
platform 机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform device 提供的标准接口进行申请并使用。这样提高了驱动和资源管理的独
立性,这样拥有更好的可移植性。platform 机制的本身使用并不复杂,由两部分组成:
platform_device 和platfrom_driver。Platform driver 通过platform bus 获取platform_device。
通常情况下只要和内核本身运行依赖性不大的外围设备,相对独立的,拥有各自独立的资源
(地址总线和IRQs),都可以用 platform_driver 来管理,而timer,irq 等小系统之内的设备
则最好不用platfrom_driver 机制。
platform_device 最大的特定是CPU 直接寻址设备的寄存器空间,即使对于其他总线设备,
设备本身的寄存器无法通过CPU 总线访问,但总线的controller 仍然需要通过platform bus
来管理。
总之,platfrom_driver 的根本目的是为了统一管理系统的外设资源,为驱动程序提供统一的
接口来访问系统资源,将驱动和资源分离,提高程序的可移植性。
I2C模型所涉及到的程序文件位置及简介,附老图一张
I2C模型相关目录文件:1. driver/base/platform.c :注册platform设备总线
2. arch\arm\mach-at91rm9200\board-sam9260k.c :添加I2C总线设备到platform总线上
3. arch\arm\mach-at91rm9200\at91sam9260_devices.c :platform片上设备文件定义,board-sam9260.c中调用
4. driver\i2c\busses\i2c-at91.c :I2C总线设备驱动注册到platform总线
5. driver\i2c\i2c-core.c :I2C模型使用的API,i2c-at91.c中调用,rtc-pcf8563.c调用
6. driver\i2c\algos :此目录为操作I2C适配器的方法,即操作I2C控制器的方法,我所用的内核将这些函数放到了i2c-at91.c中
7. drivers\i2c\i2c-dev.c:内核提供的一接口,可以让用户层编写控制I2C控制方法,我们没有使用
8. drivers\rtc\rtc-pcf8563.c :pcf8563设备驱动
9. drivers\rtc\class.c :RTC设备注册API,RTC模型一部分
以下是I2C模型框架图,更多的内容请参考:
http://hi.baidu.com/kebey2004/item/a7f08c4554607caa60d7b9f2

关键数据结构
I2C驱动有两部分组成:I2C总线驱动和I2C设备构成。I2C总线驱动是对适配器端的实现,其含有适配器数据结构struct i2c_adapter,适配器算法数据结构struct i2c_algorithm。I2C设备驱动是对设备端的实现和控制,其含有设备驱动结构i2c_driver和设备客户端结构struct i2c_client。
struct i2c_adapter {
struct module *owner;
unsigned int id;
unsigned int class;
struct i2c_algorithm *algo;//总线通讯数据结构体
void *algo_data; //用于保存包含适配器的私有数据结构
int (*client_register)(struct i2c_client *);//客户端注册函数
int (*client_unregister)(struct i2c_client *);//客户端注销函数
struct semaphore bus_lock;
struct semaphore clist_lock;
int timeout;
int retries; //重试次数
struct device dev; //适配器设备
struct class_device class_dev; //类设备
int nr;
struct list_head clients; //客户端链表
struct list_head list; //适配器链表
char name[I2C_NAME_SIZE]; //适配器名称
struct completion dev_released;
struct completion class_dev_released;
};
struct i2c_algorithm {
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg *msgs,
int num); //i2c总线传输函数
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data * data); //smbus总线传输函数
int (*slave_send)(struct i2c_adapter *,char*,int); //适配器为主模式时发送函数
int (*slave_recv)(struct i2c_adapter *,char*,int); //适配器为主模式时接收函数
int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long);
u32 (*functionality) (struct i2c_adapter *); //适配器支持功能
};以上两个数据结构为总线驱动需要设置并初始化。
struct i2c_driver {
struct module *owner;
char name[32]; //驱动名称
int id;
unsigned int class;
unsigned int flags; //I2C_DF_NOTIFY用于当设备依附或脱离时通知总线
int (*attach_adapter)(struct i2c_adapter *);//依附适配器函数
int (*detach_adapter)(struct i2c_adapter *);//脱离适配器函数
int (*detach_client)(struct i2c_client *); //脱离客户端函数
int (*command)(struct i2c_client *client,unsigned int cmd, void *arg);
struct device_driver driver; //设备驱动结构体
struct list_head list; //链表头
};
struct i2c_client {
unsigned int flags;
unsigned short addr; // 低7为设备地址
struct i2c_adapter *adapter; //依附的适配器
struct i2c_driver *driver; //依附的驱动结构
int usage_count;
struct device dev;
struct list_head list; //客户端链表
char name[I2C_NAME_SIZE];//客户端名称
struct completion released;
};以上两个数据结构为设备驱动需要设置并初始化。
Intel制定了SMBus标准用于低速通讯。SMBus二线接口与I2C接口非常相似。SMBus也使用一条数据线(SMBDATA)和一条时钟线(SMBCLK)实现通讯。I2C接口和SMBus接口的主要区别是最大和最小时钟速度。SMBCLK必须在10kHz和100kHz之间。SMBCLK和SMBDATA线也需要上拉电阻。3V供电时上拉电阻大于8.5k ,5V供电时上拉电阻大于14k 。SMBus工作电压范围在3V和5V之间,大于2.1V为高电平,低于0.8V为低电平。struct i2c_adapter对应于物理上的一个适配器,而struct i2c_algorithm对应于一套通讯方法。i2c_algorithm提供一些控制适配器发送或接收函数,对于i2c总线需要初始化master_xfer函数,对于smbus总线需要初始化smbus_xfer函数。master_xfer函数是以i2c_msg为单位进行控制的,其结构如下:
struct i2c_msg {
__u16 addr; //从设备地址
__u16 flags; //动作标志:读或写
#define I2C_M_TEN 0x10 //器件地址是10Bit
#define I2C_M_RD 0x01
#define I2C_M_NOSTART 0x4000 //意味当前i2c_msg不发送start信号
#define I2C_M_REV_DIR_ADDR 0x2000 //把读写标志位反转
#define I2C_M_IGNORE_NAK 0x1000//当前i2c_msg忽略I2C器件的ack和nack信号。
#define I2C_M_NO_RD_ACK 0x0800 //表示在正行读操作时不去ACK
__u16 len; //信息长度
__u8 *buf; //信息缓冲区首地址
};
i2c_driver和i2c_client用于控制设备驱动方面的结构。当i2c_driver->attach_adapter探测到物理设备后,因为i2c_client对应一个真实的物理设备则把探测到的i2c_client->adapter指向其依附的适配器的struct i2c_adapter 结构,把i2c_client->driver指向其依附的 i2c_driver结构.其注册和注销的函数分别为 i2c_attach_client和i2c_detach_client.
总线驱动需要定义一个包含 struct i2c_adapter的私有数据结构,用 i2c_adapter->algo_data指向它即可。
在总线驱动中需要探测并初始化适配器,分配一下IO地址和中断资源。
定义并初始化i2c_algorithm,依据总线类型为i2c或是smbus定义 master_xfer或smbus_xfer函数。
I2C模型流程图
大方框括住的代码是同一个函数中的代码,框图中的函数名可以在内核中进行搜索来熟悉这个过程。

按流程图顺序分析代码
①driver/base/platform.c :注册platform设备总线struct bus_type platform_bus_type = {
.name = "platform",
.dev_attrs = platform_dev_attrs,
.match = platform_match, //总线match 设备和驱动
.uevent = platform_uevent,
.suspend = platform_suspend,
.suspend_late = platform_suspend_late,
.resume_early = platform_resume_early,
.resume = platform_resume,
};
struct device platform_bus = {
.bus_id = "platform",
};
//平台总线注册到内核
int __init platform_bus_init(void)
{
device_register(&platform_bus); //平台总线设备注册到内核
return bus_register(&platform_bus_type); //总线注册
}
②I2C总线设备添加platform总线
arch\arm\mach-at91rm9200\board-sam9260k.c :添加I2C总线设备到platform总线上
static void __init ek_board_init(void)
{
/* Serial */
at91_add_device_serial();
/* USB Host */
…
…
at91_add_device_i2c(); //添加I2C设备
…
}
arch\arm\mach-at91rm9200\at91sam9260_devices.c :platform片上设备文件定义
static struct platform_device at91sam9260_twi_device = {
.name = "at91_i2c",
.id = -1,
.resource = twi_resources,
.num_resources = ARRAY_SIZE(twi_resources),
};
void __init at91_add_device_i2c(void)
{
/* pins used for TWI interface */
at91_set_A_periph(AT91_PIN_PA23, 0); /* TWD */
at91_set_multi_drive(AT91_PIN_PA23, 1);
at91_set_A_periph(AT91_PIN_PA24, 0); /* TWCK */
at91_set_multi_drive(AT91_PIN_PA24, 1);
//添加I2C总线设备到platform总线上
platform_device_register(&at91sam9260_twi_device);
}
③driver\i2c\busses\i2c-at91.c :I2C总线设备驱动注册到platform总线
static struct platform_driver at91_i2c_driver = {
.probe = at91_i2c_probe,
.remove = __devexit_p(at91_i2c_remove),
.suspend = at91_i2c_suspend,
.resume = at91_i2c_resume,
.driver = {
.name = "at91_i2c",
.owner = THIS_MODULE,
},
};
static int __init at91_i2c_init(void)
{
// I2C总线设备驱动注册到platform总线
return platform_driver_register(&at91_i2c_driver);
}
执行此处时,platform的match将I2C总线设备和I2C总线设备驱动连接起来,并调用I2C设备驱动的probe,即at91_i2c_probe函数
④I2C 总线PROBE,设置并添加适配器
driver\i2c\busses\i2c-at91.c
//适配器通讯方法,定义
static struct i2c_algorithm at91_algorithm = {
.master_xfer = at91_xfer,
.functionality = at91_func,
};
static int __devinit at91_i2c_probe(struct platform_device *pdev)
{
struct i2c_adapter *adapter;
struct resource *res;
int rc;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENXIO;
if (!request_mem_region(res->start, res->end - res->start + 1, "at91_i2c"))
return -EBUSY;
twi_base = ioremap(res->start, res->end - res->start + 1);
if (!twi_base) {
rc = -ENOMEM;
goto fail0;
}
twi_clk = clk_get(NULL, "twi_clk");
if (IS_ERR(twi_clk)) {
dev_err(&pdev->dev, "no clock defined\n");
rc = -ENODEV;
goto fail1;
}
adapter = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
if (adapter == NULL) {
dev_err(&pdev->dev, "can't allocate inteface!\n");
rc = -ENOMEM;
goto fail2;
}
/*-------------设置适配器成员--------------*/
sprintf(adapter->name, "AT91");
//设置适配器通讯方法
adapter->algo = &at91_algorithm;
adapter->class = I2C_CLASS_HWMON;
adapter->dev.parent = &pdev->dev;
/*-----------------------------------------------*/
platform_set_drvdata(pdev, adapter);
clk_enable(twi_clk); /* enable peripheral clock */
//TWI控制器初始化,使能,中断,频率等
at91_twi_hwinit(); /* initialize TWI controller */
//添加适配器
rc = i2c_add_adapter(adapter);
if (rc) {
dev_err(&pdev->dev, "Adapter %s registration failed\n",
adapter->name);
goto fail3;
}
dev_info(&pdev->dev, "AT91 i2c bus driver.\n");
…
}
driver\i2c\i2c-core.c
static LIST_HEAD(adapters); //全局适配器队列
static LIST_HEAD(drivers); //全局适配器驱动队列
int i2c_add_adapter(struct i2c_adapter *adap)
{
int id, res = 0;
struct list_head *item;
struct i2c_driver *driver;
mutex_lock(&core_lists);
if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0) {
res = -ENOMEM;
goto out_unlock;
}
//获得idr号,之后用idr号得到参数adap指针地址
res = idr_get_new(&i2c_adapter_idr, adap, &id);
if (res < 0) {
if (res == -EAGAIN)
res = -ENOMEM;
goto out_unlock;
}
//将idr的ID赋值到adap->nr指针中
adap->nr = id & MAX_ID_MASK;
mutex_init(&adap->bus_lock);
mutex_init(&adap->clist_lock);
//添加此适配器指针到全局adapters队列中
list_add_tail(&adap->list,&adapters);
INIT_LIST_HEAD(&adap->clients);
/* Add the adapter to the driver core.
* If the parent pointer is not set up,
* we add this adapter to the host bus.
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
printk(KERN_WARNING "**WARNING** I2C adapter driver [%s] "
"forgot to specify physical device; fix it!\n",
adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
/*------------------sysfs 文件系统相关---------------*/
res = device_register(&adap->dev);
if (res)
goto out_list;
res = device_create_file(&adap->dev, &dev_attr_name);
if (res)
goto out_unregister;
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
res = class_device_register(&adap->class_dev);
if (res)
goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
/*--------------------------------sysfs end-------------------------*/
/* inform drivers of new adapters */
//遍历全局适配器驱动队列,调用每个驱动的attach_adapter,即pcf8563_attach()
list_for_each(item,&drivers) {
driver = list_entry(item, struct i2c_driver, list);
if (driver->attach_adapter)
/* We ignore the return code; if it fails, too bad */
//和 i2c_register_driver中调用的是同一个函数
driver->attach_adapter(adap);
}
…
}
platform总线注册到adapter添加过程结束。
⑤添加I2C适配器驱动
drivers\rtc\rtc-pcf8563.c
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "pcf8563",
},
.id = I2C_DRIVERID_PCF8563,
.attach_adapter = &pcf8563_attach, //添加适配器或驱动时调用
.detach_client = &pcf8563_detach,
};
static int __init pcf8563_init(void)
{
return i2c_add_driver(&pcf8563_driver);
}
driver\i2c\i2c-core.c
static inline int i2c_add_driver(struct i2c_driver *driver)
{
//注册 I2C适配器驱动
return i2c_register_driver(THIS_MODULE, driver);
}
static LIST_HEAD(adapters); //全局适配器队列
static LIST_HEAD(drivers); //全局适配器驱动队列
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
struct list_head *item;
struct i2c_adapter *adapter;
int res;
/* add the driver to the list of i2c drivers in the driver core */
driver->driver.owner = owner;
driver->driver.bus = &i2c_bus_type;
res = driver_register(&driver->driver);
if (res)
return res;
mutex_lock(&core_lists);
//添加驱动到全局适配器驱动队列
list_add_tail(&driver->list,&drivers);
pr_debug("i2c-core: driver [%s] registered\n", driver->driver.name);
/* now look for instances of driver on our adapters */
//遍历全局适配器队列,为每个适配器调用attach_adapter,即pcf8563_attach()
if (driver->attach_adapter) {
list_for_each(item,&adapters) {
adapter = list_entry(item, struct i2c_adapter, list);
//和i2c_add_adapter调用同一个函数
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return 0;
}
⑥driver->attach_adapter(adapter)调用到我们的pcf8563_attach()
drivers\rtc\rtc-pcf8563.c
static unsigned short normal_i2c[] = {0x51, I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_addr,
.probe = ignore,
.ignore = ignore,
.forces = forces,
};
static int pcf8563_attach(struct i2c_adapter *adapter)
{
//addr_data存着I2C从设备的,即pcf8563的地址
return i2c_probe(adapter, &addr_data, pcf8563_probe);
}
int i2c_probe(struct i2c_adapter *adapter,
struct i2c_client_address_data *address_data,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int i, err;
int adap_id = i2c_adapter_id(adapter);
/*---------------一些地址检查-------------*/
…
…
/* Normal entries are done last, unless shadowed by an ignore entry */
//检索从设备地址
for (i = 0; address_data->normal_i2c[i] != I2C_CLIENT_END; i += 1) {
int j, ignore;
ignore = 0;
for (j = 0; address_data->ignore[j] != I2C_CLIENT_END;
j += 2) {
if ((address_data->ignore[j] == adap_id ||
address_data->ignore[j] == ANY_I2C_BUS)
&& address_data->ignore[j + 1]
== address_data->normal_i2c[i]) {
dev_dbg(&adapter->dev, "found ignore "
"parameter for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->ignore[j + 1]);
ignore = 1;
break;
}
}
if (ignore)
continue;
/*---------------------end-----------------------*/
dev_dbg(&adapter->dev, "found normal entry for adapter %d, "
"addr 0x%02x\n", adap_id,
address_data->normal_i2c[i]);
// address_data->normal_i2c[i] = 0x51, 从设备的地址
err = i2c_probe_address(adapter, address_data->normal_i2c[i],
-1, found_proc);
if (err)
return err;
}
return 0;
}
static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
int (*found_proc) (struct i2c_adapter *, int, int))
{
int err;
/*--------------------------地址检查---------------------*/
/* Make sure the address is valid */
if (addr < 0x03 || addr > 0x77) {
dev_warn(&adapter->dev, "Invalid probe address 0x%02x\n",
addr);
return -EINVAL;
}
/* Skip if already in use */
if (i2c_check_addr(adapter, addr))
return 0;
/*--------------------------end----------------------------*/
/*----------------------检测确实有设备存在于这个地址------------------------*/
/* Make sure there is something at this address, unless forced */
if (kind < 0) {
if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL) < 0)
return 0;
/* prevent 24RF08 corruption */
if ((addr & ~0x0f) == 0x50)
i2c_smbus_xfer(adapter, addr, 0, 0, 0,
I2C_SMBUS_QUICK, NULL);
}
/*--------------------------------------------end-------------------------------------------------*/
/* Finally call the custom detection function */
// 传入的pcf8563_probe()调用
err = found_proc(adapter, addr, kind);
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
if (err == -ENODEV)
err = 0;
if (err)
dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
addr, err);
return err;
}
⑦pcf8563_probe()调用,设置client,绑定adapter
drivers\rtc\rtc-pcf8563.c
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
struct rtc_device *rtc;
int err = 0;
dev_dbg(adapter->class_dev.dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
/*-------设置client--------*/
//之后适配器的方法调用及从设备操作都是依靠这几个值
client->addr = address;
client->driver = &pcf8563_driver;
client->adapter = adapter;
strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE);
/*-------------end-----------*/
/* Verify the chip is really an PCF8563 */
if (kind < 0) {
if (pcf8563_validate_client(client) < 0) {
err = -ENODEV;
goto exit_kfree;
}
}
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
// RTC设备注册,参数pcf8563_rtc_ops为PCF8563实现的设备操作
rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev,
&pcf8563_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
i2c_set_clientdata(client, rtc);
return 0;
}
drivers\i2c\i2c-core.c
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
res = -EBUSY;
goto out_unlock;
}
//设置adapter绑定client,重要
list_add_tail(&client->list,&adapter->clients);
client->usage_count = 0;
/*-----------sysfs 相关--------------*/
client->dev.parent = &client->adapter->dev;
client->dev.driver = &client->driver->driver;
client->dev.bus = &i2c_bus_type;
client->dev.release = &i2c_client_release;
snprintf(&client->dev.bus_id[0], sizeof(client->dev.bus_id),
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
res = device_register(&client->dev);
if (res)
goto out_list;
res = device_create_file(&client->dev, &dev_attr_client_name);
if (res)
goto out_unregister;
mutex_unlock(&adapter->clist_lock);
/*----------------------end------------------*/
/*------------适配器对client的额外设置,adapter->client_register,未定义------------*/
if (adapter->client_register) {
if (adapter->client_register(client)) {
dev_dbg(&adapter->dev, "client_register "
"failed for client [%s] at 0x%02x\n",
client->name, client->addr);
}
}
/*------------------------------------------------end--------------------------------------------------*/
…
}
到此I2C框架代码搭建完成,我们可以完成设备操作函数,即pcf8563_rtc_ops
RTC内核模型的简单介绍,附图
RTC(real time clock)实时时钟,主要作用是给Linux系统提供时间。RTC因为是电池供电的,所以掉电后时间不丢失。Linux内核把RTC用作“离线”的时间与日期维护器。当Linux内核启动时,它从RTC中读取时间与日期,作为基准值。在运行期间内核完全抛开RTC,以软件的形式维护系统的当前时间与日期,并在需要时将时间回写RTC芯片。另外如果RTC提供了IRQ中断并且可以定时,那么RTC还可以作为内核睡眠时唤醒内核的闹钟。应用程序可以用RTC提供的周期中断做一些周期的任务。 linux有两种rtc驱动的接口,一个是老的接口,专门用在PC机上的。另外一钟新接口是基于linux设备驱动程序的。这个新的接口创建了一个RTC驱动模型,实现了RTC的大部分基本功能。而底层驱动无须考虑一些功能的实现,只需将自己注册的RTC核心中,其他工作由RTC核心来完成。
以上内容来至下面的文章,具体参考下面的文章,RTC模型介绍很详细
http://blog.youkuaiyun.com/yaozhenguo2006/article/details/6824970
PCF8563设备操作驱动
①使用到的两个重要函数//buf是缓冲区,count是发送的字节数
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
{
int ret;
struct i2c_adapter *adap=client->adapter;
struct i2c_msg msg;
/*------------------设置i2c结构数据包-----------------*/
//从设备地址
msg.addr = client->addr;
//写标志位设置,保留10地址位模式
msg.flags = client->flags & I2C_M_TEN;
//数据字节数
msg.len = count;
//发送的缓冲
msg.buf = (char *)buf;
/*---------------------------end----------------------------*/
ret = i2c_transfer(adap, &msg, 1);
/* If everything went ok (i.e. 1 msg transmitted), return #bytes
transmitted, else error code. */
return (ret == 1) ? count : ret;
}
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
int ret;
if (adap->algo->master_xfer) {
mutex_lock_nested(&adap->bus_lock, adap->level);
//重点,在添加适配器时设置过algo成员,此时正式使用适配器的algo成员的master_xfer方法发送数据到从设备,master_xfer方法一般定义在driver/i2c/algos/目录下
ret = adap->algo->master_xfer(adap,msgs,num);
mutex_unlock(&adap->bus_lock);
return ret;
} else {
dev_dbg(&adap->dev, "I2C level transfers not supported\n");
return -ENOSYS;
}
}
②PCF8563寄存器地址
#define PCF8563_REG_SC 0x02 秒
#define PCF8563_REG_MN 0x03 分
#define PCF8563_REG_HR 0x04 小时
#define PCF8563_REG_DM 0x05 日
#define PCF8563_REG_DW 0x06 星期(非BCD)
#define PCF8563_REG_MO 0x07 月
#define PCF8563_REG_YR 0x08 年
③RTC设备操作函数
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
④设置时间操作
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
int i, err;
unsigned char buf[9];
unsigned char data[2];
/* hours, minutes and seconds */
/*-------------------将用户传入的datetime值转完成BCD值--------------------*/
buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
/* month, 1 - 12 */
buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
/* year and century */
buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
if (tm->tm_year < 100)
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
/*------------------------------------------end------------------------------------------------*/
/* write register's data */
//设置PCF8563七个时间寄存器
for (i = 0; i < 7; i++) {
data[0] = PCF8563_REG_SC + i;
data[1] = buf[PCF8563_REG_SC + i] ;
//分别设置PCF8563各寄存器器
err = i2c_master_send(client, data, sizeof(data));
if (err != sizeof(data)) {
dev_err(&client->dev,
"%s: err=%d addr=%02x, data=%02x\n",
__FUNCTION__, err, data[0], data[1]);
return -EIO;
}
};
return 0;
}
⑤获得时间操作
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
unsigned char buf[13] = { PCF8563_REG_ST1 };
struct i2c_msg msgs[] = {
//设置PCF8563寄存器指针为0,即第一次写地址,以后的读取PCF8563的地址会自动递增
{ client->addr, 0, 1, buf }, /* setup read ptr */
//从PCF8563中读13字节数据
{ client->addr, I2C_M_RD, 13, buf }, /* read status + date */
};
/* read registers */
//使用适配器方法发送,I2C数据格式包
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
//低电压检测位,判断VDD脚电压,首次使用芯片时,此位为1
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
dev_info(&client->dev,
"low voltage detected, date/time is not reliable.\n");
/*---------------------将PCF8563的寄存器值转成二进制传入tm结构------------------*/
tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
+ (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 0 : 100);
/*-------------------------------------------end------------------------------------------------------*/
//判断取出的值是否是合法的时间值
if (rtc_valid_tm(tm) < 0)
dev_err(&client->dev, "retrieved date/time is not valid.\n");
return 0;
}