Linux内核---47.关于clk_get与clk_enable

本文详细解析了ARM架构下时钟初始化的过程,包括将各种时钟源添加到clocks链表,并介绍了时钟使能的具体实现,如通过寄存器控制使能或禁用时钟。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一. clock初始化过程
在arch/arm/mach-s3c64xx/s3c6410.c中
  1. void __init s3c6410_init_clocks(int xtal)
  2. {
  3.     s3c64xx_register_clocks(xtal, S3C6410_CLKDIV0_ARM_MASK);  //1.将clk_src添加到clocks链表中
  4.     s3c6400_setup_clocks();
  5. }
1. 把所有的clk_src添加到clocks链表中
在arch/arm/mach-s3c64xx/clock.c中
  1. void __init s3c64xx_register_clocks(unsigned long xtal, unsigned armclk_divlimit)
  2. {
  3.     armclk_mask = armclk_divlimit;
  4.     //把xtal mpll upll clk_f clk_h clk_p添加到clocks链表中
  5.     s3c24xx_register_baseclocks(xtal);
  6.     //把 ext, epll, 27m, 48m, h2, xusbxti,添加到clocks链表中
  7.     s3c24xx_register_clocks(clks, ARRAY_SIZE(clks));
  8.     //把init_clocks区的clk添加到clokcs链表中
  9.     s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));    
  10.     //放在init_clocks_disable区内的clk,先添加进链表然后调用enable(0),禁止clk生效
  11.     clkp = init_clocks_disable;
  12.     for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
  13.         ret = s3c24xx_register_clock(clkp);
  14.         (clkp->enable)(clkp, 0);
  15.     }
  16.     //把cks1区的clk添加到clokcs链表中
  17.     s3c24xx_register_clocks(clks1, ARRAY_SIZE(clks1));
  18.     //把clksrcs区的clk添加到clokcs链表中
  19.     s3c_register_clksrc(clksrcs, ARRAY_SIZE(clksrcs));
  20.     s3c_pwmclk_init();
  21. }
2.把baseclock中的clk添加进链表中
在arch/arm/plat-samsung/clock.c中
  1. int __init s3c24xx_register_baseclocks(unsigned long xtal)
  2. {
  3.     clk_xtal.rate = xtal;
  4.     s3c24xx_register_clock(&clk_xtal) 
  5.     s3c24xx_register_clock(&clk_mpll) 
  6.     s3c24xx_register_clock(&clk_upll) 
  7.     s3c24xx_register_clock(&clk_f) 
  8.     s3c24xx_register_clock(&clk_h) 
  9.     s3c24xx_register_clock(&clk_p) 
  10. }

3.将clk结构体添加到clocks链表中
在arch/arm/plat-samsung/clock.c中
  1. int s3c24xx_register_clock(struct clk *clk)
  2. {
  3.     if (clk->enable == NULL)
  4.         clk->enable = clk_null_enable;

  5.     spin_lock(&clocks_lock);
  6.     list_add(&clk->list, &clocks);     //把参数中的clk结构体添加到clocks链表中
  7.     spin_unlock(&clocks_lock);
  8.     return 0;
  9. }
CLOCK的类型:
a. init_clocks
  1. static struct clk init_clocks[] = {
  2.         .name        = "lcd",
  3.         .name        = "gpio",
  4.         .name        = "usb-host",
  5.         .name        = "hsmmc",
  6.         .name        = "hsmmc",
  7.         .name        = "hsmmc",
  8.         .name        = "otg",
  9.         .name        = "timers",
  10.         .name        = "uart",
  11.         .name        = "uart",
  12.         .name        = "uart",
  13.         .name        = "uart",
  14.         .name        = "watchdog",
  15.         .name        = "ac97",
  16.         .name        = "cfcon",
  17.         .name        = "fimc",
  18.         .name        = "hclk_mfc",
  19.         .name        = "sclk_mfc",
  20.         .name        = "pclk_mfc",
  21.         .name        = "hclk_jpeg",
  22.         .name        = "sclk_jpeg",
  23.         .name        = "sclk_cam",
  24. };
b. init_clocks_disable
  1. static struct clk init_clocks_disable[] = {
  2.         .name        = "nand",
  3.         .name        = "rtc",
  4.         .name        = "adc",
  5.         .name        = "i2c",
  6.         .name        = "iis",
  7.         .name        = "iis",
  8.         .name        = "iis",
  9.         .name        = "keypad",
  10.         .name        = "spi",
  11.         .name        = "spi",
  12.         .name        = "spi_48m",
  13.         .name        = "spi_48m",
  14.         .name        = "48m",
  15.         .name        = "48m",
  16.         .name        = "48m",
  17.         .name        = "dma0",
  18.         .name        = "dma1",
  19. };
