arch_timer驱动分析

arch_timer的初始化

static void __init arch_timer_common_init(void)
{

	arch_timer_banner(arch_timers_present);//打印计时器的信息
	arch_counter_register(arch_timers_present);//注册计数器
	arch_timer_arch_init();
}

static int __init arch_timer_register(void)
{
	arch_timer_evt = alloc_percpu(struct clock_event_device);
	ppi = arch_timer_ppi[PHYS_SECURE_PPI];
	err = request_percpu_irq(ppi, arch_timer_handler_phys, "arch_timer", arch_timer_evt);
	ppi = arch_timer_ppi[PHYS_NONSECURE_PPI];
	err = request_percpu_irq(ppi, arch_timer_handler_phys, "arch_timer", arch_timer_evt);
}//虽然申请了两个中断,但只看到一个arch_timer发生了中断

static void __init arch_timer_init(void)
{
	arch_timer_register();
	arch_timer_common_init();
}

static void __init arch_timer_of_init(struct device_node *np)
{
	arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
static struct clocksource clocksource_counter = {
	.name	= "arch_sys_counter",
	.rating	= 400,
	.read	= arch_counter_read,
	.mask	= CLOCKSOURCE_MASK(56),
	.flags	= CLOCK_SOURCE_IS_CONTINUOUS,
};

static struct cyclecounter cyclecounter = {
	.read	= arch_counter_read_cc,
	.mask	= CLOCKSOURCE_MASK(56),
};

static void __init arch_counter_register(unsigned type)
{
	u64 start_count;

	/* Register the CP15 based counter if we have one */
	if (type & ARCH_CP15_TIMER) {
		if (IS_ENABLED(CONFIG_ARM64) || arch_timer_use_virtual)
			arch_timer_read_counter = arch_counter_get_cntvct;
		else
			arch_timer_read_counter = arch_counter_get_cntpct;
	} 

	start_count = arch_timer_read_counter();
	clocksource_register_hz(&clocksource_counter, arch_timer_rate);
	cyclecounter.mult = clocksource_counter.mult;
	cyclecounter.shift = clocksource_counter.shift;
	timecounter_init(&timecounter, &cyclecounter, start_count);

	/* 56 bits minimum, so we assume worst case rollover */
	sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate);
}

看下中断处理函数

static __always_inline irqreturn_t timer_handler(const int access,
					struct clock_event_device *evt)
{
		evt->event_handler(evt);
}

static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
{
	struct clock_event_device *evt = dev_id;
	return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
}

event_handler可以通过cat /proc/timer_list查看,如

Tick Device: mode:     1
Per CPU device: 7
Clock Event Device: arch_sys_timer
 max_delta_ns:   82595524660
 min_delta_ns:   1000
 mult:           111669150
 shift:          32
 mode:           1
 next_event:     6436132000000 nsecs
 set_next_event: arch_timer_set_next_event_phys
 shutdown: arch_timer_shutdown_phys
 event_handler:  hrtimer_interrupt
 retries:        1

然后可以通过在hrtimer_interrupt函数中加入WARN(1, "xxxxxx.\n");打印相应的调用关系。

重点关注下hrtimer_interrupt函数

void hrtimer_interrupt(struct clock_event_device *dev)
{
	struct hrtimer_cpu_base *cpu_base = this_cpu_ptr(&hrtimer_bases);
	entry_time = now = hrtimer_update_base(cpu_base);
	__hrtimer_run_queues(cpu_base, now);//找出到期的定时器执行其功能
	expires_next = __hrtimer_get_next_event(cpu_base);//找出下次的超时时间
	tick_program_event(expires_next, 0);//将定时时间设置到机器的计数模块
}
static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now)
{
	struct hrtimer_clock_base *base = cpu_base->clock_base;
	unsigned int active = cpu_base->active_bases;

	for (; active; base++, active >>= 1) {
		struct timerqueue_node *node;
		ktime_t basenow;

		if (!(active & 0x01))
			continue;

		basenow = ktime_add(now, base->offset);

		while ((node = timerqueue_getnext(&base->active))) {
			struct hrtimer *timer;
			timer = container_of(node, struct hrtimer, node);
			if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer))
				break;

			__run_hrtimer(cpu_base, base, timer, &basenow);
		}
	}
}

static void __run_hrtimer(struct hrtimer_cpu_base *cpu_base,
			  struct hrtimer_clock_base *base,
			  struct hrtimer *timer, ktime_t *now)
{
	enum hrtimer_restart (*fn)(struct hrtimer *);
	fn = timer->function;
	restart = fn(timer);
}

