Linux内核SPI时钟芯片DS3234驱动源码分析

1. 获取DS3234 SPI RTC驱动源码

1.1 源码位置

DS3234是SPI接口的高精度实时时钟芯片,其驱动在Linux内核中的位置:

drivers/rtc/rtc-ds3234.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-ds3234.c

2. 驱动源码详细分析

2.1 模块头文件和宏定义

#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/module.h>

MODULE_DESCRIPTION("DS3234 SPI RTC driver");
MODULE_AUTHOR("Dennis Aberilla <denzzzhome@yahoo.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("spi:ds3234");

2.2 寄存器定义

/* DS3234寄存器定义 */
#define DS3234_REG_SECONDS      0x00
#define DS3234_REG_MINUTES      0x01
#define DS3234_REG_HOURS        0x02
#define DS3234_REG_DAY          0x03
#define DS3234_REG_DATE         0x04
#define DS3234_REG_MONTH        0x05
#define DS3234_REG_YEAR         0x06
#define DS3234_REG_ALM1_SECONDS 0x07
#define DS3234_REG_ALM1_MINUTES 0x08
#define DS3234_REG_ALM1_HOURS   0x09
#define DS3234_REG_ALM1_DAYDATE 0x0A
#define DS3234_REG_ALM2_MINUTES 0x0B
#define DS3234_REG_ALM2_HOURS   0x0C
#define DS3234_REG_ALM2_DAYDATE 0x0D
#define DS3234_REG_CONTROL      0x0E
#define DS3234_REG_STATUS       0x0F
#define DS3234_REG_AGING_OFFSET 0x10
#define DS3234_REG_TEMP_MSB     0x11
#define DS3234_REG_TEMP_LSB     0x12

/* 控制寄存器位定义 */
#define DS3234_CTRL_EOSC   0x80  /* 振荡器使能/禁用 */
#define DS3234_CTRL_BBSQW  0x40  /* 电池备份方波使能 */
#define DS3234_CTRL_CONV   0x20  /* 转换温度 */
#define DS3234_CTRL_RS2    0x10  /* 频率选择位2 */
#define DS3234_CTRL_RS1    0x08  /* 频率选择位1 */
#define DS3234_CTRL_INTCN  0x04  /* 中断控制 */
#define DS3234_CTRL_A2IE   0x02  /* 闹钟2中断使能 */
#define DS3234_CTRL_A1IE   0x01  /* 闹钟1中断使能 */

/* 状态寄存器位定义 */
#define DS3234_STAT_OSF    0x80  /* 振荡器停止标志 */
#define DS3234_STAT_EN32KHZ 0x08 /* 32kHz输出使能 */
#define DS3234_STAT_BSY    0x04  /* 繁忙标志 */
#define DS3234_STAT_A2F    0x02  /* 闹钟2标志 */
#define DS3234_STAT_A1F    0x01  /* 闹钟1标志 */

/* 小时寄存器位定义 */
#define DS3234_HR_MIL      0x40  /* 24小时模式 */
#define DS3234_HR_12HR     0x20  /* 12小时模式 */
#define DS3234_HR_PM       0x20  /* PM标志(12小时模式) */

2.3 设备私有数据结构

struct ds3234 {
    struct rtc_device *rtc;      /* RTC设备指针 */
    struct spi_device *spi;      /* SPI设备指针 */
    u8 *tx_buf;                  /* SPI发送缓冲区 */
    u8 *rx_buf;                  /* SPI接收缓冲区 */
    int secs;                    /* 秒缓存 */
    int irq;                     /* 中断号 */
    unsigned long flags;         /* 状态标志 */
    
    /* 温度相关 */
    struct delayed_work work;    /* 延迟工作队列 */
    int temperature;             /* 温度值 */
};

2.4 SPI读写操作函数

/* SPI读寄存器 */
static int ds3234_read_reg(struct ds3234 *ds3234, u8 addr, u8 *value)
{
    int ret;
    struct spi_message msg;
    struct spi_transfer xfer[2];
    u8 tx_buf[2];
    u8 rx_buf[2];
    
    /* 准备SPI传输 */
    memset(xfer, 0, sizeof(xfer));
    spi_message_init(&msg);
    
    /* 写寄存器地址 */
    tx_buf[0] = addr | 0x80;  /* 设置读位 */
    xfer[0].tx_buf = tx_buf;
    xfer[0].len = 1;
    xfer[0].cs_change = 0;  /* 保持片选有效 */
    spi_message_add_tail(&xfer[0], &msg);
    
    /* 读取数据 */
    xfer[1].rx_buf = rx_buf;
    xfer[1].len = 1;
    spi_message_add_tail(&xfer[1], &msg);
    
    ret = spi_sync(ds3234->spi, &msg);
    if (ret >= 0)
        *value = rx_buf[1];
    
    return ret;
}

/* SPI写寄存器 */
static int ds3234_write_reg(struct ds3234 *ds3234, u8 addr, u8 value)
{
    int ret;
    u8 tx_buf[2];
    
    tx_buf[0] = addr & 0x7F;  /* 清除读位 */
    tx_buf[1] = value;
    
    ret = spi_write(ds3234->spi, tx_buf, sizeof(tx_buf));
    return ret;
}

/* 批量读取多个寄存器 */
static int ds3234_read_regs(struct ds3234 *ds3234, u8 addr, 
                           u8 *buf, u16 len)
{
    int ret;
    struct spi_message msg;
    struct spi_transfer xfer[2];
    u8 tx_buf[1];
    
    memset(xfer, 0, sizeof(xfer));
    spi_message_init(&msg);
    
    /* 发送读取命令 */
    tx_buf[0] = addr | 0x80;  /* 设置读位 */
    xfer[0].tx_buf = tx_buf;
    xfer[0].len = 1;
    xfer[0].cs_change = 0;  /* 保持片选有效 */
    spi_message_add_tail(&xfer[0], &msg);
    
    /* 读取数据 */
    xfer[1].rx_buf = buf;
    xfer[1].len = len;
    spi_message_add_tail(&xfer[1], &msg);
    
    ret = spi_sync(ds3234->spi, &msg);
    return ret;
}

2.5 时间读取函数

static int ds3234_read_time(struct device *dev, struct rtc_time *dt)
{
    struct ds3234 *ds3234 = dev_get_drvdata(dev);
    u8 buf[7];
    u8 val;
    int err;
    
    /* 读取时间寄存器(秒、分、时、星期、日、月、年) */
    err = ds3234_read_regs(ds3234, DS3234_REG_SECONDS, buf, sizeof(buf));
    if (err)
        return err;
    
    /* 解码秒 */
    dt->tm_sec = bcd2bin(buf[0] & 0x7F);
    
    /* 解码分 */
    dt->tm_min = bcd2bin(buf[1] & 0x7F);
    
    /* 解码时,处理12/24小时制 */
    if (buf[2] & DS3234_HR_12HR) {
        /* 12小时制 */
        dt->tm_hour = bcd2bin(buf[2] & 0x1F);
        if (buf[2] & DS3234_HR_PM)
            dt->tm_hour += 12;
    } else {
        /* 24小时制 */
        dt->tm_hour = bcd2bin(buf[2] & 0x3F);
    }
    
    /* 解码星期(1-7,1=星期日) */
    dt->tm_wday = bcd2bin(buf[3] & 0x07) - 1;
    
    /* 解码日 */
    dt->tm_mday = bcd2bin(buf[4] & 0x3F);
    
    /* 解码月(包括世纪位) */
    dt->tm_mon = bcd2bin(buf[5] & 0x1F) - 1;
    
    /* 解码年(假设21世纪) */
    dt->tm_year = bcd2bin(buf[6]) + 100;
    
    /* 检查振荡器停止标志 */
    err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &val);
    if (err)
        return err;
    
    if (val & DS3234_STAT_OSF) {
        dev_warn(dev, "oscillator stopped, time is unreliable\n");
        return -EINVAL;
    }
    
    return 0;
}

