linux i2c模型 rtc模型 详细分析,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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值