time_init()

<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에서 타이머용 클럭 소스를 초기화한다.
  • 코드 라인 8에서 틱 브로드캐스트용 hrtimer를 초기화한다.
  • 코드 라인 10~12에서 타이머 rate를 알아와서 HZ로 나눈 값을 lpj_file에 대입한다.
    • 예) arch_timer_rate = 19,200,000 (19.2Mhz), HZ=1000
      • lpj_fine=19,200

 

다음 그림은 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
  • 코드 라인 7에서 Local 타이머의 pre-scaler를 19.2Mhz로 설정한다.
    • ARM_LOCAL_PRESCALER
      • HW_REGISTER_RW(ARM_LOCAL_BASE+0x008)
  • 코드 라인 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 타이머를 클럭 소스 및 스케쥴러 클럭으로 등록하고 딜레이 타이머로도 등록한다.

 

 

참고

 

댓글 남기기