c. clks1与clks
  1. static struct clk *clks1[] __initdata = {
  2.     &clk_ext_xtal_mux,
  3.     &clk_iis_cd0,
  4.     &clk_iis_cd1,
  5.     &clk_iisv4_cd,
  6.     &clk_pcm_cd,
  7.     &clk_mout_epll.clk,
  8.     &clk_mout_mpll.clk,
  9.     &clk_dout_mpll,
  10.     &clk_arm,
  11. };

  12. static struct clk *clks[] __initdata = {
  13.     &clk_ext,
  14.     &clk_epll,
  15.     &clk_27m,
  16.     &clk_48m,
  17.     &clk_h2,
  18.     &clk_xusbxti,
  19. };
二. clock enable过程
在drivers/video/s3c-fb.c中
  1. static int __devinit s3c_fb_probe(struct platform_device *pdev)
  2. {
  3.     sfb->bus_clk = clk_get(dev, "lcd");    //1.从clocks链表中查找lcd的clk结构体
  4.     clk_enable(sfb->bus_clk);              //2.调用clk的enableb函数
  5. }

1. 从clocks链表中查找lcd的clk结构体
在arch/arm/plat-samsung/clock.c中
  1. struct clk *clk_get(struct device *dev, const char *id)
  2. {
  3.     spin_lock(&clocks_lock);
  4.     //从clocks中查找id与name都与参数相同的clk结构体
  5.     list_for_each_entry(p, &clocks, list) {     
  6.         if (p->id == idno && strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
  7.             clk = p;
  8.             break;
  9.         }
  10.     }
  11.     spin_unlock(&clocks_lock);
  12.     return clk;
  13. }

2. 具体的enable过程
在arch/arm/plat-samsung/clock.c中
  1. int clk_enable(struct clk *clk)
  2. {
  3.     clk_enable(clk->parent);   //先调用parent的enable

  4.     spin_lock(&clocks_lock);

  5.     if ((clk->usage++) == 0)
  6.         (clk->enable)(clk, 1);  //2.1 再调用具体的enable

  7.     spin_unlock(&clocks_lock);
  8.     return 0;
  9. }
2.1 以lcd为例为调用s3c64xx_hclk_ctrl
在arch/arm/plat-samsung/clock.c中
  1. static struct clk init_clocks[] = {
  2. {
  3.     .name        = "lcd",
  4.     .id        = -1,
  5.     .parent        = &clk_h,
  6.     .enable        = s3c64xx_hclk_ctrl,
  7.     .ctrlbit    = S3C_CLKCON_HCLK_LCD,   //S3C_CLKCON_HCLK_LCD    (1<<3)
  8. }
  9. }
在arch/arm/plat-samsung/clock.c中
  1. static int s3c64xx_hclk_ctrl(struct clk *clk, int enable)
  2. {
  3.     return s3c64xx_gate(S3C_HCLK_GATE, clk, enable);
  4. }
#define S3C_HCLK_GATE        S3C_CLKREG(0x30)
#define S3C_CLKREG(x)        (S3C_VA_SYS + (x))
HCLK_GATE 0x7E00_F030
2.2 修改gate寄存器,使能或禁止clock
在arch/arm/plat-samsung/clock.c中
  1. static int inline s3c64xx_gate(void __iomem *regstruct clk *clk, int enable)
  2. {
  3.     unsigned int ctrlbit = clk->ctrlbit;
  4.     u32 con;

  5.     con = __raw_readl(reg);  //读取寄存器的值

  6.     if (enable)             //如果是enable,将这位清0 
  7.         con |= ctrlbit;     
  8.     else                    //如果是disable,将这位清0
  9.         con &= ~ctrlbit;    

  10.     __raw_writel(con, reg);  //修改后,写入到寄存器中
  11.     return 0;
  12. }
附录:
1. 获取clk的值
    clk_get_rate(struct clk);

