一、硬體平台
1、 控制器:MT7620(A9核心)
2、 RTC晶片:MCP7940
二、軟體平台
1、開發環境:Ubuntu12.04
2、SDK核心包:MT7620 SDK軟體開發包(MediaTek_ApSoC_SDK_4320_20150414.tar.bz2)
3、核心版本:linux-2.6.36.x
三、功能簡介
RTC(real time clock)實時時鐘,主要作用是給Linux系統提供時間。本文中的RTC為單獨的RTC晶片MCP7940,與CPU采用I2C總線連接配接。
1. RTC驅動模式
與RTC核心有關的檔案有:
/drivers/rtc/class.c 這個檔案向linux裝置模型核心注冊了一個類RTC,然後向驅動程式提供了注冊/登出接口
/drivers/rtc/rtc-dev.c 這個檔案定義了基本的裝置檔案操作函數,如:open,read等
/drivers/rtc/interface.c 顧名思義,這個檔案主要提供了使用者程式與RTC驅動的接口函數,使用者程式一般通過ioctl與RTC驅動互動,這裡定義了每個ioctl指令需要調用的函數
/drivers/rtc/rtc-sysfs.c 與sysfs有關
/drivers/rtc/rtc-proc.c 與proc檔案系統有關
/include/linux/rtc.h 定義了與RTC有關的資料結構
RTC驅動模型結構如下圖:
圖3-1 RTC驅動模型
2. I2C驅動模型
對于采用I2C總線的RTC晶片,它的驅動不僅僅需要RTC的驅動,還需要I2C驅動的支援。對于Linux下,自身的I2C驅動已經比較完善,隻要添加RTC裝置支援即可。
四、修改核心配置
1. 增加I2C字元裝置的支援
位置 DeviceDriver--> Character devices--> Ralink RT2880 I2C Support,将其選擇為子產品,如圖4-1所示。
圖4-1 Ralink對 I2C字元裝置的支援
2. I2C裝置的配置
(1)配置I2C
DeviceDriver--> I2C support,配置為圖4-2所示
圖4-2 I2C支援配置
(2)配置I2C的總線協定
對于SMBUS-specificprotocols 中,需要設定一下I2C Algorithms,選擇協定“I2C bit-banging”,否則,即使rtc驅動正确,也不能使用。
位置:Device Driver -->I2C support --> SMBus-specific protocols --> I2C Algorithms
配置為如圖4-3所示。
圖4-3 I2C Algorithms配置
(3)增加硬體I2C 總線支援
Device Driver --> I2C support -->I2C Hardware Bus support
選擇增加Ralink 的I2C總線控制器,如圖4-4所示。
圖4-4 硬體I2C總線支援
3. RTC配置
(1)開啟RTC
Device Driver --> Real Time Clock
選擇RTC,如圖4-5所示
圖 4-5 開啟RTC配置
(2)RTC配置
Device Driver --> Real Time Clock
在圖4-5中,再選擇RTC的配置,如圖4-6配置。
圖4-6 RTC配置
注意,圖4-6中的 “RTC debug support”建議開啟,可以檢視RTC的調試資訊,例如RTC 驅動成功的話,系統會列印如下資訊:
“rtc-mcp7940 0-006f: rtc core: registered mcp7940 as rtc0”
五、RTC驅動代碼
1. MT7620的核心代碼
(1)修改SDK開發包的makefile。
因為預設的makefile,把I2C的編譯部分給屏蔽了,是以需要手動修改:linux-2.6.36.x/arch/mips/ralink/MAKEFIL。
原始代碼:
#obj-$(CONFIG_I2C_RALINK) += dev-i2c.o
修改之後:
obj-$(CONFIG_I2C_RALINK) += dev-i2c.o
修改之後的makefile如下:
###############################################################################
# Jan 2007 Bruce Chang
#
# Initial Release
#
#
#
###############################################################################
.S.s:
$(CPP) $(CFLAGS) $< -o $*.s
.S.o:
$(CC) $(CFLAGS) -c $< -o $*.o
obj-y := reset.o init.o irq.o \
memory.o printf.o cmdline.o setup.o time.o
ifeq ($(CONFIG_MIPS_MT_SMP),y)
obj-y += malta-amon.o
endif
obj-$(CONFIG_KERNEL_NVRAM) += nvram.o
obj-$(CONFIG_PCI) += pci.o
obj-$(CONFIG_DWC_OTG) += lm.o
obj-$(CONFIG_RALINK_TIMER_WDG) += ralink_wdt.o
obj-$(CONFIG_RALINK_TIMER_DFS) += rt_timer.o
obj-$(CONFIG_RT_DMA) += dev-dma.o
obj-$(CONFIG_MTK_MTD_NAND) += dev-nand.o
ifeq ($(CONFIG_RALINK_MT7621),y)
obj-$(CONFIG_MTD_ANY_RALINK) += dev-nand.o
endif
#obj-$(CONFIG_I2C_RALINK) += dev-i2c.o # source code
obj-$(CONFIG_I2C_RALINK) += dev-i2c.o # add by sky.houfei 2015-10-27
obj-$(CONFIG_RALINK_RT3883) += ehci_ohci.o udc.o
obj-$(CONFIG_RALINK_RT3352) += ehci_ohci.o udc.o
obj-$(CONFIG_RALINK_RT5350) += ehci_ohci.o udc.o
obj-$(CONFIG_RALINK_RT6855) += ehci_ohci.o udc.o
obj-$(CONFIG_RALINK_MT7620) += ehci_ohci.o udc.o
obj-$(CONFIG_RALINK_MT7628) += ehci_ohci.o udc.o
ifeq ($(CONFIG_CONFIG_SHRINK),y)
EXTRA_CFLAGS += -DCONFIG_SHRINK #-DHT_DEBUG #-DHASH_STAT_DBG
obj-y += hash_utils.o #list_utils.o
endif
(2)修改I2C代碼
修改I2C對裝置的支援,增加MCP7940裝置的支援。修改檔案為:linux-2.6.36.x/arch/mips/ralink/dev_i2c.c
對于MCP7940,其i2c裝置結構如下
static struct i2c_board_info __initdata mcp7940_i2c_devices[] = {
{ I2C_BOARD_INFO("rtc-mcp7940", 0x6f),
.type = "mcp7940",
/* REVISIT .irq = IRQ4 ... this RTC has an alarm */
},
};
其中,檢視MCP7940晶片手冊,可以得知裝置的I2C總線通信位址為 1101 111x
最後一位為讀寫位。對于Linux I2C而言,所有的I2C裝置的位址,都為晶片手冊中的位址右移一位的數值,是以MCP7940在I2C裝置結構中的位址為 1101 111x >> 1,結果為 0110 1111,即為 0x6f
修改之後的dev_i2c.c代碼如下所示:
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/i2c/at24.h>
#include <linux/i2c.h>
#include <asm/mach-ralink/rt_mmap.h>
static struct at24_platform_data at24_config = {
.byte_len = 0x400 / 8,
.page_size = 8,
};
static struct i2c_board_info i2c_info[] __initconst = {
{
I2C_BOARD_INFO("24c01", 0x50),
.platform_data = &at24_config,
},
};
// ========= sourc code start ============
//#if 0
//static struct i2c_board_info __initdata mpc8313_i2c_devices[] = {
// { I2C_BOARD_INFO("rtc-ds1307", 0x68),
// .type = "ds1339",
// /* REVISIT .irq = IRQ4 ... this RTC has an alarm */
// },
//};
//#endif
// ========= sourc code end ============
// ======= add by sky.houfei =============
static struct i2c_board_info __initdata mcp7940_i2c_devices[] = {
{ I2C_BOARD_INFO("rtc-mcp7940", 0x6f),
.type = "mcp7940",
/* REVISIT .irq = IRQ4 ... this RTC has an alarm */
},
};
// ============ add end ============
static struct resource i2c_resources[] = {
{
.start = -1, /* filled at runtime */
.end = -1, /* filled at runtime */
.flags = IORESOURCE_MEM,
},
};
static struct platform_device ralink_i2c_device = {
.name = "Ralink-I2C",
.id = 0,
.num_resources = ARRAY_SIZE(i2c_resources),
.resource = i2c_resources,
};
int __init ralink_i2c_register(void)
{
i2c_resources[0].start = RALINK_I2C_BASE;
i2c_resources[0].end += RALINK_I2C_BASE + 256 - 1;
platform_device_register(&ralink_i2c_device);
i2c_register_board_info(0, i2c_info, ARRAY_SIZE(i2c_info));
// ======== sourc code start========
//#if 0
// i2c_register_board_info(0, mpc8313_i2c_devices, ARRAY_SIZE(mpc8313_i2c_devices));
//#endif
// ======== sourc code end========
i2c_register_board_info(0, mcp7940_i2c_devices, ARRAY_SIZE(mcp7940_i2c_devices)); //add by sky.houfei 2015-10-19
return 0;
}
arch_initcall(ralink_i2c_register);
2. MCP7940驅動
驅動對應的檔案名為:rtc_mcp7940.c
/*
* rtc-mcp7940.c - RTC driver for some mostly-compatible I2C chips.
* 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>
#include <asm/mach-ralink/rt_mmap.h>
#define RALINK_SYSCTL_ADDR RALINK_SYSCTL_BASE // system control
#define RALINK_REG_GPIOMODE (RALINK_SYSCTL_ADDR + 0x60) // GPIO MODE
/* 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");
3.mc7940驅動對應的Makefile
obj-m = rtc_mcp7940.o
PWD=$(shell pwd)
KDIR = /home/sky/develop/kernel/sau2ag1/source/linux-2.6.36.x/
all:
make ARCH=mips CROSS_COMPILE="/opt/buildroot-gcc463/usr/bin"/mipsel-linux- -C $(KDIR) M=$(PWD) modules
clean:
make ARCH=mips CROSS_COMPILE="/opt/buildroot-gcc463/usr/bin"/mipsel-linux- -C $(KDIR) M=$(PWD) clean
rm -f rtc_mcp7940.ko
#make command:
#make
#make clean
4. mcp7940應用程式
mcp7940應用程式為 rtc_app.c,應用中,存儲的時間為UTC格式時間,對于UTC時間,year = year -1900, month = month -1,應用程式如下。
/*
* Real Time Clock Driver Test/Example Program
*
* Compile with:
* mipsel-linux-gcc rtc_app.c -o rtc_app
* 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 = 2016 - 1900;
rtc_tm.tm_mon = 1 - 1;
rtc_tm.tm_mday = 4;
rtc_tm.tm_wday = 1;
rtc_tm.tm_hour = 10;
rtc_tm.tm_min = 23;
rtc_tm.tm_sec = 53;
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 + 1, 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;
}
六、常見問題分析
1. 如果檢視裝置的檔案:
/dev/i2c-0 提示ic2初始化正确。
/sys/bus/i2c/drivers/rtc-mcp7940 提示I2C總線挂載了一個名叫rtc-mcp7940 的裝置,但是并不意味着總線已經可以和該裝置通信,驅動的probe函數有可能都沒有執行。
/dev/rtc0 說明裝置已經加載成功,I2C總線成功的和RTC晶片通信。