一、硬件平台
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、目标芯片的kernel,增加i2c支持。
3、修改dts文件,增加对i2c支持。
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文件:
[email protected] { gpio0: [email protected] { status = "okay"; }; gpio1: [email protected] { status = "okay"; }; gpio2: [email protected] { status = "okay"; }; [email protected] { status = "okay"; [email protected] {
在palmbus节点中,增加MT7620A对I2C总线和RTC芯片的支持,修改之后的DTS文件:
[email protected] { gpio0: [email protected] { status = "okay"; }; gpio1: [email protected] { status = "okay"; }; gpio2: [email protected] { status = "okay"; }; [email protected] { compatible ="ralink,mt7620a-i2c", "ralink,rt2880-i2c"; reg= <0x900 0x100>; resets = <&rstctrl 16>; reset-names= "i2c"; #address-cells = <1>; #size-cells= <0>; status= "okay"; [email protected] { compatible = "mcp,mcp7940"; reg = <0x6f>; }; }; [email protected] { status = "okay"; [email protected] {
说明:
1、[email protected]为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模式中!
[email protected] { compatible ="ralink,mt7620a-i2c", "ralink,rt2880-i2c"; reg= <0x900 0x100>; resets = <&rstctrl 16>; reset-names= "i2c"; #address-cells = <1>; #size-cells= <0>; status= "okay"; [email protected] { compatible = "mcp,mcp7940"; reg = <0x6f>; }; };
如果需要修改为ds1307芯片,则只需要将其中的rtc地址和comatible修改即可。ds1307的通信地址为0x68,则修改如下:
[email protected] { 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:=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-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:
[email protected]:/tmp# insmod ./rtc_mcp7940.ko [ 203.740000] rtc-mcp7940 0-006f: rtc core: registered mcp7940 as rtc0 [email protected]:/tmp#
2、运行rtc测试程序
先设置时间,设置完毕之后,再次读取rtc时间,发现可以正常读写时间,且正确。而且将设备重启,重启之后,rtc时间也正常计时,重启过程中,时间没有丢失和停止。
[email protected]:/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 [email protected]:/tmp# ./rtc_app_openwrt 1 rtc app date/time is 2015-10-28 week3, 15:22:26. rtc test end