<think>好的,用户遇到了SDHCI_MSM模块中的TCXO时钟缺失和总线带宽数据获取失败的问题。我需要结合提供的引用信息和Linux驱动开发知识来分析解决方案。首先,TCXO(温度补偿晶体振荡器)时钟是SDHCI控制器的重要时钟源,如果系统检测不到这个时钟,可能导致驱动初始化失败。根据引用[3]和[4],SDHCI_MSM驱动在probe函数中会进行资源初始化,包括时钟获取。用户需要检查设备树(DTS)是否正确配置了TCXO时钟,比如时钟名称、频率以及是否被其他模块占用。此外,驱动代码中是否调用了clk_get函数获取该时钟,并处理了可能的错误情况。其次,总线带宽数据(bus_scale)获取失败可能QoS或带宽配置相关。引用[4]提到bus_scale数据通常通过DTS配置,需确认设备树节点是否包含正确的总线带宽参数,比如qcom,msm-bus向量。同时,驱动代码中是否有正确解析这些数据的部分,比如使用of_property_read函数读取相关属性。另外,需要检查内核日志,查看是否有更详细的错误信息,比如时钟请求失败的原因或总线配置的具体问题。还有,确认内核版本和补丁,有时类似问题可能已通过更新驱动或内核修复。最后,建议用户逐步排查:首先验证设备树配置,然后检查驱动代码中的资源获取部分,确保错误处理机制完善,必要时添加调试打印以定位问题点。</think>### 问题分析及解决方案:SDHCI_MSM模块中TCXO时钟缺失总线带宽数据获取失败 #### 一、TCXO时钟缺失问题("TCXO clk not present") 1. **设备树(DTS)配置验证** - 确认设备树中是否正确定义了TCXO时钟源,例如: ```dts &sdhc_1 { vmmc-supply = <&pm8953_l8>; clock-names = "iface", "core", "xo"; // 关键字段 clocks = <&gcc GCC_SDCC1_AHB_CLK>, <&gcc GCC_SDCC1_APPS_CLK>, <&rpmcc RPM_SMD_XO_CLK_SRC>; // 需匹配硬件时钟源 }; ``` - 检查`clock-names`是否包含`"xo"`或`"tcxo"`,并驱动代码中的时钟请求名称一致[^3][^4]。 2. **驱动代码时钟获取逻辑** - 在`sdhci_msm_probe`函数中,查找类似以下代码段: ```c host->clk = devm_clk_get(dev, "core"); host->iface = devm_clk_get(dev, "iface"); host->xo_clk = devm_clk_get(dev, "xo"); // 关键时钟获取 ``` - 若时钟名称不匹配或未启用,需调整`devm_clk_get`参数或DTS配置[^3][^5]。 3. **时钟依赖性电源管理** - 使用`clk_prepare_enable(host->xo_clk)`确保时钟已启用。 - 通过`dmesg | grep -i tcxo`查看内核日志,确认是否因电源域未激活导致时钟失效。 #### 二、总线带宽数据获取失败("bus_scale data failed to get") 1. **总线带宽设备树配置** - 检查DTS中是否包含`msm-bus`向量定义,例如: ```dts qcom,msm-bus,name = "sdhc1"; qcom,msm-bus,num-cases = <9>; qcom,msm-bus,num-paths = <1>; qcom,msm-bus,vectors-KBps = <...>; ``` - 确认`qcom,bus-width`属性是否存在且值正确(如`<8>`)[^4]。 2. **驱动中的总线初始化逻辑** - 在`sdhci_msm_probe`中,查找总线客户端初始化代码: ```c host->bus_client = msm_bus_scale_register_client(&sdhci_msm_bus_scale_data); if (!host->bus_client) dev_err(dev, "Bus scale register failed\n"); ``` - 确认`sdhci_msm_bus_scale_data`结构体是否DTS数据匹配[^4]。 3. **内核QoS子系统兼容性** -内核版本≥5.0,需检查是否因`msm_bus` API变更导致兼容性问题(如改用`icc`接口)。 - 更新驱动或回退相关补丁以验证问题来源。 #### 三、联合调试步骤 1. **启用详细调试日志** 在驱动代码中添加调试打印: ```c dev_info(dev, "TCXO clock status: %d\n", clk_get_rate(host->xo_clk)); dev_info(dev, "Bus scale client ID: %d\n", host->bus_client); ``` 2. **硬件寄存器检查** 使用`devmem`工具直接读取SDHCI寄存器状态: ```bash devmem 0x01C00000 32 # 假设SDHCI基地址为0x01C00000 ``` 确认`SDHCI_CLOCK_CONTROL`寄存器(偏移量0x2C)的时钟使能位是否置位[^1]。 3. **参考已知补丁** 搜索Linux内核邮件列表(如[LKML](https://lore.kernel.org/))中类似问题的修复记录,例如: - *"sdhci-msm: Fix TCXO clock registration for certain SoCs"* - *"msm: bus: Fix vector allocation race condition"*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值