一、硬件平臺
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支持。
[ 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的支持
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:=mt7620
BOARDNAME:=MT7620 based boards
ARCH_PACKAGES:=ramips_24kec
FEATURES+=usb rtc
CPU_TYPE:=24kec
CPU_SUBTYPE:=dsp
DEFAULT_PACKAGES += kmod-rt2800-pci kmod-rt2800-soc
define 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_BASE 0xB0000000
#define RALINK_TIMER_BASE 0xB0000100
#define RALINK_INTCL_BASE 0xB0000200
#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 0x01
struct 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 10
static 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.o
TARGET_NAME=rtc_mcp7940.ko
PWD=$(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) \
modules
clean:
rm -f $(obj-m)
rm -f *.mod.c
rm -f *.mod.o
rm -f *.order
rm -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.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 rtc0
root@OpenWrt:/tmp#
2、運行rtc測試程序
先設置時間,設置完畢之後,再次讀取rtc時間,發現可以正常讀寫時間,且正確。而且將設備重啓,重啓之後,rtc時間也正常計時,重啓過程中,時間沒有丟失和停止。
root@OpenWrt:/tmp# ./rtc_app_openwrt 2
app set time 2015-10-28 week3, 15:22:10.
rtc app date/time is 2015-10-28 week3, 15:22:10.
rtc test end
root@OpenWrt:/tmp# ./rtc_app_openwrt 1
rtc app date/time is 2015-10-28 week3, 15:22:26.
rtc test end