LINUX I2C模型 RTC模型 详细分析

来源:互联网 发布:三菱plcfx3u编程方法 编辑:程序博客网 时间:2024/06/03 14:10
注意:
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 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 租房合同弄丢了怎么办 买房的合同丢了怎么办 押金的收据丢了怎么办 房东的合同掉了怎么办 个人档案里单位没有放合同怎么办 签的合同掉了怎么办 一方合同弄丢了怎么办 合同丢了怎么办如何补 签了定金合同对方违约怎么办 医学出生证明丢了怎么办 易通行出站未刷怎么办 炭烧酸奶过期了怎么办 西安建行etc坏了怎么办 电机在设备壳体中拔不出来怎么办 公司变更股东不能亲临现场怎么办? 公司股东变更老股东不签字怎么办 公司变更地址股东不签字怎么办 公司变更股份股东不签字怎么办 公司股东离职股东没变更过来怎么办 河南省宋基投资公司欠钱怎么办 曲江楼观2O18怎么办 华旭金卡身份证扫描不了怎么办 水表里有钱没水怎么办? ff14过图速度慢怎么办 想让电表跑的慢怎么办 家里电表突然没有电了怎么办 电表不识别电卡怎么办 家里水表不转了怎么办 车管所体检色弱怎么办 煤气押金单没了怎么办 中国建设银行登录密码忘了怎么办 中国建设银行登录密码忘记了怎么办 公司车辆怎么办换新能源牌 杭州新能源汽车牌照外地人怎么办 建行登录密码忘了怎么办 新捷达epc灯亮怎么办 捷达车玻璃升降偏离怎么办 交金中断一个月怎么办 博士拟录取没导师怎么办 保研联系导师后怎么办 特别害怕和导师交流怎么办