<kernel v5.4>
클럭 및 타이머 초기화
time_init() – ARM64
arch/arm64/kernel/time.c
void __init time_init(void) { u32 arch_timer_rate; of_clk_init(NULL); timer_probe(); tick_setup_hrtimer_broadcast(); arch_timer_rate = arch_timer_get_rate(); if (!arch_timer_rate) panic("Unable to initialise architected timer.\n"); /* Calibrate the delay loop directly */ lpj_fine = arch_timer_rate / HZ; }
클럭 및 타이머를 초기화한다.
- 코드 라인 5에서 디바이스 트리 기반의 클럭 디바이스를 초기화한다.
- 코드 라인 6에서 타이머용 클럭 소스를 초기화한다.
- 참고: Timer -3- (Clock Sources Subsystem) | 문c
- 코드 라인 8에서 틱 브로드캐스트용 hrtimer를 초기화한다.
- 코드 라인 10~12에서 타이머 rate를 알아와서 HZ로 나눈 값을 lpj_file에 대입한다.
- 예) arch_timer_rate = 19,200,000 (19.2Mhz), HZ=1000
- lpj_fine=19,200
- 예) arch_timer_rate = 19,200,000 (19.2Mhz), HZ=1000
다음 그림은 time_init() 함수의 클럭과 타이머를 초기화하기 위한 함수 호출 관계이다.
time_init() – ARM32
arch/arm/kernel/time.c
void __init time_init(void) { if (machine_desc->init_time) { machine_desc->init_time(); } else { #ifdef CONFIG_COMMON_CLK of_clk_init(NULL); #endif timer_probe(); } }
클럭 및 타이머를 초기화한다.
- 코드 라인 3~4에서 시스템이 머신 specific한 코드로 초기화를 지원하는 경우 해당 함수를 호출한다.
- rpi2: bcm2709_timer_init() 함수 호출
- 코드 라인 5~9에서 시스템이 Device Tree를 사용하여 클럭 디바이스 및 타이머용 클럭 소스를 초기화한다.
다음 그림은 time_init() 함수의 클럭과 타이머를 초기화하기 위한 함수 호출 관계이다.
머신 디스크립터를 이용한 time 초기화 – RPI2(BCM2709) – 커널 v4.0
bcm2709_timer_init()
arch/arm/mach-bcm2709/bcm2709.c
static void __init bcm2709_timer_init(void) { extern void dc4_arch_timer_init(void); // timer control writel(0, __io_address(ARM_LOCAL_CONTROL)); // timer pre_scaler writel(0x80000000, __io_address(ARM_LOCAL_PRESCALER)); // 19.2MHz //writel(0x06AAAAAB, __io_address(ARM_LOCAL_PRESCALER)); // 1MHz if (use_dt) { of_clk_init(NULL); clocksource_of_init(); } else dc4_arch_timer_init(); }
부트 cpu의 Local 타이머를 0으로 초기화하고 19.2Mhz pre-scaler로 설정한 후 클럭 소스들을 초기화한다.
- 코드 라인 5에서 Local 타이머를 0으로 초기화한다.
- ARM_LOCAL_CONTROL
- HW_REGISTER_RW(ARM_LOCAL_BASE+0x000)
- ARM_LOCAL_BASE = 0x4000_0000
- ARM_LOCAL_CONTROL
- 코드 라인 7에서 Local 타이머의 pre-scaler를 19.2Mhz로 설정한다.
- ARM_LOCAL_PRESCALER
- HW_REGISTER_RW(ARM_LOCAL_BASE+0x008)
- ARM_LOCAL_PRESCALER
- 코드 라인 10~14에서 디바이스 트리를 사용하는 방법으로 클럭 및 클럭 소스들을 초기화한다.
- 코드 라인 15~16에서 rpi2 머신 전용 코드로 클럭 및 클럭 소스들을 초기화한다.
dc4_arch_timer_init()
drivers/clocksource/arm_arch_timer.c
int __init dc4_arch_timer_init(void) { if (arch_timers_present & ARCH_CP15_TIMER) { pr_warn("arch_timer: multiple nodes in dt, skipping\n"); return -1; } arch_timers_present |= ARCH_CP15_TIMER; /* Try to determine the frequency from the device tree or CNTFRQ */ arch_timer_rate = 19200000; arch_timer_ppi[PHYS_SECURE_PPI] = IRQ_ARM_LOCAL_CNTPSIRQ; arch_timer_ppi[PHYS_NONSECURE_PPI] = IRQ_ARM_LOCAL_CNTPNSIRQ; arch_timer_ppi[VIRT_PPI] = IRQ_ARM_LOCAL_CNTVIRQ; arch_timer_ppi[HYP_PPI] = IRQ_ARM_LOCAL_CNTHPIRQ; /* * If HYP mode is available, we know that the physical timer * has been configured to be accessible from PL1. Use it, so * that a guest can use the virtual timer instead. * * If no interrupt provided for virtual timer, we'll have to * stick to the physical timer. It'd better be accessible... */ if (is_hyp_mode_available() || !arch_timer_ppi[VIRT_PPI]) { arch_timer_use_virtual = false; } arch_timer_c3stop = 0; arch_timer_register(); arch_timer_common_init(); return 0; }
Generic 타이머를 아키텍처 클럭 소스로 등록하여 인터럽트를 연결하고 event source로 등록한다. 그리고 스케쥴러 클럭 및 딜레이 타이머로도 등록하도록 준비한다.
- 코드 라인 3~8에서 이미 보조프로세서 cp15를 사용하는 방식의 generic 아키텍처 타이머가 초기화된 경우 함수를 빠져나간다.
- 코드 라인 11에서 전역 arch_timer_rate에 19.2Mhz를 대입한다.
- 코드 라인 13~16에서 4개의 타이머 각각에서 사용할 인터럽트 번호를 대입한다.
- 코드 순서대로 96, 97, 99, 98번 IRQ를 사용한다.
- 코드 라인 26~28에서 하이퍼 모드로 부트한 경우 arch_timer_use_virtual을 false로 바꾼다.
- 코드 라인 30에서 전역 arch_timer_c3stop에 0을 대입한다.
- 카운터가 정지하지 않고 항상 동작한다는 의미이다.
- 코드 라인 32에서 현재 커널이 사용하도록 지정된 Generic 타이머를 per-cpu 인터럽트에 등록하고 boot cpu용은 즉시 enable하고 클럭 이벤트에 등록한다.
- 코드 라인 33에서 현재 커널이 사용하도록 지정된 Generic 타이머를 클럭 소스 및 스케쥴러 클럭으로 등록하고 딜레이 타이머로도 등록한다.
참고
- Common Clock Framework -1- (초기화)
- Common Clock Framework -2- (APIs)
- Timer -1- (Lowres Timer) | 문c
- Timer -2- (HRTimer) | 문c
- Timer -3- (Clock Sources Subsystem) | 문c
- Timer -4- (Clock Sources Watchdog) | 문c
- Timer -5- (Clock Events Subsystem) | 문c
- Timer -6- (Sched Clock & Delay Timers) | 문c
- Timer -7- (Timecounter) | 문c
- Timer -8- (Tick Device) | 문c
- Timer -9- (Timekeeping) | 문c
- Timer -10- (Posix Clock & Timers) | 문c
- time_init() | 문c – 현재 글
- sched_clock_postinit() | 문c
- sched_clock_init() | 문c
- init_timers() | 문c
- hrtimers_init() | 문c
- tick_init() | 문c
- timekeeping_init() | 문c
- calibrate_delay() | 문c