openwrt 增加RTC(MCP7940 I2C总线)驱动详解

来源:互联网 发布:c语言预处理指令放在哪 编辑:程序博客网 时间:2024/06/03 23:54

一、硬件平台

    1.1 控制器:MT7620(A9内核)
    1.2 RTC芯片:MCP7940(I2C总线)


二、软件平台

    2.1、开发环境:Ubuntu12.04 
    2.2、软件版本:openwrt 官方15.05版本SDK开发包(CHAOS CALMER 15.05版本)


三、功能说明

    本文章所选择的目标芯片为MT7620,profile 选择的为“Xiaomi MiWiFi Mini ”。

    3.1、在openwrt 系统上,移植mcp7940的rtc芯片驱动。

    3.2、在openwrt系统上,增加对i2c总线的支持。

    注意事项:openwrt系统比较奇怪,在menuconfig配置中,配置了i2c,仍然不能支持。需要另外修改“*.dts”文件,才能支持i2c总线。


四、操作步骤

   4.1 增加系统对于 i2c 总线的支持

    对于系统增加i2c总线的支持,需要修改2个地方
    1、openwrt增加对i2c支持。
    2、修改dts文件,增加对i2c支持。


   注意:对于增加I2C总线驱动支持,可以 make menuconfig或者make kernel_menuconfig中配置,两者只需要配置一个即可,如果同事配置两个地方(4.1.1和4.1.2都配置),虽然也可以正常驱动I2C,但是将会出现错误提示信息:
   如错误提示所言,重复引用了I2C的一些函数。
[   18.100000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.110000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.120000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.130000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.140000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.150000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.160000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)[   18.170000] i2c_core: exports duplicate symbol __i2c_transfer (owned by kernel)

    4.1.1 配置openwrt 的I2C

    在openwrt 目录下,执行“make menuconfig”命令。

    进入菜单 Kernel modules  --->I2C support  --->,在菜单选项中,配置如图4-1所示。


图4-1 I2C support

   

    4.1.2 配置目标芯片的 I2C 

    在openwrt 目录下,执行"make kernel_menuconfig"命令,对目标芯片内核进行配置。

    进入菜单, Device Drivers  ---> I2C support  --->,如图4-2所示

     

图4-2 目标芯片的I2C总线配置


    在图4-2中,选择“I2C Hardware Bus support  ---> ”,配置I2C 硬件总线的支持,选择“Ralink I2C Controller”,如图4-3所示。


图4-3 I2C Hardware Bus support

       