2.6 时间设置函数

static int ds3234_set_time(struct device *dev, struct rtc_time *dt)
{
    struct ds3234 *ds3234 = dev_get_drvdata(dev);
    u8 buf[8];  /* 寄存器地址 + 7个时间寄存器 */
    u8 val;
    int err;
    
    /* 构建写缓冲区 */
    buf[0] = DS3234_REG_SECONDS & 0x7F;  /* 写命令 */
    
    /* 编码秒 */
    buf[1] = bin2bcd(dt->tm_sec);
    
    /* 编码分 */
    buf[2] = bin2bcd(dt->tm_min);
    
    /* 编码时(24小时制) */
    buf[3] = bin2bcd(dt->tm_hour) & 0x3F;  /* 24小时模式 */
    
    /* 编码星期 */
    buf[4] = bin2bcd(dt->tm_wday + 1);
    
    /* 编码日 */
    buf[5] = bin2bcd(dt->tm_mday);
    
    /* 编码月 */
    buf[6] = bin2bcd(dt->tm_mon + 1);
    
    /* 编码年 */
    if (dt->tm_year >= 100)
        buf[7] = bin2bcd(dt->tm_year - 100);
    else
        buf[7] = bin2bcd(dt->tm_year);
    
    /* 写入时间寄存器 */
    err = spi_write(ds3234->spi, buf, sizeof(buf));
    if (err)
        return err;
    
    /* 清除振荡器停止标志 */
    err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &val);
    if (err)
        return err;
    
    val &= ~DS3234_STAT_OSF;  /* 清除OSF位 */
    err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, val);
    
    return err;
}

