03-I2C设备驱动开发(DS1307)
一 目的
二 内容
2.1 V1.0:基于设备驱动模型,基础i2c字符设备驱动
-
源码
#include "linux/uaccess.h"
#include <linux/bcd.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/rtc/ds1307.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/watchdog.h>
#include <linux/device.h>
#include <linux/fs.h>#define DRIVER_NAME "rtc_ds1307"
#define CLASS_NAME "ds1307_class"
#define DRIVER_VERSION "V1.0"/* Time Register */
#define DS1307_TIME_REGS_COUNT 7
#define DS1307_REG_SECS 0x00
# define DS1307_BIT_CH 0x80
#define DS1307_REG_MIN 0x01
#define DS1307_REG_HOUR 0x02
# define DS1307_BIT_12HR 0x40
# define DS1307_BIT_PM 0x20
#define DS1307_REG_WDAY 0x03
#define DS1307_REG_MDAY 0x04
#define DS1307_REG_MONTH 0x05
#define DS1307_REG_YEAR 0x06/* Control Register */
#define DS1307_REG_CONTROL 0x07
# define DS1307_BIT_OUT 0x80
# define DS1307_BIT_SQWE 0x10
# define DS1307_BIT_RS1 0x02
# define DS1307_BIT_RS0 0x01struct ds1307_time {
u8 secs;
u8 min;
u8 hour;
u8 wday;
u8 mday;
u8 month;
u8 year;
};struct ds1307_dev {
struct cdev cdev;
dev_t dev_num;
struct class *dev_class;
struct device *device;
struct i2c_client *client;
};static int ds1307_read_reg(struct i2c_client *client, u8 reg, u8 *val)
{
int ret;
ret = i2c_smbus_read_byte_data(client, reg);
if (ret < 0) {
pr_err("Failed to read register 0x%02x\\n", reg);
return ret;
}
*val = ret;
return 0;
}static int ds1307_write_reg(struct i2c_client *client, u8 reg, u8 val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, reg, val);
if (ret < 0) {
pr_err("Failed to write register 0x%02x\\n", reg);
return ret;
}
return 0;
}static int ds1307_read_regs(struct i2c_client *client, u8 reg, u8 *buf, u8 count)
{
int ret;
ret = i2c_smbus_read_i2c_block_data(client, reg, count, buf);
if (ret < 0) {
pr_err("Failed to read registers starting from 0x%02x\\n", reg);
}
return ret;
}static int ds1307_write_regs(struct i2c_client *client, u8 reg, u8 *buf, u8 count)
{
int ret;
ret = i2c_smbus_write_i2c_block_data(client, reg, count, buf);
if (ret < 0) {
pr_err("Failed to write registers starting from 0x%02x\\n", reg);
}
return ret;
}static int ds1307_check_init(struct i2c_client *client)
{
int ret;
struct ds1307_dev *dev = i2c_get_clientdata(client);
u8 seconds;/* 1. check */
ret = i2c_smbus_read_byte_data(client, DS1307_REG_SECS);
if(ret < 0) {
dev_err(&client->dev, "Failed to read from DS1307 device\\n");
return ret;
}seconds = (u8)ret;
/* 2. init */
if(seconds & DS1307_BIT_CH) {
u8 default_time[] = {0x00, 0x00, 0x00, 0x04, 0x01, 0x01, 0x25};
dev_warn(&client->dev, "Clock was halted. Initializing clock…\\n");ret = ds1307_write_regs(client, DS1307_REG_SECS, default_time, DS1307_TIME_REGS_COUNT);
if (ret < 0) {
dev_err(&client->dev, "Failed to write default time\\n");
return ret;
}
dev_info(&client->dev, "Clock started with default time.\\n");
}
else {
dev_info(&client->dev, "ds1307 without init\\n");
}return 0;
}static int ds1307_open(struct inode *inode, struct file *file)
{
struct ds1307_dev *dev = container_of(inode->i_cdev, struct ds1307_dev, cdev);
file->private_data = dev;
return 0;
}static int ds1307_release(struct inode *inode, struct file *file)
{
return 0;
}static ssize_t ds1307_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
struct ds1307_dev *dev = file->private_data;
u8 time_buf[DS1307_TIME_REGS_COUNT];
int ret;if(*f_pos > 0) {
return 0;
}ret = ds1307_read_regs(dev->client, DS1307_REG_SECS, time_buf, DS1307_TIME_REGS_COUNT);
if (ret < 0) {
pr_err("Failed to read time registers from DS1307\\n");
return ret;
}if (count > DS1307_TIME_REGS_COUNT) {
count = DS1307_TIME_REGS_COUNT;
}if(copy_to_user(buf, time_buf, count)) {
pr_err("Failed to copy data to user space\\n");
return –EFAULT;
}*f_pos += count;
return count;
}static ssize_t ds1307_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
struct ds1307_dev *dev = file->private_data;
u8 time_buf[DS1307_TIME_REGS_COUNT];
int ret;if(count > DS1307_TIME_REGS_COUNT) {
count = DS1307_TIME_REGS_COUNT;
}ret = copy_from_user(time_buf, buf, count);
if(ret < 0) {
pr_err("Failed to copy data from user space\\n");
return –EFAULT;
}
time_buf[0] &= ~DS1307_BIT_CH;ret = ds1307_write_regs(dev->client, DS1307_REG_SECS, time_buf, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
pr_err("Failed to write time registers from DS1307\\n");
return ret;
}return count;
}static const struct file_operations ds1307_fops = {
.owner = THIS_MODULE,
.open = ds1307_open,
.read = ds1307_read,
.write = ds1307_write,
.release = ds1307_release,
};static int ds1307_drv_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct ds1307_dev *dev;
int ret;/* 1.内存分配 */
dev = kzalloc(sizeof(struct ds1307_dev), GFP_KERNEL);
if(!dev) {
return –ENOMEM;
}
dev->client = client;
i2c_set_clientdata(client, dev);/* 1.1 检查RTC是否工作 */
ret = ds1307_check_init(client);
if (ret < 0) {
goto err_free_mem;
}/* 2.字符设备号分配 */
ret = alloc_chrdev_region(&dev->dev_num, 0, 1, DRIVER_NAME);
if(ret < 0) {
pr_err("Failed to allocate char device region\\n");
goto err_free_mem;
}/* 3.初始化cdev */
cdev_init(&dev->cdev, &ds1307_fops);
dev->cdev.owner = THIS_MODULE;
ret = cdev_add(&dev->cdev, dev->dev_num, 1);
if (ret < 0) {
pr_err("Failed to add cdev\\n");
goto err_unregister_chrdev;
}/* 4.设备类创建 */
dev->dev_class = class_create(THIS_MODULE, CLASS_NAME);
if (IS_ERR(dev->dev_class)) {
pr_err("Failed to create device class\\n");
ret = PTR_ERR(dev->dev_class);
goto err_del_cdev;
}/* 5.创建设备文件“/dev/rtc_ds1307” */
dev->device = device_create(dev->dev_class, NULL, dev->dev_num, NULL, DRIVER_NAME);
if (IS_ERR(dev->device)) {
pr_err("Failed to create device file\\n");
ret = PTR_ERR(dev->device);
goto err_destroy_class;
}pr_info("DS1307 driver probed successfully\\n");
return 0;/* 错误处理:按申请顺序的逆序释放资源 */
err_destroy_class:
class_destroy(dev->dev_class);
err_del_cdev:
cdev_del(&dev->cdev);
err_unregister_chrdev:
unregister_chrdev_region(dev->dev_num, 1);
err_free_mem:
kfree(dev);
return ret;
}static void ds1307_drv_remove(struct i2c_client *client) {
struct ds1307_dev *dev = i2c_get_clientdata(client);
pr_info("ds1307_remove function entered\\n");device_destroy(dev->dev_class, dev->dev_num);
class_destroy(dev->dev_class);
cdev_del(&dev->cdev);
unregister_chrdev_region(dev->dev_num, 1);
kfree(dev);
}static const struct i2c_device_id ds1307_id[] = {
{"rtc-ds1307", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);static const struct of_device_id ds1307_of_match[] = {
{ .compatible = "fog,rtc-ds1307" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds1307_of_match);static struct i2c_driver ds1307_driver = {
.driver = {
.name = "ds1307",
.of_match_table = of_match_ptr(ds1307_of_match),
},
.probe = ds1307_drv_probe,
.remove = ds1307_drv_remove,
.id_table = ds1307_id,
};
module_i2c_driver(ds1307_driver);MODULE_LICENSE("GPL");
-
测试
-
驱动匹配 && 节点信息
-
设备识别

-
驱动加载

-
节点信息

-
测试程序
-
主要实现
- 通过i2c读取寄存器原始数据
- 打印原始数据与转化后的数据
- 通过i2c写入时间
- 同步系统时间并写入设备
-
-
源码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <time.h>#define DEVICE_PATH "/dev/rtc_ds1307"
#define DS1307_TIME_REGS_COUNT 7static uint8_t bcd2bin(uint8_t val)
{
return (val & 0x0f) + (val >> 4) * 10;
}static uint8_t bin2bcd(uint8_t val)
{
return ((val / 10) << 4) + (val % 10);
}static const char *wday_name[] = {
"", "Sun.", "Mon.", "Tue.", "Wed.", "Fri.", "Sat."
};static void print_raw_regs(uint8_t *regs)
{
printf("\\n Raw data: \\n");
printf(" Sec: 0x%02X\\n", regs[0]);
printf(" Min: 0x%02X\\n", regs[1]);
printf(" Hour: 0x%02X\\n", regs[2]);
printf(" Week: 0x%02X\\n", regs[3]);
printf(" Day: 0x%02X\\n", regs[4]);
printf(" Mon: 0x%02X\\n", regs[5]);
printf(" Year: 0x%02X\\n", regs[6]);
}static void print_time(uint8_t *regs)
{
uint8_t sec = bcd2bin(regs[0] & 0x7F);
uint8_t min = bcd2bin(regs[1]);
uint8_t hour = bcd2bin(regs[2] & 0x3F);
uint8_t wday = regs[3];
uint8_t mday = bcd2bin(regs[4]);
uint8_t mon = bcd2bin(regs[5]);
uint8_t year = bcd2bin(regs[6]);printf("\\n Time:\\n");
printf(" 20%02d:%02d:%02d:(%s)\\n", year, mon, mday,
(wday >= 1 && wday <= 7) ? wday_name[wday] : "Unknowd");
printf(" %02d:%02d:%02d\\n", hour, min, sec);
}static int read_time(void)
{
int fd;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;fd = open(DEVICE_PATH, O_RDONLY);
if(fd < 0) {
perror("Cant open device");
return –1;
}ret = read(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("read error");
close(fd);
return –1;
}printf("read %d Byte\\n", ret);
print_raw_regs(time_regs);
print_time(time_regs);
}static int write_time(int year, int mon, int mday, int wday,
int hour, int min, int sec)
{
int fd;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;time_regs[0] = bin2bcd(sec);
time_regs[1] = bin2bcd(min);
time_regs[2] = bin2bcd(hour);
time_regs[3] = wday;
time_regs[4] = bin2bcd(mday);
time_regs[5] = bin2bcd(mon);
time_regs[6] = bin2bcd(year);printf("\\n Write Time: \\n");
printf(" 20%02d:%02d:%02d:(%s)\\n", year, mon, mday,
(wday >= 1 && wday <= 7) ? wday_name[wday] : "Unknowd");
printf(" %02d:%02d:%02d\\n", hour, min, sec);fd = open(DEVICE_PATH, O_WRONLY);
if(fd < 0) {
perror("Cant open device");
return –1;
}ret = write(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("write error");
close(fd);
return –1;
}printf("Write Byte:%d\\n", ret);
close(fd);
return 0;
};int sync_time(void)
{
int fd;
struct tm sync_time;
time_t tNow = –1;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;fd = open(DEVICE_PATH, O_WRONLY);
if(fd < 0) {
perror("Cant open device");
close(fd);
}time(&tNow);
if(tNow < 0) {
perror("time");
close(fd);
return –1;
}
printf("time:%s\\n", ctime(&tNow));memset(&sync_time, 0, sizeof(sync_time));
localtime_r(&tNow, &sync_time);time_regs[0] = bin2bcd((uint8_t)sync_time.tm_sec);
time_regs[1] = bin2bcd((uint8_t)sync_time.tm_min);
time_regs[2] = bin2bcd((uint8_t)sync_time.tm_hour);
time_regs[3] = bin2bcd((uint8_t)sync_time.tm_wday + 1);
time_regs[4] = bin2bcd((uint8_t)sync_time.tm_mday);
time_regs[5] = bin2bcd((uint8_t)sync_time.tm_mon + 1);
time_regs[6] = bin2bcd((uint8_t)sync_time.tm_year – 100);ret = write(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("sync error");
close(fd);
return –1;
}
close(fd);
return 0;
}int main(int argc, char *argv[])
{
if(strcmp(argv[1], "read") == 0) {
return read_time();
}
else if(strcmp(argv[1], "write") == 0) {
int year = atoi(argv[2]);
int mon = atoi(argv[3]);
int mday = atoi(argv[4]);
int wday = atoi(argv[5]);
int hour = atoi(argv[6]);
int min = atoi(argv[7]);
int sec = atoi(argv[8]);return write_time(year, mon, mday, wday, hour, min, sec);
}
else if(strcmp(argv[1], "sync") == 0) {
return sync_time();
}
else {
return –1;
}
return 0;
}
测试现象
读功能

写功能

同步功能

2.2 V1.1:引入rtc子系统,实现基础功能
-
源码
-
主要修改
-
ds1307_dev结构体中新增成员struct rtc_device *rtc_device

-
实现rtc_class_ops,用于读写操作



-
probe函数中注册RTC设备


-
-
补丁
commit 2f560c6869afc63565f83970f0b629f7bafcd87b (HEAD -> master)
Author: zjp <zjp@tronlong.com>
Date: Tue Jan 13 15:52:33 2026 +0800V1.1–Implement basic RTC device functionality
diff —git a/ds1307.c b/ds1307.c
index de9c917..1ae5997 100644
—– a/ds1307.c
+++ b/ds1307.c
@@ –1,3 +1,5 @@
+#include "asm-generic/errno-base.h"
+#include "linux/export.h"
#include "linux/uaccess.h"
#include <linux/bcd.h>
#include <linux/i2c.h>
@@ –57,6 +59,7 @@ struct ds1307_dev {
struct class *dev_class;
struct device *device;
struct i2c_client *client;
+ struct rtc_device *rtc_device;
};static int ds1307_read_reg(struct i2c_client *client, u8 reg, u8 *val)
@@ –204,6 +207,66 @@ static ssize_t ds1307_write(struct file *file, const char __user *buf, size_t co
return count;
}+static int ds1307_rtc_read_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307_dev *ds1307_dev = dev_get_drvdata(dev);
+ u8 time_buf[DS1307_TIME_REGS_COUNT];
+ int ret;
+
+ ret = ds1307_read_regs(ds1307_dev->client, DS1307_REG_SECS, time_buf, DS1307_TIME_REGS_COUNT);
+ if(ret < 0) {
+ pr_err("Failed to read time registers from DS1307\\n");
+ return ret;
+ }
+ if(time_buf[0] & DS1307_BIT_CH)
+ return –EINVAL;
+
+ t->tm_sec = bcd2bin(time_buf[0] & 0x7f);
+ t->tm_min = bcd2bin(time_buf[1] & 0x7f);
+ t->tm_hour = bcd2bin(time_buf[2] & 0x3f);
+ t->tm_wday = bcd2bin(time_buf[3] & 0x07) – 1 ;
+ t->tm_mday = bcd2bin(time_buf[4] & 0x3f);
+ t->tm_mon = bcd2bin(time_buf[5] & 0x1f) – 1;
+ t->tm_year = bcd2bin(time_buf[6] ) + 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);
+
+ return 0;
+}
+
+static int ds1307_rtc_set_time(struct device *dev, struct rtc_time *t)
+{
+ struct ds1307_dev *ds1307_dev = dev_get_drvdata(dev);
+ u8 time_buf[DS1307_TIME_REGS_COUNT];
+ int ret;
+
+ 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);
+
+
+ time_buf[0] = bin2bcd(t->tm_sec);
+ time_buf[1] = bin2bcd(t->tm_min);
+ time_buf[2] = bin2bcd(t->tm_hour);
+ time_buf[3] = bin2bcd(t->tm_wday + 1);
+ time_buf[4] = bin2bcd(t->tm_mday);
+ time_buf[5] = bin2bcd(t->tm_mon + 1);
+ time_buf[6] = bin2bcd(t->tm_year – 100);
+
+ ret = ds1307_write_regs(ds1307_dev->client, DS1307_REG_SECS, time_buf, DS1307_TIME_REGS_COUNT);
+ return 0;
+}
+
+static const struct rtc_class_ops ds1307_rtc_ops = {
+ .read_time = ds1307_rtc_read_time,
+ .set_time = ds1307_rtc_set_time,
+};
+
static const struct file_operations ds1307_fops = {
.owner = THIS_MODULE,
.open = ds1307_open,
@@ –221,6 +284,7 @@ static int ds1307_drv_probe(struct i2c_client *client, const struct i2c_device_i
if(!dev) {
return –ENOMEM;
}
+ dev_set_drvdata(&client->dev, dev);
dev->client = client;
i2c_set_clientdata(client, dev);@@ –262,6 +326,14 @@ static int ds1307_drv_probe(struct i2c_client *client, const struct i2c_device_i
goto err_destroy_class;
}+ /* 6.ע<B2><E1>RTC<C9>豸 */
+ dev->rtc_device = devm_rtc_device_register(&client->dev, DRIVER_NAME, &ds1307_rtc_ops, THIS_MODULE);
+ if (IS_ERR(dev->rtc_device)) {
+ dev_err(&client->dev, "Failed to register RTC device\\n");
+ ret = PTR_ERR(dev->rtc_device);
+ goto err_destroy_class; // <CC><F8>ת<B5><BD><CF><D6><D3>д<ED><CE><F3><B4><A6><C0><ED><B5><E3>
+}
+
测试
驱动加载 && 节点信息


hwclock 读写时间
查看时钟
hwclock –u

设置系统时间 && 同步至 RTC时钟
date –s "2026-01-13 17:22:00"
date
hwclock —systohc –u
hwclock –u

2.3 V1.2:注册 NV SRAM设备
-
源码
-
主要修改
-
头文件引入 && 配置相关宏

-
实现rtc_nvme设备的读写函数

-
配置nvmem_config并注册rtc_nvmem

-
修改ds1307_read_regs与ds1307_write_regs实现,使用i2c_transfer
若不修改,存在问题3.2


-
测试
节点信息 && 读取内容


指令写入测试
echo –ne "01234567890123456789012345678901234567890123456789012345" > /sys/bus/nvmem/devices/ds1307_nvram0/nvmem

若不加-ne,将打印报错信息-sh: echo: write error: File too large实际为字符串的截至符
程序读写测试
-
主要实现
- 向rtc-nvme节点写入数据并校验
- 从rtc-nvme读出数据
- 对rtc-nvme所有NV SRAM进行写入后读出,进行校验
源码
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#define DEVICE_PATH "/dev/rtc_ds1307"
#define DS1307_TIME_REGS_COUNT 7
#define NVMEM_PATH "/sys/bus/nvmem/devices/ds1307_nvram0/nvmem"
#define NVMEM_SIZE 56
#define NVMEM_TEST_SIZE 16
static uint8_t bcd2bin(uint8_t val)
{
return (val & 0x0f) + (val >> 4) * 10;
}
static uint8_t bin2bcd(uint8_t val)
{
return ((val / 10) << 4) + (val % 10);
}
static const char *wday_name[] = {
"", "Sun.", "Mon.", "Tue.", "Wed.", "Fri.", "Sat."
};
static void print_raw_regs(uint8_t *regs)
{
printf("\\n Raw data: \\n");
printf(" Sec: 0x%02X\\n", regs[0]);
printf(" Min: 0x%02X\\n", regs[1]);
printf(" Hour: 0x%02X\\n", regs[2]);
printf(" Week: 0x%02X\\n", regs[3]);
printf(" Day: 0x%02X\\n", regs[4]);
printf(" Mon: 0x%02X\\n", regs[5]);
printf(" Year: 0x%02X\\n", regs[6]);
}
static void print_time(uint8_t *regs)
{
uint8_t sec = bcd2bin(regs[0] & 0x7F);
uint8_t min = bcd2bin(regs[1]);
uint8_t hour = bcd2bin(regs[2] & 0x3F);
uint8_t wday = regs[3];
uint8_t mday = bcd2bin(regs[4]);
uint8_t mon = bcd2bin(regs[5]);
uint8_t year = bcd2bin(regs[6]);
printf("\\n Time:\\n");
printf(" 20%02d:%02d:%02d:(%s)\\n", year, mon, mday,
(wday >= 1 && wday <= 7) ? wday_name[wday] : "Unknowd");
printf(" %02d:%02d:%02d\\n", hour, min, sec);
}
static int read_time(void)
{
int fd;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;
fd = open(DEVICE_PATH, O_RDONLY);
if(fd < 0) {
perror("Cant open device");
return –1;
}
ret = read(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("read error");
close(fd);
return –1;
}
printf("read %d Byte\\n", ret);
print_raw_regs(time_regs);
print_time(time_regs);
}
static int write_time(int year, int mon, int mday, int wday,
int hour, int min, int sec)
{
int fd;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;
time_regs[0] = bin2bcd(sec);
time_regs[1] = bin2bcd(min);
time_regs[2] = bin2bcd(hour);
time_regs[3] = wday;
time_regs[4] = bin2bcd(mday);
time_regs[5] = bin2bcd(mon);
time_regs[6] = bin2bcd(year);
printf("\\n Write Time: \\n");
printf(" 20%02d:%02d:%02d:(%s)\\n", year, mon, mday,
(wday >= 1 && wday <= 7) ? wday_name[wday] : "Unknowd");
printf(" %02d:%02d:%02d\\n", hour, min, sec);
fd = open(DEVICE_PATH, O_WRONLY);
if(fd < 0) {
perror("Cant open device");
return –1;
}
ret = write(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("write error");
close(fd);
return –1;
}
printf("Write Byte:%d\\n", ret);
close(fd);
return 0;
};
int sync_time(void)
{
int fd;
struct tm sync_time;
time_t tNow = –1;
uint8_t time_regs[DS1307_TIME_REGS_COUNT];
ssize_t ret;
fd = open(DEVICE_PATH, O_WRONLY);
if(fd < 0) {
perror("Cant open device");
close(fd);
}
time(&tNow);
if(tNow < 0) {
perror("time");
close(fd);
return –1;
}
printf("time:%s\\n", ctime(&tNow));
memset(&sync_time, 0, sizeof(sync_time));
localtime_r(&tNow, &sync_time);
time_regs[0] = bin2bcd((uint8_t)sync_time.tm_sec);
time_regs[1] = bin2bcd((uint8_t)sync_time.tm_min);
time_regs[2] = bin2bcd((uint8_t)sync_time.tm_hour);
time_regs[3] = bin2bcd((uint8_t)sync_time.tm_wday + 1);
time_regs[4] = bin2bcd((uint8_t)sync_time.tm_mday);
time_regs[5] = bin2bcd((uint8_t)sync_time.tm_mon + 1);
time_regs[6] = bin2bcd((uint8_t)sync_time.tm_year – 100);
ret = write(fd, time_regs, DS1307_TIME_REGS_COUNT);
if(ret < 0) {
perror("sync error");
close(fd);
return –1;
}
close(fd);
return 0;
}
static int nvmem_read_test(void)
{
int fd;
uint8_t buffer[NVMEM_SIZE];
ssize_t ret;
int i;
printf("\\n=== NVMEM Read Test ===\\n");
/* Open NVMEM file */
fd = open(NVMEM_PATH, O_RDONLY);
if (fd < 0) {
perror("Failed to open NVMEM device");
return –1;
}
/* Set file offset to 0 */
lseek(fd, 0, SEEK_SET);
/* Read all NVMEM data */
ret = read(fd, buffer, NVMEM_SIZE);
if (ret < 0) {
perror("Failed to read NVMEM data");
close(fd);
return –1;
}
printf("Read %zd bytes from NVMEM (0x00-0x%02X)\\n", ret, ret–1);
/* Print the data in hex format */
printf("NVMEM Data (hex):\\n");
for (i = 0; i < ret; i++) {
if (i % 16 == 0) {
printf(" 0x%02X: ", i);
}
printf("%02X ", buffer[i]);
if (i % 16 == 15 || i == ret – 1) {
printf("\\n");
}
}
/* Print the data in ASCII format */
printf("NVMEM Data (ASCII):\\n");
for (i = 0; i < ret; i++) {
if (i % 16 == 0) {
printf(" 0x%02X: ", i);
}
if (buffer[i] >= 32 && buffer[i] <= 126) {
printf("%c ", buffer[i]);
} else {
printf(". ");
}
if (i % 16 == 15 || i == ret – 1) {
printf("\\n");
}
}
close(fd);
return 0;
}
static int nvmem_write_test(void)
{
int fd;
uint8_t write_data[NVMEM_SIZE];
uint8_t read_data[NVMEM_SIZE];
ssize_t ret;
int i;
int test_offset = 0;
const char *test_str = "DS1307 NVMEM Test 2026";
printf("\\n=== NVMEM Write Test ===\\n");
/* Open NVMEM file for writing */
fd = open(NVMEM_PATH, O_WRONLY);
if (fd < 0) {
perror("Failed to open NVMEM device for writing");
return –1;
}
/* Set file offset to 0 */
lseek(fd, 0, SEEK_SET);
/* Prepare test data */
memset(write_data, 0x55, NVMEM_SIZE); /* Fill with 0x55 first */
/* Write test string to specific offset */
strncpy((char *)&write_data[test_offset], test_str, sizeof(write_data) – test_offset – 1);
write_data[sizeof(write_data) – 1] = '\\0'; /* Ensure null termination */
printf("Writing test data to offset 0x%02X:\\n", test_offset);
printf(" Test string: %s\\n", test_str);
printf(" Data pattern: 0x55 for remaining bytes\\n");
/* Write data to NVMEM */
ret = write(fd, write_data, NVMEM_SIZE);
if (ret < 0) {
perror("Failed to write NVMEM data");
close(fd);
return –1;
}
printf("Wrote %zd bytes to NVMEM (0x00-0x%02X)\\n", ret, ret–1);
close(fd);
/* Verify the write operation by reading back */
fd = open(NVMEM_PATH, O_RDONLY);
if (fd < 0) {
perror("Failed to open NVMEM device for verification");
return –1;
}
/* Set file offset to 0 */
lseek(fd, 0, SEEK_SET);
ret = read(fd, read_data, NVMEM_SIZE);
if (ret < 0) {
perror("Failed to read back NVMEM data");
close(fd);
return –1;
}
/* Compare written data with read data */
if (memcmp(write_data, read_data, NVMEM_SIZE) == 0) {
printf("✓ Write verification PASSED\\n");
} else {
printf("✗ Write verification FAILED\\n");
/* Show mismatch details */
int match_count = 0;
int mismatch_count = 0;
for (i = 0; i < NVMEM_SIZE; i++) {
if (write_data[i] == read_data[i]) {
match_count++;
} else {
mismatch_count++;
/* Only show first 10 mismatches to avoid too much output */
if (mismatch_count <= 10) {
printf(" Mismatch at offset 0x%02X: expected 0x%02X, got 0x%02X\\n",
i, write_data[i], read_data[i]);
}
}
}
if (mismatch_count > 10) {
printf(" … and %d more mismatches\\n", mismatch_count – 10);
}
printf(" Total: %d matches, %d mismatches\\n", match_count, mismatch_count);
}
close(fd);
return 0;
}
static int nvmem_verify_test(void)
{
int fd;
uint8_t test_data[56];
uint8_t read_back[56];
ssize_t ret;
int i;
printf("\\n=== NVMEM Verification Test ===\\n");
for (i = 0; i < 56; i++) {
test_data[i] = i % 256;
}
/* Write test pattern */
fd = open(NVMEM_PATH, O_WRONLY);
if (fd < 0) {
perror("Failed to open NVMEM device");
return –1;
}
lseek(fd, 0, SEEK_SET); /* Seek to beginning */
ret = write(fd, test_data, sizeof(test_data));
if (ret < 0) {
perror("Failed to write verification data");
close(fd);
return –1;
}
close(fd);
/* Read back and verify */
fd = open(NVMEM_PATH, O_RDONLY);
if (fd < 0) {
perror("Failed to open NVMEM device for verification");
return –1;
}
lseek(fd, 0, SEEK_SET); /* Seek to beginning */
ret = read(fd, read_back, sizeof(read_back));
if (ret < 0) {
perror("Failed to read verification data");
close(fd);
return –1;
}
printf("Verification Data:\\n");
printf(" Expected: ");
for (i = 0; i < sizeof(test_data); i++) {
if(i%8 == 0 && i!=0)
printf("\\n");
printf("%02X ", test_data[i]);
}
printf("\\n");
printf(" Actual: ");
for (i = 0; i < sizeof(read_back); i++) {
if(i%8 == 0 && i!=0)
printf("\\n");
printf("%02X ", read_back[i]);
}
printf("\\n");
/* Compare */
if (memcmp(test_data, read_back, sizeof(test_data)) == 0) {
printf("✓ Data verification PASSED\\n");
} else {
printf("✗ Data verification FAILED\\n");
}
close(fd);
return 0;
}
void print_help(void)
{
printf("DS1307 Test Application\\n");
printf("Usage: %s <command> [arguments]\\n\\n", "test");
printf("Commands:\\n");
printf(" read – Read RTC time\\n");
printf(" write <y> <m> <d> <wd> <h> <mi> <s> – Write RTC time\\n");
printf(" sync – Sync system time to RTC\\n");
printf(" nvmem-read – Read and display NVMEM data\\n");
printf(" nvmem-write – Write test data to NVMEM\\n");
printf(" nvmem-verify – Verify NVMEM read/write functionality\\n");
printf(" help – Show this help message\\n\\n");
}
int main(int argc, char *argv[])
{
if (argc < 2) {
print_help();
return –1;
}
if(strcmp(argv[1], "read") == 0) {
return read_time();
}
else if(strcmp(argv[1], "write") == 0) {
if (argc != 9) {
printf("Error: write command requires 7 arguments (year month day weekday hour minute second)\\n");
return –1;
}
int year = atoi(argv[2]);
int mon = atoi(argv[3]);
int mday = atoi(argv[4]);
int wday = atoi(argv[5]);
int hour = atoi(argv[6]);
int min = atoi(argv[7]);
int sec = atoi(argv[8]);
return write_time(year, mon, mday, wday, hour, min, sec);
}
else if(strcmp(argv[1], "sync") == 0) {
return sync_time();
}
else if(strcmp(argv[1], "nvmem-read") == 0) {
return nvmem_read_test();
}
else if(strcmp(argv[1], "nvmem-write") == 0) {
return nvmem_write_test();
}
else if(strcmp(argv[1], "nvmem-verify") == 0) {
return nvmem_verify_test();
}
else if(strcmp(argv[1], "help") == 0) {
print_help();
return 0;
}
else {
printf("Error: Unknown command '%s'\\n", argv[1]);
print_help();
return –1;
}
return 0;
}
测试现象
写入

读取

完全校验

2.4 V2.0:使用Regmap实现寄存器访问
-
Regmap简介
Regmap是Linux内核提供的一个寄存器映射框架,用于抽象不同类型设备的寄存器访问方式。它的主要作用包括:
- 统一API :为不同总线(I2C、SPI、MMIO等)提供一致的寄存器访问接口
- 减少重复代码 :避免在每个驱动中重复实现相似的寄存器访问逻辑
- 自动缓存 :支持寄存器值的缓存,减少实际硬件访问次数
- 同步管理 :自动处理寄存器访问的同步和锁定
- 字节序转换 :自动处理不同设备的字节序差异
- 调试支持 :提供内置的调试功能,方便开发和故障排查
-
源码
-
主要修改
-
定义Regmap结构

-
ds1307_dev结构体新增成员regmap
-
probe函数中初始化regmap(在check_init之前)

-
替换寄存器访问函数

-
删除或屏蔽原寄存器访问函数

-
测试
读写字符设备节点/dev/rtc_ds1307测试

通过hwclock测试rtc设备

测试NV SRAM

Regmap调试节点

生成调试节点,但默认为只读
三 问题 && 经验
3.1 设备驱动匹配
当驱动中配置compatible为fog,ds1307时,设备树同步修改
此时原先驱动rtc-ds1307.c仍编译进内核
进入系统后,发现设备与驱动仍能匹配上

根本原因
rtc-ds1307.c中,id表中有ds1307

设备与驱动的匹配次序为of_mach—>ACPI—>id_table
id_table匹配时,使用的匹配项为设备的名称
在设备树中的节点转化为device_node节点过程中,会取compatible中逗号后的字符作为设备的name,即“ds1307”
此时,设备与驱动完成匹配
3.2 SMBus函数限制
在V1.0和V1.1版本中,ds1307_read_regs函数与ds1307_write_regs函数的使用i2c_smbus_read_i2c_block_data和i2c_smbus_write_i2c_block_data进行通信
SMBus函数有严格的32字节限制,导致如果一次性发送大于32字节的数据,无法正确发送

测试现象
在V1.1基础,引入NVMEM子系统(仍使用SMBus函数实现数据通信),并对节点进行测试
测试一次发大于32字节数据,实际只有前32位生效
hexdump –C /sys/bus/nvmem/devices/ds1307_nvram0/nvmem
echo "01234567890123456789012345678901234567890123456789" > /sys/bus/nvmem/devices/ds1307_nvram0/nvmem

实际后续的24字节可正常使用,通过加入偏移值写入
echo –ne "012345678901234567890123" | dd of=/sys/bus/nvmem/devices/ds1307_nvram0/nvmem bs=1 seek=32 conv=notrunc

V1.2修改实现后,测试现象如下
单次写入超出32字节数据
正常写入





