1. 获取DS1307驱动源码
1.1 源码位置
DS1307是I2C接口的实时时钟芯片,其驱动在Linux内核中的位置:
drivers/rtc/rtc-ds1307.c
1.2 获取方法
方法1:从内核源码树获取
# 克隆Linux内核源码
git clone https://github.com/torvalds/linux.git
cd linux
# 查看DS1307驱动
cat drivers/rtc/rtc-ds1307.c
方法2:在线查看
-
https://elixir.bootlin.com/linux/latest/source/drivers/rtc/rtc-ds1307.c
2. 驱动源码结构分析
2.1 驱动模块基本结构
// 模块信息
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("DS1307 and similar I2C RTC driver");
MODULE_LICENSE("GPL");
// 支持的设备ID表
static const struct i2c_device_id ds1307_id[] = {
{ "ds1307", ds_1307 },
{ "ds1337", ds_1337 },
{ "ds1338", ds_1338 },
{ "ds1339", ds_1339 },
{ "ds1340", ds_1340 },
{ "m41t00", m41t00 },
{ "mcp7941x", mcp7941x },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(i2c, ds1307_id);
2.2 设备树兼容性列表
#ifdef CONFIG_OF
static const struct of_device_id ds1307_of_match[] = {
{ .compatible = "dallas,ds1307" },
{ .compatible = "dallas,ds1337" },
{ .compatible = "dallas,ds1338" },
{ .compatible = "dallas,ds1339" },
{ .compatible = "dallas,ds1340" },
{ .compatible = "microchip,mcp7941x" },
{ /* LIST END */ }
};
MODULE_DEVICE_TABLE(of, ds1307_of_match);
#endif
3. 核心数据结构分析
3.1 芯片类型定义
enum ds_type {
ds_1307, // DS1307
ds_1337, // DS1337
ds_1338, // DS1338
ds_1339, // DS1339
ds_1340, // DS1340
m41t00, // M41T00
mcp794xx, // MCP794xx系列
// ... 其他型号
};
3.2 设备私有数据结构
struct ds1307 {
struct i2c_client *client;
struct rtc_device *rtc;
struct regmap *regmap;
const struct chip_desc *chip;
u8 reg_addr;
u8 offset;
bool nvram; // 是否有NVRAM
struct nvmem_config nvmem_cfg;
// 其他芯片特定数据
};
3.3 芯片描述结构
struct chip_desc {
u8 nvram_offset; // NVRAM偏移地址
u8 nvram_size; // NVRAM大小
u8 alarm_offset; // 闹钟寄存器偏移
bool nvram_24aa02; // 24AA02 EEPROM
bool alarm; // 支持闹钟
bool trickle_charger; // 涓流充电
u32 (*trickle_charger_setup)(struct i2c_client *, uint32_t, bool);
};
4. 关键函数分析
4.1 探测函数(probe)
static int ds1307_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct ds1307 *ds1307;
int err = -ENODEV;
int tmp;
const struct chip_desc *chip;
// 1. 分配设备结构
ds1307 = devm_kzalloc(&client->dev, sizeof(*ds1307), GFP_KERNEL);
// 2. 初始化设备结构
ds1307->client = client;
i2c_set_clientdata(client, ds1307);
// 3. 识别芯片类型
chip = &chips[id->driver_data];
ds1307->chip = chip;
// 4. 读取并验证芯片
tmp = ds1307_get_reg(ds1307, DS1307_REG_SECS);
if (tmp < 0)
return tmp;
// 5. 检查时钟是否运行
if (tmp & DS1307_BIT_CH) {
// 时钟停止,尝试启动
ds1307_set_reg(ds1307, DS1307_REG_SECS,
tmp & ~DS1307_BIT_CH);
}
// 6. 注册RTC设备
ds1307->rtc = devm_rtc_device_register(&client->dev,
client->name,
&ds1307_rtc_ops,
THIS_MODULE);
// 7. 初始化NVRAM(如果支持)
if (chip->nvram_size) {
ds1307_nvram_register(ds1307, chip->nvram_size);
}
return 0;
}
4.2 RTC操作集
static const struct rtc_class_ops ds1307_rtc_ops = {
.read_time = ds1307_rtc_read_time,
.set_time = ds1307_rtc_set_time,
.read_alarm = ds1307_rtc_read_alarm, // 如果支持闹钟
.set_alarm = ds1307_rtc_set_alarm, // 如果支持闹钟
.alarm_irq_enable = ds1307_rtc_alarm_irq_enable,
};
4.3 时间读取函数
static int ds1307_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
int err;
u8 regs[7];
// 读取时间寄存器
err = regmap_bulk_read(ds1307->regmap, ds1307->offset, regs, 7);
if (err)
return err;
// 解码BCD格式的时间
tm->tm_sec = bcd2bin(regs[DS1307_REG_SECS] & 0x7f);
tm->tm_min = bcd2bin(regs[DS1307_REG_MIN] & 0x7f);
// 处理12/24小时制
if (regs[DS1307_REG_HOUR] & DS1307_BIT_12HR) {
// 12小时制
tm->tm_hour = bcd2bin(regs[DS1307_REG_HOUR] & 0x1f);
if (regs[DS1307_REG_HOUR] & DS1307_BIT_PM)
tm->tm_hour += 12;
} else {
// 24小时制
tm->tm_hour = bcd2bin(regs[DS1307_REG_HOUR] & 0x3f);
}
tm->tm_wday = bcd2bin(regs[DS1307_REG_WDAY] & 0x07) - 1;
tm->tm_mday = bcd2bin(regs[DS1307_REG_MDAY] & 0x3f);
tm->tm_mon = bcd2bin(regs[DS1307_REG_MONTH] & 0x1f) - 1;
tm->tm_year = bcd2bin(regs[DS1307_REG_YEAR]) + 100;
return 0;
}
4.4 时间设置函数
static int ds1307_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
u8 regs[7];
int err;
// 编码为BCD格式
regs[DS1307_REG_SECS] = bin2bcd(tm->tm_sec);
regs[DS1307_REG_MIN] = bin2bcd(tm->tm_min);
regs[DS1307_REG_HOUR] = bin2bcd(tm->tm_hour); // 24小时制
regs[DS1307_REG_WDAY] = bin2bcd(tm->tm_wday + 1);
regs[DS1307_REG_MDAY] = bin2bcd(tm->tm_mday);
regs[DS1307_REG_MONTH] = bin2bcd(tm->tm_mon + 1);
regs[DS1307_REG_YEAR] = bin2bcd(tm->tm_year - 100);
// 写入时间寄存器
err = regmap_bulk_write(ds1307->regmap, ds1307->offset, regs, 7);
return err;
}
5. 寄存器定义
// DS1307寄存器地址
#define DS1307_REG_SECS 0x00
#define DS1307_REG_MIN 0x01
#define DS1307_REG_HOUR 0x02
#define DS1307_REG_WDAY 0x03
#define DS1307_REG_MDAY 0x04
#define DS1307_REG_MONTH 0x05
#define DS1307_REG_YEAR 0x06
#define DS1307_REG_CONTROL 0x07
#define DS1307_REG_NVRAM 0x08 // NVRAM起始地址
// 寄存器位定义
#define DS1307_BIT_CH 0x80 // 时钟停止位
#define DS1307_BIT_12HR 0x40 // 12小时制标志
#define DS1307_BIT_PM 0x20 // PM标志
#define DS1307_BIT_OUT 0x80 // 控制寄存器输出位
#define DS1307_BIT_SQWE 0x10 // 方波使能
#define DS1307_BIT_RS1 0x02 // 频率选择位1
#define DS1307_BIT_RS0 0x01 // 频率选择位0
6. 驱动初始化与注册
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "rtc-ds1307",
.of_match_table = of_match_ptr(ds1307_of_match),
},
.probe = ds1307_probe,
.remove = ds1307_remove,
.id_table = ds1307_id,
};
module_i2c_driver(ds1307_driver);
7. 设备树绑定示例
// DS1307设备树节点示例
i2c {
rtc@68 {
compatible = "dallas,ds1307";
reg = <0x68>;
// 可选:涓流充电配置
trickle-resistor-ohms = <250>;
trickle-diode-disable;
};
};
8. 关键功能分析
8.1 涓流充电功能
static int ds1307_trickle_init(struct i2c_client *client)
{
// 配置涓流充电器
// DS1307支持通过一个二极管和电阻连接Vcc到Vbat
// 用于在外部电源存在时给备用电池充电
}
8.2 闹钟功能(部分型号支持)
static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
// 读取闹钟寄存器
// DS1337/DS1338/DS1339支持闹钟功能
}
8.3 NVRAM访问
static int ds1307_nvram_read(void *priv, unsigned int offset,
void *val, size_t bytes)
{
// 读取NVRAM(56字节用户RAM)
}
9. 编译配置选项
# 内核配置
CONFIG_RTC_DRV_DS1307=y
10. 用户空间接口
驱动注册后,可通过以下接口访问:
# 查看硬件时间
hwclock -r
# 设置硬件时间
hwclock -w --utc
# 通过sysfs接口
/sys/class/rtc/rtc0/
总结
DS1307驱动的主要特点:
-
多芯片支持:支持DS1307系列及兼容芯片
-
完整RTC功能:时间读写、闹钟(部分型号)、NVRAM
-
硬件抽象:通过regmap API访问I2C寄存器
-
设备树支持:完整的设备树绑定
-
模块化设计:良好的可扩展性,易于支持新芯片
该驱动是典型的I2C设备驱动,采用了标准的RTC框架,具有良好的可维护性和可移植性。
5531

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