2.7 温度读取功能

/* 温度转换函数 */
static int ds3234_read_temp(struct ds3234 *ds3234, int *temp)
{
    u8 buf[2];
    s16 raw_temp;
    int err;
    
    /* 启动温度转换 */
    err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL, 
                          DS3234_CTRL_CONV);
    if (err)
        return err;
    
    /* 等待转换完成(最大10ms) */
    msleep(10);
    
    /* 读取温度寄存器 */
    err = ds3234_read_regs(ds3234, DS3234_REG_TEMP_MSB, buf, 2);
    if (err)
        return err;
    
    /* 转换温度值 */
    raw_temp = (buf[0] << 8) | buf[1];
    raw_temp >>= 6;  /* 温度值在14位中,右移6位 */
    
    *temp = (raw_temp * 250) / 1000;  /* 转换为摄氏度 */
    
    return 0;
}

/* 温度读取工作队列 */
static void ds3234_temp_work(struct work_struct *work)
{
    struct ds3234 *ds3234 = container_of(work, struct ds3234, 
                                        work.work);
    int temp;
    int err;
    
    err = ds3234_read_temp(ds3234, &temp);
    if (!err) {
        ds3234->temperature = temp;
        dev_dbg(&ds3234->spi->dev, "temperature: %d°C\n", temp);
    }
    
    /* 10秒后再次读取 */
    schedule_delayed_work(&ds3234->work, 10 * HZ);
}

2.8 闹钟功能

/* 设置闹钟 */
static int ds3234_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
    struct ds3234 *ds3234 = dev_get_drvdata(dev);
    u8 buf[4];
    u8 ctrl, stat;
    int err;
    
    /* 读取控制寄存器 */
    err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, &ctrl);
    if (err)
        return err;
    
    /* 读取状态寄存器 */
    err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &stat);
    if (err)
        return err;
    
    /* 设置闹钟1时间(秒、分、时、日/星期) */
    buf[0] = DS3234_REG_ALM1_SECONDS;
    buf[1] = bin2bcd(alarm->time.tm_sec) | 0x80;  /* 秒 + A1M1 */
    buf[2] = bin2bcd(alarm->time.tm_min) | 0x80;  /* 分 + A1M2 */
    buf[3] = bin2bcd(alarm->time.tm_hour) | 0x80; /* 时 + A1M3 */
    buf[4] = bin2bcd(alarm->time.tm_mday) | 0x80; /* 日 + A1M4 */
    
    err = spi_write(ds3234->spi, buf, 5);
    if (err)
        return err;
    
    /* 设置闹钟使能 */
    if (alarm->enabled) {
        ctrl |= DS3234_CTRL_A1IE;  /* 使能闹钟1中断 */
        ctrl |= DS3234_CTRL_INTCN; /* 设置中断模式 */
    } else {
        ctrl &= ~DS3234_CTRL_A1IE; /* 禁用闹钟1中断 */
    }
    
    err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL, ctrl);
    if (err)
        return err;
    
    /* 清除闹钟标志 */
    stat &= ~DS3234_STAT_A1F;
    err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, stat);
    
    return err;
}