同样,查看/proc/timer_list可以查看在运行的每个cpu上不同类型的高精度定时器,如

/*
 * The timer bases:
 *
 * There are more clockids than hrtimer bases. Thus, we index
 * into the timer bases by the hrtimer_base_type enum. When trying
 * to reach a base using a clockid, hrtimer_clockid_to_base()
 * is used to convert from clockid to the proper hrtimer_base_type.
 */
DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) =
{
	.lock = __RAW_SPIN_LOCK_UNLOCKED(hrtimer_bases.lock),
	.seq = SEQCNT_ZERO(hrtimer_bases.seq),
	.clock_base =
	{
		{
			.index = HRTIMER_BASE_MONOTONIC,
			.clockid = CLOCK_MONOTONIC,
			.get_time = &ktime_get,
		},
		{
			.index = HRTIMER_BASE_REALTIME,
			.clockid = CLOCK_REALTIME,
			.get_time = &ktime_get_real,
		},
		{
			.index = HRTIMER_BASE_BOOTTIME,
			.clockid = CLOCK_BOOTTIME,
			.get_time = &ktime_get_boottime,
		},
		{
			.index = HRTIMER_BASE_TAI,
			.clockid = CLOCK_TAI,
			.get_time = &ktime_get_clocktai,
		},
	}
};

高精度的类型

enum  hrtimer_base_type {
    HRTIMER_BASE_MONOTONIC,
    HRTIMER_BASE_REALTIME,
    HRTIMER_BASE_BOOTTIME,
    HRTIMER_BASE_TAI,
    HRTIMER_MAX_CLOCK_BASES,
};

clock 0指HRTIMER_BASE_MONOTONIC

cpu 3指cpu3上的


//重点关注调度定时器

调度的周期 tick_period

union ktime {
	s64	tv64;
};
typedef union ktime ktime_t;		/* Kill this */
#define NSEC_PER_SEC	1000000000L
CONFIG_HZ=100
tick_period = ktime_set(0, NSEC_PER_SEC / HZ); 10ms
static inline ktime_t ktime_set(const s64 secs, const unsigned long nsecs)
{
	if (unlikely(secs >= KTIME_SEC_MAX))
		return (ktime_t){ .tv64 = KTIME_MAX };

	return (ktime_t) { .tv64 = secs * NSEC_PER_SEC + (s64)nsecs };
}
 
void tick_setup_sched_timer(void)
{
	struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);
	ktime_t now = ktime_get();

	
	hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
	ts->sched_timer.function = tick_sched_timer;

	hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
	hrtimer_forward(&ts->sched_timer, now, tick_period);
	hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
	tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
}

static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
{
	struct tick_sched *ts =
		container_of(timer, struct tick_sched, sched_timer);
	struct pt_regs *regs = get_irq_regs();
	ktime_t now = ktime_get();

	tick_sched_do_timer(now); //更新jiffies变量和墙上时间

	/*
	 * Do not call, when we are not in irq context and have
	 * no valid regs pointer
	 */
	if (regs)
		tick_sched_handle(ts, regs);//周期性调度进程

	/* No need to reprogram if we are in idle or full dynticks mode */
	if (unlikely(ts->tick_stopped))
		return HRTIMER_NORESTART;

	hrtimer_forward(timer, now, tick_period);

	return HRTIMER_RESTART;
}
static void tick_sched_do_timer(ktime_t now)
{
	tick_do_update_jiffies64(now);
}

static void tick_do_update_jiffies64(ktime_t now)
{
	do_timer(++ticks);
	update_wall_time();
}

void do_timer(unsigned long ticks)
{
	jiffies_64 += ticks;
}

static inline u64 tk_clock_read(struct tk_read_base *tkr)
{
	struct clocksource *clock = READ_ONCE(tkr->clock);

	return clock->read(clock);
}
void update_wall_time(void)
{
	offset = clocksource_delta(tk_clock_read(&tk->tkr_mono),tk->tkr_mono.cycle_last, tk->tkr_mono.mask);
}
static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
{
	update_process_times(user_mode(regs));
}

void update_process_times(int user_tick)
{
	run_local_timers();//找出超时的定时器运行
	scheduler_tick();
}

void run_local_timers(void)
{
	hrtimer_run_queues();//高精度定时器
	raise_softirq(TIMER_SOFTIRQ);//低精度定时器
}

