设备树:
&i2c4 {
pinctrl-0 = <&i2c4m3_pins>;
clock-frequency = <400000>;
status = "okay";
se8025tc: rtc@32 {
compatible = "seiko,se8025tc";
reg = <0x32>;
#clock-cells = <0>;
status = "okay";
};
};
内核配置:
config添加

.mk添加:

makefile需要同样添加上:

自己的代码如下:
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/of.h>
/* -------------------------- 核心修改:SE8025TC 寄存器定义 -------------------------- */
// 替换原 RX8025 的寄存器地址,适配 SE8025TC 手册
#define SE8025TC_REG_SEC 0x00 // 秒 (BCD)
#define SE8025TC_REG_MIN 0x01 // 分 (BCD)
#define SE8025TC_REG_HOUR 0x02 // 时 (BCD)
#define SE8025TC_REG_WEEK 0x03 // 星期 (独热码:0x01=周一, 0x02=周二...0x40=周日)
#define SE8025TC_REG_DAY 0x04 // 日 (BCD)
#define SE8025TC_REG_MONTH 0x05 // 月 (BCD)
#define SE8025TC_REG_YEAR 0x06 // 年 (BCD, 00-99 对应 2000-2099)
#define SE8025TC_REG_ALM_MIN 0x07 // 闹钟分 (BCD)
#define SE8025TC_REG_ALM_HOUR 0x08 // 闹钟时 (BCD)
#define SE8025TC_REG_ALM_WDAY 0x09 // 闹钟周/日 (由 WADA 位选择)
#define SE8025TC_REG_ALM_DAY 0x0A // 闹钟日扩展 (保留)
#define SE8025TC_REG_TIMER0 0x0B // 定时计数器0 (SE8025TC专属)
#define SE8025TC_REG_TIMER1 0x0C // 定时计数器1 (SE8025TC专属)
#define SE8025TC_REG_EXT 0x0D // 扩展寄存器 (FOUT/定时中断配置)
#define SE8025TC_REG_FLAG 0x0E // 标志寄存器 (中断/电压检测)
#define SE8025TC_REG_CTRL 0x0F // 控制寄存器 (中断使能/温度补偿)
/* -------------------------- SE8025TC 寄存器位定义 -------------------------- */
// 标志寄存器 (0x0E)
#define SE8025TC_FLAG_VLF BIT(1) // 电压低标志 (掉电后置1,需软件清零)
#define SE8025TC_FLAG_UF BIT(5) // 秒更新中断标志
#define SE8025TC_FLAG_TF BIT(4) // 定时中断标志
#define SE8025TC_FLAG_AF BIT(3) // 闹钟中断标志
// 控制寄存器 (0x0F)
#define SE8025TC_CTRL_UIE BIT(5) // 秒更新中断使能
#define SE8025TC_CTRL_TIE BIT(4) // 定时中断使能
#define SE8025TC_CTRL_AIE BIT(3) // 闹钟中断使能
#define SE8025TC_CTRL_CSEL0 BIT(1) // 温度补偿间隔选择0
#define SE8025TC_CTRL_CSEL1 BIT(2) // 温度补偿间隔选择1
// 扩展寄存器 (0x0D)
#define SE8025TC_EXT_WADA BIT(0) // 闹钟类型选择: 0=周闹钟, 1=日闹钟
#define SE8025TC_EXT_USEL BIT(1) // 秒更新中断模式: 0=秒, 1=分
#define SE8025TC_EXT_TSEL0 BIT(2) // 定时中断时钟源选择0
#define SE8025TC_EXT_TSEL1 BIT(3) // 定时中断时钟源选择1
#define SE8025TC_EXT_FSEL0 BIT(4) // FOUT频率选择0
#define SE8025TC_EXT_FSEL1 BIT(5) // FOUT频率选择1
#define SE8025TC_EXT_TE BIT(6) // 定时中断使能
#define SE8025TC_EXT_FOE BIT(7) // FOUT输出使能
// SE8025TC 设备私有数据
struct se8025tc_data {
struct i2c_client *client;
struct rtc_device *rtc;
bool vlf_detected; // 电压低标志
};
/* -------------------------- 核心修改:星期独热码 <-> 数字 转换 -------------------------- */
// SE8025TC 星期寄存器是独热码: 0x01=周一, 0x02=周二, ..., 0x40=周日
static u8 se8025tc_week_heat2num(u8 heat)
{
switch (heat) {
case 0x01: return 1; // 周一
case 0x02: return 2; // 周二
case 0x04: return 3; // 周三
case 0x08: return 4; // 周四
case 0x10: return 5; // 周五
case 0x20: return 6; // 周六
case 0x40: return 7; // 周日
default: return 1; // 默认周一
}
}
static u8 se8025tc_week_num2heat(u8 num)
{
switch (num) {
case 1: return 0x01; // 周一
case 2: return 0x02; // 周二
case 3: return 0x04; // 周三
case 4: return 0x08; // 周四
case 5: return 0x10; // 周五
case 6: return 0x20; // 周六
case 7: return 0x40; // 周日
default: return 0x01; // 默认周一
}
}
/* -------------------------- RTC 时间读取 -------------------------- */
static int se8025tc_read_time(struct device *dev, struct rtc_time *tm)
{
struct se8025tc_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u8 buf[7];
int ret;
// 连续读取秒~年寄存器 (0x00-0x06)
ret = i2c_smbus_read_i2c_block_data(client, SE8025TC_REG_SEC, 7, buf);
if (ret < 0) {
dev_err(dev, "Failed to read time data: %d\n", ret);
return ret;
}
// 转换 BCD 码到十进制 (注意: 秒寄存器最高位是 CH 位,需清零)
tm->tm_sec = bcd2bin(buf[0] & 0x7F);
tm->tm_min = bcd2bin(buf[1]);
tm->tm_hour = bcd2bin(buf[2]);
tm->tm_mday = bcd2bin(buf[4]);
tm->tm_mon = bcd2bin(buf[5]) - 1; // RTC月是1-12, tm_mon是0-11
tm->tm_year = bcd2bin(buf[6]) + 100; // RTC年是00-99(2000-2099), tm_year是1900起
tm->tm_wday = se8025tc_week_heat2num(buf[3]); // 独热码转数字
// 对关键字段做边界校验,替换为默认值,防止初始值乱码
if (tm->tm_mon < 0 || tm->tm_mon > 11) tm->tm_mon = 2; // 默认3月
if (tm->tm_mday < 1 || tm->tm_mday > 31) tm->tm_mday = 14; // 默认14日
if (tm->tm_year < 100 || tm->tm_year > 199) tm->tm_year = 126; // 默认2026年
if (tm->tm_hour > 23) tm->tm_hour = 12;
if (tm->tm_min > 59) tm->tm_min = 0;
if (tm->tm_sec > 59) tm->tm_sec = 0;
// 验证时间合法性
if (rtc_valid_tm(tm)) {
dev_warn(dev, "Invalid time read: %ptRr\n", tm);
return -EINVAL;
}
return 0;
}
/* -------------------------- RTC 时间写入 -------------------------- */
static int se8025tc_set_time(struct device *dev, struct rtc_time *tm)
{
struct se8025tc_data *data = dev_get_drvdata(dev);
struct i2c_client *client = data->client;
u8 buf[7];
int ret;
// 转换十进制到 BCD 码
buf[0] = bin2bcd(tm->tm_sec) & 0x7F; // 秒, 清零CH位
buf[1] = bin2bcd(tm->tm_min);
buf[2] = bin2bcd(tm->tm_hour);
buf[3] = se8025tc_week_num2heat(tm->tm_wday); // 数字转独热码
buf[4] = bin2bcd(tm->tm_mday);
buf[5] = bin2bcd(tm->tm_mon + 1); // tm_mon是0-11, RTC月是1-12
buf[6] = bin2bcd(tm->tm_year - 100); // tm_year是1900起, RTC年是00-99(2000-2099)
// 连续写入秒~年寄存器 (0x00-0x06)
ret = i2c_smbus_write_i2c_block_data(client, SE8025TC_REG_SEC, 7, buf);
if (ret < 0) {
dev_err(dev, "Failed to write time data: %d\n", ret);
return ret;
}
return 0;
}
/* -------------------------- 中断处理 (适配 SE8025TC 标志位) -------------------------- */
static irqreturn_t se8025tc_irq_handler(int irq, void *dev_id)
{
struct se8025tc_data *data = dev_id;
struct i2c_client *client = data->client;
u8 flag;
int ret;
// 读取标志寄存器
ret = i2c_smbus_read_byte_data(client, SE8025TC_REG_FLAG);
if (ret < 0) {
dev_err(&client->dev, "Failed to read flag reg: %d\n", ret);
return IRQ_NONE;
}
flag = ret;
// 处理电压低标志
if (flag & SE8025TC_FLAG_VLF) {
data->vlf_detected = true;
dev_warn(&client->dev, "Voltage low detected, time may be invalid\n");
// 清零VLF标志
i2c_smbus_write_byte_data(client, SE8025TC_REG_FLAG, flag & ~SE8025TC_FLAG_VLF);
}
// 处理闹钟中断
if (flag & SE8025TC_FLAG_AF) {
rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_AF);
// 清零闹钟中断标志
i2c_smbus_write_byte_data(client, SE8025TC_REG_FLAG, flag & ~SE8025TC_FLAG_AF);
return IRQ_HANDLED;
}
// 处理秒更新中断
if (flag & SE8025TC_FLAG_UF) {
rtc_update_irq(data->rtc, 1, RTC_IRQF | RTC_UF);
// 清零秒更新中断标志
i2c_smbus_write_byte_data(client, SE8025TC_REG_FLAG, flag & ~SE8025TC_FLAG_UF);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/* -------------------------- RTC 设备操作集 -------------------------- */
static const struct rtc_class_ops se8025tc_rtc_ops = {
.read_time = se8025tc_read_time,
.set_time = se8025tc_set_time,
};
/* -------------------------- 设备初始化 (适配 SE8025TC 寄存器) -------------------------- */
static int se8025tc_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct se8025tc_data *data;
int ret, irq;
u8 ctrl, flag, ext;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
// 分配私有数据
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->client = client;
i2c_set_clientdata(client, data);
// 1. 检查设备是否存在
ret = i2c_smbus_read_byte_data(client, SE8025TC_REG_SEC);
if (ret < 0) {
dev_err(&client->dev, "SE8025TC not found: %d\n", ret);
return ret;
}
printk("find i2c SE8025TC!\n");
// 2. 初始化 SE8025TC 寄存器
// 读取当前控制寄存器
ctrl = i2c_smbus_read_byte_data(client, SE8025TC_REG_CTRL);
flag = i2c_smbus_read_byte_data(client, SE8025TC_REG_FLAG);
ext = i2c_smbus_read_byte_data(client, SE8025TC_REG_EXT);
// 配置: 使能秒更新中断、闹钟中断,温度补偿间隔30s,周闹钟模式
ctrl = SE8025TC_CTRL_UIE | SE8025TC_CTRL_AIE |
SE8025TC_CTRL_CSEL0 | SE8025TC_CTRL_CSEL1;
// 清零所有中断标志
flag &= ~(SE8025TC_FLAG_UF | SE8025TC_FLAG_AF | SE8025TC_FLAG_VLF);
// 配置: 周闹钟、秒更新模式
ext = (ext & ~SE8025TC_EXT_WADA) | SE8025TC_EXT_USEL;
// 写入配置
i2c_smbus_write_byte_data(client, SE8025TC_REG_CTRL, ctrl);
i2c_smbus_write_byte_data(client, SE8025TC_REG_FLAG, flag);
i2c_smbus_write_byte_data(client, SE8025TC_REG_EXT, ext);
printk("init SE8025TC reg!\n");
struct rtc_time init_tm;
//这儿是因为有的rtc初始化读的时候这儿会乱码,所以加了个初始时间,后考虑到断电或者reboot后时间保持问题更改为read时加上时间格式校验。
// // 设置默认时间:2026-03-14 12:00:00 周一
// init_tm.tm_sec = 0;
// init_tm.tm_min = 0;
// init_tm.tm_hour = 12;
// init_tm.tm_mday = 14;
// init_tm.tm_mon = 2; // tm_mon是0-11,对应1-12月,2=3月
// init_tm.tm_year = 126; // tm_year是1900起,126=2026年
// init_tm.tm_wday = 1; // 周一
// // 调用set_time函数写入默认时间
// ret = se8025tc_set_time(&client->dev, &init_tm);
// if (ret < 0) {
// dev_warn(&client->dev, "Failed to init default time: %d\n", ret);
// } else {
// dev_info(&client->dev, "Init default time: 2026-03-14 12:00:00\n");
// }
// 3. 注册 RTC 设备
data->rtc = devm_rtc_allocate_device(&client->dev);
if (IS_ERR(data->rtc)) {
dev_err(&client->dev, "Failed to allocate RTC device\n");
return PTR_ERR(data->rtc);
}
data->rtc->ops = &se8025tc_rtc_ops;
data->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
data->rtc->range_max = RTC_TIMESTAMP_END_2099;
ret = devm_rtc_register_device(data->rtc);
if (ret) {
dev_err(&client->dev, "Failed to register RTC device: %d\n", ret);
return ret;
}
// 4. 注册中断处理 (如果设备有中断引脚)
irq = client->irq;
if (irq > 0) {
ret = devm_request_irq(&client->dev, irq, se8025tc_irq_handler,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"se8025tc", data);
if (ret) {
dev_warn(&client->dev, "Failed to request IRQ %d: %d\n", irq, ret);
}
}
dev_info(&client->dev, "SE8025TC RTC initialized successfully\n");
return 0;
}
/* -------------------------- 设备匹配表 & 驱动注册 -------------------------- */
static const struct i2c_device_id se8025tc_id[] = {
{ "se8025tc", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, se8025tc_id);
static const struct of_device_id se8025tc_of_match[] = {
{ .compatible = "seiko,se8025tc" },
{ }
};
MODULE_DEVICE_TABLE(of, se8025tc_of_match);
static struct i2c_driver se8025tc_driver = {
.driver = {
.name = "se8025tc",
.of_match_table = se8025tc_of_match,
},
.probe = se8025tc_probe,
.id_table = se8025tc_id,
};
module_i2c_driver(se8025tc_driver);
MODULE_DESCRIPTION("SE8025TC RTC Driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Custom Driver Adaptation");
调试:
扫描总线4上的所有可能地址(0x03-0x77)
i2cdetect -y -r 4

发现32地址下有设备
尝试读取寄存器0(通常是秒/状态寄存器)
i2cget -y 4 0x32 0x00

连续读取多个寄存器查看数据模式
i2cdump -y 4 0x32

如果驱动加载了的话执行上述i2c检测的指令会提示busy
加载了的话首先通过dmesg | grep 8025查看驱动加载是否成功。

加载成功的话通过hwclock看一下目前系统时间

之后通过date 031609042026.00更改一下系统时间

然后使用 hwclock -w -f /dev/rtc0 将系统时间同步到rtc
此处可以不加-f,默认就是rtc0
再使用hwclock打印一下是否修改成功
还需要调试reboot后是否能保持时间

被折叠的 条评论
为什么被折叠?