/* 读取闹钟 */
static int ds3234_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
    struct ds3234 *ds3234 = dev_get_drvdata(dev);
    u8 buf[4];
    u8 ctrl, stat;
    int err;
    
    /* 读取闹钟寄存器 */
    err = ds3234_read_regs(ds3234, DS3234_REG_ALM1_SECONDS, buf, 4);
    if (err)
        return err;
    
    /* 解码闹钟时间 */
    alarm->time.tm_sec = bcd2bin(buf[0] & 0x7F);
    alarm->time.tm_min = bcd2bin(buf[1] & 0x7F);
    alarm->time.tm_hour = bcd2bin(buf[2] & 0x3F);
    alarm->time.tm_mday = bcd2bin(buf[3] & 0x3F);
    
    /* 检查闹钟使能状态 */
    err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, &ctrl);
    if (err)
        return err;
    
    alarm->enabled = !!(ctrl & DS3234_CTRL_A1IE);
    
    /* 检查闹钟触发标志 */
    err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, &stat);
    if (err)
        return err;
    
    alarm->pending = !!(stat & DS3234_STAT_A1F);
    
    return 0;
}

2.9 RTC操作集

static const struct rtc_class_ops ds3234_rtc_ops = {
    .read_time = ds3234_read_time,
    .set_time = ds3234_set_time,
    .read_alarm = ds3234_read_alarm,
    .set_alarm = ds3234_set_alarm,
    .alarm_irq_enable = ds3234_alarm_irq_enable,
    .proc = ds3234_proc,  /* 可选的proc文件系统接口 */
};

2.10 探测函数(probe)

static int ds3234_probe(struct spi_device *spi)
{
    struct ds3234 *ds3234;
    struct rtc_device *rtc;
    u8 buf[2];
    int err;
    
    /* 分配设备私有数据结构 */
    ds3234 = devm_kzalloc(&spi->dev, sizeof(*ds3234), GFP_KERNEL);
    if (!ds3234)
        return -ENOMEM;
    
    /* 初始化数据结构 */
    ds3234->spi = spi;
    spi_set_drvdata(spi, ds3234);
    
    /* 初始化缓冲区 */
    ds3234->tx_buf = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
    ds3234->rx_buf = devm_kzalloc(&spi->dev, 8, GFP_KERNEL);
    if (!ds3234->tx_buf || !ds3234->rx_buf)
        return -ENOMEM;
    
    /* 验证设备ID */
    err = ds3234_read_reg(ds3234, DS3234_REG_SECONDS, buf);
    if (err) {
        dev_err(&spi->dev, "unable to read RTC register\n");
        return err;
    }
    
    /* 检查振荡器是否启用 */
    err = ds3234_read_reg(ds3234, DS3234_REG_CONTROL, buf);
    if (err)
        return err;
    
    if (buf[0] & DS3234_CTRL_EOSC) {
        /* 振荡器被禁用,启用它 */
        buf[0] &= ~DS3234_CTRL_EOSC;
        err = ds3234_write_reg(ds3234, DS3234_REG_CONTROL, buf[0]);
        if (err)
            return err;
    }
    
    /* 清除振荡器停止标志 */
    err = ds3234_read_reg(ds3234, DS3234_REG_STATUS, buf);
    if (err)
        return err;
    
    buf[0] &= ~DS3234_STAT_OSF;
    err = ds3234_write_reg(ds3234, DS3234_REG_STATUS, buf[0]);
    if (err)
        return err;
    
    /* 注册RTC设备 */
    rtc = devm_rtc_device_register(&spi->dev, "ds3234",
                                   &ds3234_rtc_ops, THIS_MODULE);
    if (IS_ERR(rtc))
        return PTR_ERR(rtc);
    
    ds3234->rtc = rtc;
    
    /* 设置SPI模式 */
    spi->mode = SPI_MODE_3;  /* CPOL=1, CPHA=1 */
    spi->bits_per_word = 8;
    err = spi_setup(spi);
    if (err)
        return err;
    
    /* 初始化温度监控工作队列 */
    INIT_DELAYED_WORK(&ds3234->work, ds3234_temp_work);
    schedule_delayed_work(&ds3234->work, 0);
    
    /* 如果支持中断,初始化中断 */
    if (spi->irq > 0) {
        err = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
                                       ds3234_irq,
                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
                                       "ds3234", ds3234);
        if (err) {
            dev_warn(&spi->dev, "unable to request IRQ\n");
        }
    }
    
    dev_info(&spi->dev, "DS3234 SPI RTC registered\n");
    
    return 0;
}

