Linux内核DS1307驱动源码分析

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驱动的主要特点:

  1. 多芯片支持:支持DS1307系列及兼容芯片

  2. 完整RTC功能:时间读写、闹钟(部分型号)、NVRAM

  3. 硬件抽象:通过regmap API访问I2C寄存器

  4. 设备树支持:完整的设备树绑定

  5. 模块化设计:良好的可扩展性,易于支持新芯片

该驱动是典型的I2C设备驱动,采用了标准的RTC框架,具有良好的可维护性和可移植性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值