IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> linux时钟子系统-框架 -> 正文阅读

[系统运维]linux时钟子系统-框架

前言

1.首先介绍下 linux初始化时,时钟的初始化的脉络。
2.介绍各个种类时钟的初始化流程。
3.此篇基于 全志的t7进行讲述:sun8iw17p1

linux启动时,时钟的初期化

在介绍之前,先要理清楚,machine_desc,即 设备描述符。

machine_desc的获取

这个描述符比较重要,里面有个init_time函数,被内核调用,所以想要搞清楚他的由来。
首先看下 连接脚本文件 。

cat linux-3.10/arch/arm/kernel/vmlinux.lds.S
.init.arch.info : {
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
}

即,连接脚本定义了个 .arch.info.init的段,软件通过 __arch_info_begin 来获取首地址。
接下来,平台驱动文件定义了machine_desc

#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\
 __used							\
 __attribute__((__section__(".arch.info.init"))) = {	\
	.nr		= ~0,				\
	.name		= _namestr,

DT_MA
CHINE_START(SUNXI_DT, CONFIG_SUNXI_SOC_NAME)
#ifdef CONFIG_SMP
	.smp_init	= smp_init_ops(sun8i_smp_init_ops),
#ifndef CONFIG_MCPM
	.smp		= smp_ops(sunxi_smp_ops),
#endif
#endif
	.map_io		= sunxi_map_io,
	.init_time	= sunxi_timer_init,
	.dt_compat	= sunxi_board_dt_compat,
MACHINE_END

即,在 “.arch.info.init” section中定义了,__mach_desc_SUNXI_DT 结构体。

系统的初始化

start_kernel
	setup_arch
		//setup_machine_fdt 会将 __arch_info_begin 的section取出来,
		//即取出 __mach_desc_SUNXI_DT 结构体的首地址
		mdesc = setup_machine_fdt 
	time_init
		machine_desc->init_time();

正式进入 init_time

static void __init sunxi_timer_init(void)
{
	of_clk_init(NULL); //我们主要看这个函数
#ifdef CONFIG_COMMON_CLK_ENABLE_SYNCBOOT_EARLY
	clk_syncboot();
#endif
	clocksource_of_init();
}

void __init of_clk_init(const struct of_device_id *matches)
{
	struct device_node *np;

	if (!matches)
		//获取 __clk_of_table的首地址
		matches = __clk_of_table; 
	pr_debug("[%s][%d]match-name:%s\n", __func__, __LINE__, matches->name);
	//__clk_of_table定义了一堆结构体,取出每个结构体的名字,匹配名字与 device的名字是否一致。
	for_each_matching_node(np, matches) {
		const struct of_device_id *match = of_match_node(matches, np);
		//一致的话,调用结构体中的 clk_init_cb 函数。
		of_clk_init_cb_t clk_init_cb = match->data;
		pr_debug("[%s][%d]match-name:%s\n", __func__, __LINE__, matches->name);
		clk_init_cb(np);
	}
}
//接下来,我们在看一看 __clk_of_table中的结构体定义。
#define CLK_OF_DECLARE(name, compat, fn)			\
	static const struct of_device_id __clk_of_table_##name	\
		__used __section(__clk_of_table)		\
		= { .compatible = compat, .data = fn };

//在这里,我们重点关注下
// of_sunxi_clocks_init
// of_sunxi_fixed_clk_setup
// of_sunxi_pll_clk_setup
// of_sunxi_periph_clk_setup
// 即三个函数的结构体顺次放在了  __clk_of_table_ 这个段中。

CLK_OF_DECLARE(sunxi_clocks_init, "allwinner,sunxi-clk-init",
		 of_sunxi_clocks_init);
CLK_OF_DECLARE(sunxi_fixed_clk, "allwinner,fixed-clock",
		 of_sunxi_fixed_clk_setup);
CLK_OF_DECLARE(pll_clk, "allwinner,sunxi-pll-clock",
		 of_sunxi_pll_clk_setup);
CLK_OF_DECLARE(sunxi_fixed_factor_clk, "allwinner,fixed-factor-clock",
		 of_sunxi_fixed_factor_clk_setup);
CLK_OF_DECLARE(periph_clk, "allwinner,sunxi-periph-clock",
		 of_sunxi_periph_clk_setup);
CLK_OF_DECLARE(periph_cpus_clk, "allwinner,sunxi-periph-cpus-clock",
		 of_sunxi_periph_cpus_clk_setup);

所以从上面来看 init_time执行后,按照以下的顺序执行

of_sunxi_clocks_init
of_sunxi_fixed_clk_setup
of_sunxi_pll_clk_setup
of_sunxi_periph_clk_setup

之后我们开始一个一个分析这几个函数如何构筑了庞大的时钟帝国。

of_sunxi_clocks_init

通过定义:

CLK_OF_DECLARE(sunxi_clocks_init, "allwinner,sunxi-clk-init",
		 of_sunxi_clocks_init);

我们可以知道,此函数的设备树参数,对应下面的设备树

	clocks {
		compatible = "allwinner,sunxi-clk-init";
		device_type = "clocks";
		#address-cells = <0x2>;
		#size-cells = <0x2>;
		ranges;
		reg = <0x0 0x3001000 0x0 0x1000 0x0 0x7010000 0x0 0x400 0x0 0x7000060 0x0 0x4>;

		losc {
			#clock-cells = <0x0>;
			compatible = "allwinner,fixed-clock";
			clock-frequency = <0x8000>;
			clock-output-names = "losc";
			linux,phandle = <0xf>;
			phandle = <0xf>;
		};

我们看下函数内容:

void of_sunxi_clocks_init(struct device_node *node)
{
	//reg = <0x0 0x3001000 0x0 0x1000 >
	//获取了 sunxi_clk_base, 即时钟寄存器的基地址。
	sunxi_clk_base = of_iomap(node, 0);
	sunxi_clk_cpus_base = of_iomap(node, 1);
	sunxi_clk_periph_losc_out.gate.bus = of_iomap(node, 2);
	/*do some initialize arguments here*/
	//这里设定了时钟能设置的最大限制,即限制用户设定时钟的最大值
	sunxi_clk_factor_initlimits();

	//获取提供给 pll 时钟的底层操作函数, 并拷贝一份给 pll_mipi_ops
	sunxi_clk_get_factors_ops(&pll_mipi_ops); 

	//设置了 pll_mipi_ops的parent
	//我们知道 pll相关的时钟不需要设定 parent(因为parent就是 hosc),pll_mipi
	//比较特殊,他的parent 是 pll_video0(1x),所以需要提供相应的函数
	//所以最后 pll_mipi的操作比 别的pll时钟的操作,多了个 get_parent, set_parent接口。
	pll_mipi_ops.get_parent = get_parent_pll_mipi;
	pll_mipi_ops.set_parent = set_parent_pll_mipi;
	pll_mipi_ops.enable = clk_enable_pll_mipi;
	pll_mipi_ops.disable = clk_disable_pll_mipi;
	sunxi_set_clk_priv_ops("mipi_rx", set_mipi_rx_priv_ops);
}

//这个结构体是提供给 pll 时钟的底层操作函数
static const struct clk_ops clk_factors_ops = {
	.enable = sunxi_clk_fators_enable,
	.disable = sunxi_clk_fators_disable,
	.is_enabled = sunxi_clk_fators_is_enabled,
	.recalc_rate = sunxi_clk_factors_recalc_rate,
	.round_rate = sunxi_clk_factors_round_rate,
	.set_rate = sunxi_clk_factors_set_rate,
};
void sunxi_clk_get_factors_ops(struct clk_ops* ops)
{
	memcpy(ops,&clk_factors_ops,sizeof(clk_factors_ops));
}
sunxi_clk_get_factors_ops(&pll_mipi_ops);

of_sunxi_fixed_clk_setup

通过定义:

CLK_OF_DECLARE(sunxi_fixed_clk, "allwinner,fixed-clock",
		 of_sunxi_fixed_clk_setup);

我们知道匹配的设备树是:

		hosc {
			#clock-cells = <0x0>;
			compatible = "allwinner,fixed-clock";
			clock-frequency = <0x16e3600>; //24Mhz
			clock-output-names = "hosc";
			linux,phandle = <0x8>;
			phandle = <0x8>;
		};

		osc48m {
			#clock-cells = <0x0>;
			compatible = "allwinner,fixed-clock";
			clock-frequency = <0x2dc6c00>; //48Mhz
			clock-output-names = "osc48m";
			linux,phandle = <0x7>;
			phandle = <0x7>;
		};

这里 osc48m 和 hosc 都是晶振,都属于 clk root。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-01-29 23:28:17  更:2022-01-29 23:30:37 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 7:23:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码