2.11 移除函数(remove)

static int ds3234_remove(struct spi_device *spi)
{
    struct ds3234 *ds3234 = spi_get_drvdata(spi);
    
    /* 取消温度监控工作队列 */
    cancel_delayed_work_sync(&ds3234->work);
    
    /* 禁用闹钟中断 */
    ds3234_write_reg(ds3234, DS3234_REG_CONTROL, 0);
    
    return 0;
}

2.12 设备ID表

static const struct spi_device_id ds3234_id[] = {
    { "ds3234", 0 },
    { }
};
MODULE_DEVICE_TABLE(spi, ds3234_id);

2.13 设备树兼容性

#ifdef CONFIG_OF
static const struct of_device_id ds3234_dt_ids[] = {
    { .compatible = "maxim,ds3234" },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, ds3234_dt_ids);
#endif

2.14 SPI驱动结构

static struct spi_driver ds3234_driver = {
    .driver = {
        .name   = "rtc-ds3234",
        .of_match_table = of_match_ptr(ds3234_dt_ids),
    },
    .probe  = ds3234_probe,
    .remove = ds3234_remove,
    .id_table = ds3234_id,
};
module_spi_driver(ds3234_driver);

3. 设备树绑定示例

/* DS3234设备树节点示例 */
&spi0 {
    status = "okay";
    
    rtc@0 {
        compatible = "maxim,ds3234";
        reg = <0>;                     /* CS0 */
        spi-max-frequency = <5000000>; /* 最大SPI频率5MHz */
        spi-cpol;                      /* 时钟极性1 */
        spi-cpha;                      /* 时钟相位1 */
        
        /* 中断引脚(如果连接) */
        interrupts = <&gpio 17 IRQ_TYPE_EDGE_FALLING>;
        interrupt-parent = <&gpio>;
    };
};

4. 驱动使用示例

4.1 加载驱动模块

# 加载SPI核心模块
modprobe spi_bcm2835  # Raspberry Pi SPI控制器

# 加载DS3234驱动
insmod rtc-ds3234.ko

# 查看注册的RTC设备
ls /sys/class/rtc/
cat /proc/driver/rtc

4.2 用户空间访问

# 设置系统时间到RTC
hwclock -w

# 从RTC读取时间到系统
hwclock -s

# 查看RTC时间
hwclock -r

# 通过sysfs查看信息
cat /sys/class/rtc/rtc0/date
cat /sys/class/rtc/rtc0/time
cat /sys/class/rtc/rtc0/since_epoch

5. 驱动关键设计分析

5.1 SPI通信设计

  • 使用Linux SPI子系统API

  • 支持SPI模式3(CPOL=1, CPHA=1)

  • 实现寄存器读写函数

  • 支持批量读写操作

5.2 时间处理

  • BCD格式和二进制格式转换

  • 12/24小时制处理

  • 世纪位处理

  • 振荡器状态监控

5.3 中断处理

  • 闹钟中断支持

  • 工作队列处理温度读取

  • 异步事件处理

5.4 电源管理

  • 振荡器使能/禁用控制

  • 电池备份支持

  • 低功耗模式

6. 与其他SPI RTC驱动的比较

特性

DS3234

DS1305

MCP7951X

温度传感器

精度

±2ppm

±2ppm

±2ppm

闹钟

2个

2个

2个

电池备份

支持

支持

支持

方波输出

支持

支持

支持

SPI模式

模式3

模式1,3

模式0,3

7. 总结

DS3234驱动的主要特点:

  1. 完整的SPI驱动实现:使用标准的SPI框架

  2. 高精度时间管理:支持温度补偿

  3. 完整的功能集:时间、闹钟、温度监控

  4. 中断支持:支持闹钟中断

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

  6. 电源管理:支持振荡器控制和低功耗模式

这个驱动展示了典型的SPI设备驱动开发模式:

  • 实现probe/remove函数

  • 定义设备私有数据结构

  • 实现设备特定操作(RTC操作集)

  • 处理中断和工作队列

  • 支持sysfs/proc接口

  • 完整的错误处理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值