LINUX I2C模型 RTC模型 详细分析

转载请注明出处玮璘博客: http://www.wangweilin.name/qrx_456.html

注意:
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.csdn.net/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;
}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值