void scheduler_tick(void)
{
	int cpu = smp_processor_id();
	curr->sched_class->task_tick(rq, curr, 0);//调度
	calc_global_load_tick(rq);

#ifdef CONFIG_SMP
	rq->idle_balance = idle_cpu(cpu);
	trigger_load_balance(rq);//触发负载平衡
#endif
}

 

<think>好的,我现在需要回答用户关于U-Boot中timer_init初始化位置的问题。首先,我需要回忆一下U-Boot的启动流程和相关代码结构。 U-Boot的初始化分为几个阶段,首先是arch级的初始化,然后是板级初始化。timer_init通常属于板级或CPU相关的初始化步骤,可能是在lowlevel_init或者更早的阶段被调用。不过具体位置可能因架构不同而有所变化。 接下来,我应该考虑不同架构的情况,比如ARM、PowerPC、RISC-V等。例如,在ARM架构中,U-Boot的启动流程会执行一系列的init_sequence_f数组中的函数,包括timer_init。但这里可能是指特定板级的timer_init,比如在board_init_f阶段调用的。 另外,我需要确认是否timer_init是在启动阶段的早期,比如在relocate_code之前还是之后。通常,基础外设的初始化会在relocation之前完成,但timer可能因为需要用于延时或看门狗而在较早阶段初始化。 可能还需要查看具体的板级支持包(BSP)代码,比如在arch/arm目录下的相关处理器代码,或者board/目录下的具体板级代码。例如,对于某些SoC,timer的初始化可能在lowlevel_init.S中,或者在spl阶段就已经完成。 另外,用户可能想知道如何找到timer_init的调用位置。可以建议通过代码搜索,在U-Boot源码中查找timer_init函数的调用链。例如,在common/board_f.c或common/board_r.c中的init_sequence数组里可能有相关条目。 还需要注意,某些平台可能将timer_init作为weak函数,允许板级代码覆盖它。因此,具体的实现可能位于arch下的某个目录,或者板级目录下的某个文件中。 总结一下,timer_init的位置取决于具体的架构和板级实现,但通常在板级初始化阶段,可能在init_sequence_f中的某个步骤被调用,或者在lowlevel_init中直接初始化。需要具体分析目标平台的代码结构。</think>在U-Boot中,`timer_init`函数的初始化位置与具体硬件平台和代码实现相关。以下是关键步骤解析: --- ### **1. 初始化流程定位** U-Boot的初始化分为两个主要阶段: - **板级初始化前期** (`board_init_f`) - **板级初始化后期** (`board_init_r`) `timer_init`通常属于**前期初始化**,用于为后续操作(如延时、超时控制)提供时钟基础。 --- ### **2. 代码位置分析** #### **(1) ARM架构常见路径** 对于ARM平台,`timer_init`通常出现在以下位置: - **文件**: `arch/arm/cpu/armv7/start.S` 或板级代码(如`board/厂商名/板名/板名.c`) - **调用链**: ```c start.S → _main → board_init_f → init_sequence_f[] → timer_init() ``` 其中`init_sequence_f`是初始化函数数组,定义在`common/board_f.c`中。 #### **(2) 其他架构** - **PowerPC**: 通常在`cpu/ppc4xx/start.S`或板级代码中调用。 - **RISC-V**: 可能在`arch/riscv/cpu/start.S`或SoC特定目录下。 --- ### **3. 代码示例** 在`common/board_f.c`的初始化序列中,可能包含如下定义: ```c static init_fnc_t init_sequence_f[] = { ... timer_init, /* 初始化定时器 */ ... }; ``` --- ### **4. 如何手动验证** - **步骤1**: 在U-Boot源码中搜索`timer_init`函数 ```bash grep -rnw 'timer_init' arch/ board/ drivers/ ``` - **步骤2**: 检查`init_sequence_f`或`init_sequence_r`数组,确认是否包含`timer_init`。 --- ### **5. 典型场景** - **场景1**: 使用DM(Driver Model)的平台,定时器驱动可能通过`driver/clk`或`driver/timer`实现。 - **场景2**: 旧版代码可能直接在板级文件的`board_init()`中调用`timer_init()`。 --- ### **总结** - **核心位置**: `timer_init`通常位于`board_init_f`阶段的初始化序列中。 - **依赖关系**: 具体实现因芯片架构(ARM/PowerPC/RISC-V)和板级设计而异。 - **调试建议**: 通过代码搜索或跟踪启动日志(`CONFIG_LOG`启用)确认调用路径。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值