4.1.3 修改DTS配置文件

    默认的openwrt系统,没有对I2C总线的支持,需要自己修改DTS配置文件。由于本文章选择的芯片为MT7620A,目标profile 选择的为“Xiaomi MiWiFi Mini”,故需要修改的文件为“XIAOMI-MIWIFI-MINI.dts”

    文件的路径为“openwrt/target/linux/ramips/dts/XIAOMI-MIWIFI-MINI.dts”

    修改之前的DTS文件:

        palmbus@10000000 {                gpio0: gpio@600 {                        status = "okay";                };                gpio1: gpio@638 {                        status = "okay";                };                gpio2: gpio@660 {                        status = "okay";                };                spi@b00 {                        status = "okay";                        m25p80@0 {

    在palmbus节点中,增加MT7620A对I2C总线和RTC芯片的支持,修改之后的DTS文件:

        palmbus@10000000 {                gpio0: gpio@600 {                        status = "okay";                };                gpio1: gpio@638 {                        status = "okay";                };                gpio2: gpio@660 {                        status = "okay";                };                i2c@900 {                        compatible ="ralink,mt7620a-i2c", "ralink,rt2880-i2c";                        reg= <0x900 0x100>;                        resets = <&rstctrl 16>;                        reset-names= "i2c";                        #address-cells = <1>;                        #size-cells= <0>;                        status= "okay";                        rtc@6f {                                compatible = "mcp,mcp7940";                                reg = <0x6f>;                        };                 };                spi@b00 {                        status = "okay";                        m25p80@0 {


    说明:

    1、i2c@900为MT7620A的I2C节点;

    2、对于MCP7940芯片,通过查找芯片手册,知道其通信地址为0x6f。如果需要换成其他的芯片,则将对应的地址0x6f都改成其对应的地址即可。

    3、对于RTC的名称描述也需要注意,compatible = "mcp,mcp7940",其中mcp7940对应本文5.1章节,驱动程序rtc_mcp7940.c中第93行,struct i2c_device_id的名称。如果名字不匹配,则会导致驱动程序执行到probe函数不成功,导致rtc驱动加载失败。

static const struct i2c_device_id mcp7940_id[] = {        { "mcp7940", mcp7940 },        { }};


    4、如果I2C的引脚,没有设置,则默认可能工作在GPIO模式,特别注意!在本文中的RTC驱动程序,将I2C的引脚设置为了I2C模式,所以在DTS文件中,就没有再设置I2C引脚的工作模式。如果自己的RTC驱动程序中,没有设置I2C引脚,请在DTS文件中设置其工作为I2C模式,以免它默认工作在GPIO模式中!

                i2c@900 {                        compatible ="ralink,mt7620a-i2c", "ralink,rt2880-i2c";                        reg= <0x900 0x100>;                        resets = <&rstctrl 16>;                        reset-names= "i2c";                        #address-cells = <1>;                        #size-cells= <0>;                        status= "okay";                        rtc@6f {                                compatible = "mcp,mcp7940";                                reg = <0x6f>;                        };                 };

    

    如果需要修改为ds1307芯片,则只需要将其中的rtc地址和comatible修改即可。ds1307的通信地址为0x68,则修改如下:

                        rtc@68 {                                compatible = "dallas,ds1307";                                reg = <0x68>;                        };


4.2 增加对RTC的支持

    openwrt系统增加对RTC的支持,需要两个操作:
    1、修改target文件,增加RTC支持。
    2、修改内核配置,增加RTC支持和配置。

4.2.1 修改target文件

    系统在./scripts/medatata.pl中判断并处理RTC_SUPPORT开关,分析之后,原来是在 target/linux/ramips/mt7620/target.mk中,将原始的内容:

FEATURES+=usb

修改为:
FEATURES+=usb rtc

即可打开mt7620对rtc的支持。修改之后,target.mk内容如下:

## Copyright (C) 2009 OpenWrt.org#SUBTARGET:=mt7620BOARDNAME:=MT7620 based boardsARCH_PACKAGES:=ramips_24kecFEATURES+=usb rtcCPU_TYPE:=24kecCPU_SUBTYPE:=dspDEFAULT_PACKAGES += kmod-rt2800-pci kmod-rt2800-socdefine Target/Description        Build firmware images for Ralink MT7620 based boards.endef


4.2.2 配置内核RTC

            1、在openwrt的SDK开发包中,执行“make kernel_menuconfig”命令。

    在弹出的菜单“Linux/mips 3.18.29 Kernel Configuration”中,选择“Device Drivers  ---> Real Time Clock  --->”,开启RTC功能选项,如图4-4所示。


图4-4 开启RTC功能


    2、进入“ Real Time Clock--->”,对RTC进行配置

    配置如图4-5所示,其中:

   (1)去除选项“Set system time from RTC on startup and resume”,不在开机的时候就启动RTC驱动。因为本文中,打算开机完毕之后,再自行挂载RTC驱动。如果没有一开始就挂载了RTC驱动,开启这个选项,将出现错误提示“drivers/rtc/hctosys.c: unable to open rtc device (rtc0)”

   (2)对于“RTC debug support”,依据个人喜好而定,开启之后,可以查看RTC的调试信息。


图4-5 RTC 配置

五、RTC程序

5.1 RTC驱动程序

    驱动对应的文件名为:rtc_mcp7940.c,程序如下:

/* * rtc-mcp7940.c - RTC driver for some mostly-compatible I2C chips. * *  Copyright (C) 2005 James Chapman (ds1337 core) *  Copyright (C) 2006 David Brownell *  Copyright (C) 2009 Matthias Fuchs (rx8025 support) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/i2c.h>#include <linux/string.h>#include <linux/rtc.h>#include <linux/bcd.h>#define RALINK_SYSCTL_BASE0xB0000000#define RALINK_TIMER_BASE0xB0000100#define RALINK_INTCL_BASE0xB0000200#define RALINK_SYSCTL_ADDR      RALINK_SYSCTL_BASE    // system control #define RALINK_REG_GPIOMODE     (RALINK_SYSCTL_ADDR + 0x60) // GPIO MODE#define __devexit#define __devinitdata#define __devinit #define __devexit_p/* We can't determine type by probing, but if we expect pre-Linux code * to have set the chip up as a clock (turning on the oscillator and * setting the date and time), Linux can ignore the non-clock features. * That's a natural job for a factory or repair bench. */enum ds_type {mcp7940,};/* RTC registers don't differ much, except for the century flag */#define MCP7940_REG_SECS         0x00    /* 00-59 */#define MCP7940_BIT_CH           0x80#define MCP7940_BIT_ST           0x80#define MCP7940_REG_MIN          0x01    /* 00-59 */#define MCP7940_REG_HOUR         0x02    /* 00-23, or 1-12{am,pm} */#define MCP7940_BIT_12HR         0x40    /* in REG_HOUR */#define MCP7940_BIT_PM           0x20    /* in REG_HOUR */#define MCP7940_REG_WDAY         0x03    /* 01-07 */#define MCP7940_REG_MDAY         0x04    /* 01-31 */#define MCP7940_REG_MONTH        0x05    /* 01-12 */#define MCP7940_REG_YEAR         0x06    /* 00-99 */#define MCP7940_BIT_VBATEN       0x08/* Other registers (control, status, alarms, trickle charge, NVRAM, etc) * start at 7, and they differ a LOT. Only control and status matter for * basic RTC date and time functionality; be careful using them. */#define MCP7940_REG_CONTROL      0x07#define MCP7940_BIT_OUT          0x80#define MCP7940_BIT_SQWE         0x10#define MCP7940_BIT_RS1          0x02#define MCP7940_BIT_RS0          0x01struct mcp7940 {        u8                      offset; /* register's offset */        u8                      regs[11];        enum ds_type            type;        unsigned long           flags;#define HAS_NVRAM       0               /* bit 0 == sysfs file active */#define HAS_ALARM       1               /* bit 1 == irq claimed */        struct i2c_client       *client;        struct rtc_device       *rtc;        struct work_struct      work;        s32 (*read_block_data)(struct i2c_client *client, u8 command,                               u8 length, u8 *values);        s32 (*write_block_data)(struct i2c_client *client, u8 command,                                u8 length, const u8 *values);};struct chip_desc {        unsigned                nvram56:1;        unsigned                alarm:1;};static const struct i2c_device_id mcp7940_id[] = {        { "mcp7940", mcp7940 },        { }};MODULE_DEVICE_TABLE(i2c, mcp7940_id);/*----------------------------------------------------------------------*/#define BLOCK_DATA_MAX_TRIES 10static s32 mcp7940_read_block_data_once(struct i2c_client *client, u8 command,                                  u8 length, u8 *values){        s32 i, data;        for (i = 0; i < length; i++) {                data = i2c_smbus_read_byte_data(client, command + i);                if (data < 0)                        return data;                values[i] = data;        }        return i;}static s32 mcp7940_read_block_data(struct i2c_client *client, u8 command,                                  u8 length, u8 *values){        u8 oldvalues[I2C_SMBUS_BLOCK_MAX];        s32 ret;        int tries = 0;        dev_dbg(&client->dev, "mcp7940_read_block_data (length=%d)\n", length);        ret = mcp7940_read_block_data_once(client, command, length, values);        if (ret < 0)                return ret;        do {                if (++tries > BLOCK_DATA_MAX_TRIES) {                        dev_err(&client->dev,                                "mcp7940_read_block_data failed\n");                        return -EIO;                }                memcpy(oldvalues, values, length);                ret = mcp7940_read_block_data_once(client, command, length,                                                  values);                if (ret < 0)                        return ret;        } while (memcmp(oldvalues, values, length));        return length;}static s32 mcp7940_write_block_data(struct i2c_client *client, u8 command,                                   u8 length, const u8 *values){    u8 currvalues[I2C_SMBUS_BLOCK_MAX];    int tries = 0;    dev_dbg(&client->dev, "mcp7940_write_block_data (length=%d)\n", length);    do {            s32 i, ret;            if (++tries > BLOCK_DATA_MAX_TRIES) {                    dev_err(&client->dev,                            "mcp7940_write_block_data failed\n");                    return -EIO;            }            for (i = 0; i < length; i++) {                    ret = i2c_smbus_write_byte_data(client, command + i,                                                    values[i]);                    if (ret < 0)                            return ret;            }            ret = mcp7940_read_block_data_once(client, command, length,                                              currvalues);            if (ret < 0)                    return ret;    } while (memcmp(currvalues, values, length));    return length;}static int mcp7940_get_time(struct device *dev, struct rtc_time *t){        struct mcp7940  *mcp7940 = dev_get_drvdata(dev);        int             tmp;        /* read the RTC date and time registers all at once */        tmp = mcp7940->read_block_data(mcp7940->client,                mcp7940->offset, 7, mcp7940->regs);        if (tmp != 7) {                dev_err(dev, "%s error %d\n", "read", tmp);                return -EIO;        }        dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",                        "read",                        mcp7940->regs[0], mcp7940->regs[1],                        mcp7940->regs[2], mcp7940->regs[3],                        mcp7940->regs[4], mcp7940->regs[5],                        mcp7940->regs[6]);        t->tm_sec = bcd2bin(mcp7940->regs[MCP7940_REG_SECS] & 0x7f);        t->tm_min = bcd2bin(mcp7940->regs[MCP7940_REG_MIN] & 0x7f);        tmp = mcp7940->regs[MCP7940_REG_HOUR] & 0x3f;        t->tm_hour = bcd2bin(tmp);        t->tm_wday = bcd2bin(mcp7940->regs[MCP7940_REG_WDAY] & 0x07) - 1;        t->tm_mday = bcd2bin(mcp7940->regs[MCP7940_REG_MDAY] & 0x3f);        tmp = mcp7940->regs[MCP7940_REG_MONTH] & 0x1f;        t->tm_mon = bcd2bin(tmp) - 1;        /* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */        t->tm_year = bcd2bin(mcp7940->regs[MCP7940_REG_YEAR]) + 100;        dev_dbg(dev, "%s secs=%d, mins=%d, "                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",                "read", t->tm_sec, t->tm_min,                t->tm_hour, t->tm_mday,                t->tm_mon, t->tm_year, t->tm_wday);        /* initial clock setting can be undefined */        return rtc_valid_tm(t);}static int mcp7940_set_time(struct device *dev, struct rtc_time *t){        struct mcp7940  *mcp7940 = dev_get_drvdata(dev);        int             result;        int             tmp;        u8              *buf = mcp7940->regs;        dev_dbg(dev, "%s secs=%d, mins=%d, "                "hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",                "write", t->tm_sec, t->tm_min,                t->tm_hour, t->tm_mday,                t->tm_mon, t->tm_year, t->tm_wday);        buf[MCP7940_REG_SECS] = bin2bcd(t->tm_sec);        buf[MCP7940_REG_MIN] = bin2bcd(t->tm_min);        buf[MCP7940_REG_HOUR] = bin2bcd(t->tm_hour);        buf[MCP7940_REG_WDAY] = bin2bcd(t->tm_wday + 1);        buf[MCP7940_REG_MDAY] = bin2bcd(t->tm_mday);        buf[MCP7940_REG_MONTH] = bin2bcd(t->tm_mon + 1);        /* assume 20YY not 19YY */        tmp = t->tm_year - 100;        buf[MCP7940_REG_YEAR] = bin2bcd(tmp);                buf[MCP7940_REG_SECS] |= MCP7940_BIT_ST;                buf[MCP7940_REG_WDAY] |= MCP7940_BIT_VBATEN;        dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",                "write", buf[0], buf[1], buf[2], buf[3],                buf[4], buf[5], buf[6]);        result = mcp7940->write_block_data(mcp7940->client,                mcp7940->offset, 7, buf);        if (result < 0) {                dev_err(dev, "%s error %d\n", "write", result);                return result;        }        return 0;}static int mcp7940_ioctl(struct device *dev, unsigned int cmd, unsigned long arg){struct rtc_time time;void __user *uarg = (void __user *) arg;    switch (cmd) {case RTC_RD_TIME:mcp7940_get_time(dev, &time);if (copy_to_user(uarg, &time, sizeof(time))){printk("RTC_RD_TIME error, can not copy to user\n");return -EFAULT;}break;case RTC_SET_TIME:if (copy_from_user(&time, uarg, sizeof(time))){printk("RTC_SET_TIME error, can not copy from user\n");return -EFAULT;}mcp7940_set_time(dev, &time);break;    default:        return -ENOIOCTLCMD;    }    return 0;}static const struct rtc_class_ops mcp7940_rtc_ops = {.read_time = mcp7940_get_time,.set_time  = mcp7940_set_time,.ioctl     = mcp7940_ioctl,};/*----------------------------------------------------------------------*/static struct i2c_driver mcp7940_driver;static int __devinit mcp7940_probe(struct i2c_client *client,                                  const struct i2c_device_id *id){    struct mcp7940          *mcp7940;    int                     err = -ENODEV;    int                     tmp;    struct i2c_adapter      *adapter = to_i2c_adapter(client->dev.parent);    unsigned char           *buf;    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)        && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))            return -EIO;    if (!(mcp7940 = kzalloc(sizeof(struct mcp7940), GFP_KERNEL)))            return -ENOMEM;    i2c_set_clientdata(client, mcp7940);    mcp7940->client = client;    mcp7940->type   = id->driver_data;    mcp7940->offset = 0;    buf = mcp7940->regs;    if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {            mcp7940->read_block_data = i2c_smbus_read_i2c_block_data;            mcp7940->write_block_data = i2c_smbus_write_i2c_block_data;    } else {            mcp7940->read_block_data = mcp7940_read_block_data;            mcp7940->write_block_data = mcp7940_write_block_data;    }read_rtc:        /* read RTC registers */        tmp = mcp7940->read_block_data(mcp7940->client, 0, 8, buf);        if (tmp != 8) {                pr_debug("read error %d\n", tmp);                err = -EIO;                goto exit_free;        }        /* minimal sanity checking; some chips (like DS1340) don't         * specify the extra bits as must-be-zero, but there are         * still a few values that are clearly out-of-range.         */        tmp = mcp7940->regs[MCP7940_REG_SECS];                /* make sure that the backup battery is enabled */                if (!(mcp7940->regs[MCP7940_REG_WDAY] & MCP7940_BIT_VBATEN)) {                        i2c_smbus_write_byte_data(client, MCP7940_REG_WDAY,                                mcp7940->regs[MCP7940_REG_WDAY]                                | MCP7940_BIT_VBATEN);                }                /* clock halted?  turn it on, so clock can tick. */                if (!(tmp & MCP7940_BIT_ST)) {                        i2c_smbus_write_byte_data(client, MCP7940_REG_SECS,                                MCP7940_BIT_ST);                        dev_warn(&client->dev, "SET TIME!\n");                        goto read_rtc;                }        tmp = mcp7940->regs[MCP7940_REG_HOUR];        switch (mcp7940->type) {        default:                if (!(tmp & MCP7940_BIT_12HR))                        break;                /* Be sure we're in 24 hour mode.  Multi-master systems                 * take note...                 */                tmp = bcd2bin(tmp & 0x1f);                if (tmp == 12)                        tmp = 0;                if (mcp7940->regs[MCP7940_REG_HOUR] & MCP7940_BIT_PM)                        tmp += 12;                i2c_smbus_write_byte_data(client,                                MCP7940_REG_HOUR,                                bin2bcd(tmp));        }        mcp7940->rtc = rtc_device_register(client->name, &client->dev,                                &mcp7940_rtc_ops, THIS_MODULE);        if (IS_ERR(mcp7940->rtc)) {                err = PTR_ERR(mcp7940->rtc);                dev_err(&client->dev,                        "unable to register the class device\n");                goto exit_free;        }    return 0;exit_free:        kfree(mcp7940);        return err;}static int __devexit mcp7940_remove(struct i2c_client *client){struct mcp7940          *mcp7940 = i2c_get_clientdata(client);    rtc_device_unregister(mcp7940->rtc);    kfree(mcp7940);    return 0;}static struct i2c_driver mcp7940_driver = {    .driver = {        .name   = "rtc-mcp7940",        .owner  = THIS_MODULE,    },    .probe          = mcp7940_probe,    .remove         = __devexit_p(mcp7940_remove),    .id_table       = mcp7940_id,};static void rtc_pin_mux_init(void){u32 mode = 0;mode = le32_to_cpu(*(volatile u32 *)(RALINK_REG_GPIOMODE));     mode &= ~(0x1 << 0); // I2C_GPIO_MODE引脚,设置为I2C模式,即I2C_SD(GPIO#1)I2C_SCLK(GPIO#2)都设置为I2C模式    *(volatile u32 *)(RALINK_REG_GPIOMODE) = cpu_to_le32(mode); }static int __init mcp7940_init(void){rtc_pin_mux_init();    return i2c_add_driver(&mcp7940_driver);}module_init(mcp7940_init);static void __exit mcp7940_exit(void){    i2c_del_driver(&mcp7940_driver);}module_exit(mcp7940_exit);MODULE_AUTHOR("sky.houfei");MODULE_DESCRIPTION("RTC driver for MCP7940");MODULE_LICENSE("GPL");


5.2 RTC驱动的makefile

    makefile 名称为makefile

############################## mcp7940 rtc driver makefile #########################obj-m = rtc_mcp7940.oTARGET_NAME=rtc_mcp7940.koPWD=$(shell pwd)KERNEL_DIR?=/home/sky/develop/openWrt/openwrt/build_dir/target-mipsel_24kec+dsp_glibc-2.21/linux-ramips_mt7620/linux-3.18.29/TOOL_CAHIN="/home/sky/develop/openWrt/openwrt/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_glibc-2.21/bin/mipsel-openwrt-linux-gnu-"OUTPUT_DIR=$(PWD)/../../build_openwrt/mnt/sau2ag1/###############################################################################all:make -C $(KERNEL_DIR) \ARCH=mips \CROSS_COMPILE=$(TOOL_CAHIN)  \M=$(PWD) \modulesclean:rm -f $(obj-m)rm -f *.mod.crm -f *.mod.orm -f *.orderrm -f *.sysvers  #make command:#make #make clean


5.3 RTC应用程序

      mcp7940应用程序为 rtc_app.c,应用中,存储的时间为UTC格式时间。

     对于UTC时间,year = year -1900, month = month -1,应用程序如下。

/* *      Real Time Clock Driver Test/Example Program * *      Compile with: *     gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest * *      Copyright (C) 1996, Paul Gortmaker. * *      Released under the GNU General Public License, version 2, *      included herein by reference. * */ #include <stdio.h> #include <linux/rtc.h> #include <sys/ioctl.h> #include <sys/time.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> #include <stdlib.h> #include <errno.h>/* * This expects the new RTC class driver framework, working with * clocks that will often not be clones of what the PC-AT had. * Use the command line to specify another RTC if you need one. */ static const char default_rtc[] = "/dev/rtc0";int main(int argc, char **argv) { int i, fd, retval, irqcount = 0;unsigned int cmd = 0;unsigned long tmp, data;struct rtc_time rtc_tm;const char *rtc = default_rtc;fd = open(default_rtc, O_RDONLY);if (fd == -1){printf("Can not open %s, exit the app\n", default_rtc);}rtc_tm.tm_year = 2015 - 1900;rtc_tm.tm_mon = 10;rtc_tm.tm_mday = 28;rtc_tm.tm_wday = 3;rtc_tm.tm_hour = 15;rtc_tm.tm_min = 22;rtc_tm.tm_sec = 10;i = atoi(argv[1]);switch(i){case 1:cmd = RTC_RD_TIME;break;case 2:cmd = RTC_SET_TIME;printf("app set time %d-%d-%d week%d, %02d:%02d:%02d.\n", rtc_tm.tm_year + 1900, rtc_tm.tm_mon, rtc_tm.tm_mday, rtc_tm.tm_wday,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);break;default:cmd = RTC_RD_TIME;break;}retval = ioctl(fd, cmd, &rtc_tm);if (retval == -1) { printf("ioctl cmd = %d, error, exit the rtc app\n");exit(errno); }printf("rtc app date/time is %d-%d-%d week%d, %02d:%02d:%02d.\n", rtc_tm.tm_year + 1900, rtc_tm.tm_mon, rtc_tm.tm_mday, rtc_tm.tm_wday,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);close(fd);printf("rtc test end\n");return 0; } 


六、RTC常见问题分析

6.1 查看是i2c总线是否开启

    在MT7620开发板上,输入命令 "ls /dev/i2c*",如图6-1所示。在图中可以看到i2c-0,说明I2C总线已经开启。

图6-1 查看I2C总线是否开启
   说明:如果没有看到i2c设备,则说明系统并未开启i2c总线,不支持i2c总线的操作。此时需要查看下4.1.3章节,dts文件是否修改正确。

6.2 查看I2C总线支持的从机device

    在MT7620开发板上,输入命令“ls /sys/bus/i2c/devices/”,如图6-2所示。

    在图6-2中,可以看到支持了0x006f的从机设备(mcp7940芯片的地址刚好为0x6f)。


图6-2查看I2C总线支持的从机device

   说明:如果没有看到d0x006f或者除了i2c-0之外的设备,则说明i2c总线不支持device从机,此时任何i2c 芯片(从机device)挂载到MT7620上,都不可能通信,因为主机(MT620芯片)不支持从机的地址。此时需要查看下4.1.3章节,dts文件是否修改正确。

6.3 查看i2c总线挂载的驱动

    在MT7620开发板上,输入命令“ls /sys/bus/i2c/drivers/”,如图6-3所示。由图可知,设备已经成功将rtc-mcp7940驱动程序挂载至i2c总线。


图6-3 查看i2c总线挂载的驱动

    说明:如果没有看到rtc-mcp7940,则说明rtc的驱动程序有问题,需要查看下5.1章节的驱动程序。

6.4 查看rtc设备

    在MT7620开发板上,输入命令“ls /dev/rtc*”,如图6-4所示。由图可知,rtc已经驱动成功,可以看到rtc设备。


图6-4 查看rtc设备

    说明:如果没有看到rtc0或者其他的rtc设备,则说明rtc的配置有问题,系统不支持rtc,需要查看下4.2章节的rtc配置。


7、测试

1、挂载rtc驱动

rtc编译后的驱动名称为rtc_mcp7940,执行挂载,提示信息如下,说明rtc成功挂载在0-006f节点下,并且注册为/dev/rtc0:

root@OpenWrt:/tmp# insmod ./rtc_mcp7940.ko [  203.740000] rtc-mcp7940 0-006f: rtc core: registered mcp7940 as rtc0root@OpenWrt:/tmp# 


2、运行rtc测试程序

    先设置时间,设置完毕之后,再次读取rtc时间,发现可以正常读写时间,且正确。而且将设备重启,重启之后,rtc时间也正常计时,重启过程中,时间没有丢失和停止。

root@OpenWrt:/tmp# ./rtc_app_openwrt 2app set time 2015-10-28 week3, 15:22:10.rtc app date/time is 2015-10-28 week3, 15:22:10.rtc test endroot@OpenWrt:/tmp# ./rtc_app_openwrt 1rtc app date/time is 2015-10-28 week3, 15:22:26.rtc test end
0 0
原创粉丝点击