You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Linux内核5.10.24下PCF8563 RTC CLKOUT配置问题求助

问题详情

我正在开发基于Linux内核5.10.24的嵌入式系统,系统搭载PCF8563 RTC,其CLKOUT引脚连接至其他硬件逻辑。我需配置PCF8563的CLKOUT输出速率,但未找到对应用户态API。经搜索得知可通过内核模块调用clk_get()和clk_set_rate()实现,遂编写如下内核模块:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

static struct clk *rtc_clk;

static int __init rtc_module_init(void)
{
    int ret;

    rtc_clk = clk_get(NULL, "pcf8563");   // Get the RTC of the name
    if (IS_ERR(rtc_clk)) {
        ret = PTR_ERR(rtc_clk);
        printk(KERN_ERR "Failed to get RTC clock: %d\n", ret);
        return ret;
    }

    unsigned long rate = clk_get_rate(rtc_clk);
    printk(KERN_INFO "Current RTC clock rate: %lu Hz\n", rate);

    unsigned long new_rate = 32768;
    ret = clk_set_rate(rtc_clk, new_rate);
    if (ret < 0) {
        printk(KERN_ERR "Failed to set RTC clock rate: %d\n", ret);
        return ret;
    }

    rate = clk_get_rate(rtc_clk);
    printk(KERN_INFO "Updated RTC clock rate: %lu Hz\n", rate);

    return 0;
}

static void __exit rtc_module_exit(void)
{
    if (rtc_clk)
        clk_put(rtc_clk);
}

module_init(rtc_module_init);
module_exit(rtc_module_exit);

MODULE_LICENSE("GPL");

加载模块时出现错误:Failed to get RTC clock: -2

系统中RTC的设备树配置如下:

rtc_pcf8563:rtc_pcf8563@51{
            compatible = "nxp,pcf8563";
            reg = <0x51>;
            status = "okay";
}

同时查看PCF8563驱动代码发现如下定义:

static const struct clk_ops pcf8563_clkout_ops = {
    .prepare = pcf8563_clkout_prepare,
    .unprepare = pcf8563_clkout_unprepare,
    .is_prepared = pcf8563_clkout_is_prepared,
    .recalc_rate = pcf8563_clkout_recalc_rate,
    .round_rate = pcf8563_clkout_round_rate,
    .set_rate = pcf8563_clkout_set_rate,
};

static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563)
{
    struct i2c_client *client = pcf8563->client;
    struct device_node *node = client->dev.of_node;
    struct clk *clk;
    struct clk_init_data init;
    int ret;
    unsigned char buf;

    /* disable the clkout output */
    buf = 0;
    ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf);
    if (ret < 0)
        return ERR_PTR(ret);

    init.name = "pcf8563-clkout";
    init.ops = &pcf8563_clkout_ops;
    init.flags = 0;
    init.parent_names = NULL;
    init.num_parents = 0;
    pcf8563->clkout_hw.init = &init;

    /* optional override of the clockname */
    of_property_read_string(node, "clock-output-names", &init.name);

    /* register the clock */
    clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw);

    if (!IS_ERR(clk))
        of_clk_add_provider(node, of_clk_src_simple_get, clk);

    return clk;
}

我想知道:

  1. 调用clk_get(NULL, ???)时应传入什么名称?
  2. 这些驱动中的函数在Linux中如何调用?
  3. 系统启用PCF8563的CLKOUT应如何操作?
  4. 是否存在调用这些函数的用户态接口?

解答

1. clk_get的正确名称

从驱动代码可以看到:

  • 默认注册的时钟名称是pcf8563-clkout
  • 如果设备树中添加clock-output-names属性,会覆盖默认名称

你有两种选择:

  • 直接使用默认名称修改模块中的clk_get调用:
    rtc_clk = clk_get(NULL, "pcf8563-clkout");
    
  • 或者在设备树中自定义名称,比如:
    rtc_pcf8563:rtc_pcf8563@51{
        compatible = "nxp,pcf8563";
        reg = <0x51>;
        status = "okay";
        clock-output-names = "my-pcf8563-clk";
    }
    
    此时clk_get传入"my-pcf8563-clk"即可。

2. 驱动中函数的调用方式

你看到的pcf8563_clkout_register_clk和pcf8563_clkout_ops下的函数都是内核驱动内部接口,不需要手动调用:

  • pcf8563_clkout_register_clk会在PCF8563驱动的probe函数中自动执行,设备树配置正确、驱动加载成功后,就会完成CLKOUT时钟的注册
  • pcf8563_clkout_ops中的函数是时钟框架的回调,当你调用clk_set_rate、clk_prepare等上层时钟API时,内核会自动调用这些底层实现函数

3. 启用PCF8563的CLKOUT操作

现有模块仅设置了速率,未启用时钟输出,完整步骤如下:

  1. 修正clk_get的名称,成功获取时钟句柄
  2. 设置目标速率(调用clk_set_rate)
  3. 启用时钟输出:调用clk_prepare_enable(触发驱动中的pcf8563_clkout_prepare,完成CLKOUT引脚的启用配置)

修改后的模块初始化函数示例:

static int __init rtc_module_init(void)
{
    int ret;

    rtc_clk = clk_get(NULL, "pcf8563-clkout");
    if (IS_ERR(rtc_clk)) {
        ret = PTR_ERR(rtc_clk);
        printk(KERN_ERR "Failed to get RTC clock: %d\n", ret);
        return ret;
    }

    unsigned long rate = clk_get_rate(rtc_clk);
    printk(KERN_INFO "Current RTC clock rate: %lu Hz\n", rate);

    unsigned long new_rate = 32768;
    ret = clk_set_rate(rtc_clk, new_rate);
    if (ret < 0) {
        printk(KERN_ERR "Failed to set RTC clock rate: %d\n", ret);
        clk_put(rtc_clk);
        return ret;
    }

    // 启用CLKOUT输出
    ret = clk_prepare_enable(rtc_clk);
    if (ret < 0) {
        printk(KERN_ERR "Failed to enable RTC clock: %d\n", ret);
        clk_put(rtc_clk);
        return ret;
    }

    rate = clk_get_rate(rtc_clk);
    printk(KERN_INFO "Enabled RTC clock rate: %lu Hz\n", rate);

    return 0;
}

static void __exit rtc_module_exit(void)
{
    if (rtc_clk) {
        clk_disable_unprepare(rtc_clk);
        clk_put(rtc_clk);
    }
}

注意:PCF8563的CLKOUT仅支持固定几种速率(32768Hz、1024Hz、32Hz、1Hz),clk_set_rate会自动匹配最接近的支持速率,可通过clk_round_rate先确认实际能设置的速率。

4. 用户态接口情况

Linux内核没有提供直接控制PCF8563 CLKOUT的标准用户态API,但可通过两种方式实现用户态控制:

  • 利用sysfs时钟接口:内核时钟框架会在/sys/class/clk/下生成对应时钟的节点,比如/sys/class/clk/pcf8563-clkout/,可通过读写其中的rate、enable文件来设置速率和启用/禁用时钟(需内核开启CONFIG_SYSFS和CONFIG_COMMON_CLK)
  • 自定义内核模块提供用户态接口:在你的内核模块中添加sysfs节点或字符设备,实现用户态到内核时钟API的转发,比如通过读写sysfs文件触发clk_set_rate和clk_prepare_enable操作

内容的提问来源于stack exchange,提问作者wangt13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:53:13