2015년 5월 16일 토요일

[Linux Kernel] 101주차(2015.05.16)

ARM10C : 101 주차 2번째 진행
일시 : 2015.05.16 (101 주차 스터디 진행)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 3명

101 주차 진도

1 1 start_kernel 1 ~/kernel/iamroot/linux-stable/init/main.c 2 1 time_init 741 ~/kernel/iamroot/linux-stable/init/main.c 3 1 clocksource_of_init 557 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c 4 1 mct_init_spi 56 ~/kernel/iamroot/linux-stable/drivers/clocksource/clksrc-of.c 5 1 mct_init_dt 1107 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 6 1 exynos4_timer_resources 1036 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 7 1 exynos4_local_timer_setup 944 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 8 1 request_irq 762 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 9 1 request_threaded_irq 171 ~/kernel/iamroot/linux-stable/include/linux/interrupt.h 10 1 __setup_irq 1648 retval = __setup_irq(irq, desc, action);

main.c::start_kernel()->time_init()

  • called: start_kernel()->time_init()
asmlinkage void __init start_kernel(void)
{
...
    early_irq_init();
    // irq_desc 0 ~ 15 까지의 object을 할당 받고 초기화를 수행
    // allocated_irqs에 bit를 1로 세팅하고 radix tree에 각 irq_desc를 노트로 추가

    init_IRQ();
    // gic, combiner이 사용할 메모리 할당과 자료 구조 설정,
    // gic irq (0~15), combiner irq (32~63) interrupt 를 enable 시킴

    tick_init();
    // tick 관련 mask 변수를 0으로 초기화 수행

    init_timers();
    // boot_tvec_bases의 맴버 값을 초기화하고 timers_nb를 cpu_notifier 에 등록,
    // softirq_vec[1] 에 run_timer_softirq 등록하여 초기화 수행

    hrtimers_init();
    // hrtimer_bases의 맴버 값을 초기화하고 hrtimers_nb를 cpu_notifier 에 등록,
    // softirq_vec[8] 에 run_hrtimer_softirq 등록하여 초기화 수행

    softirq_init();
    // tasklet_vec, tasklet_hi_vec 맴버 값을 초기화하고,
    // softirq_vec[6]에 tasklet_action, softirq_vec[0]에 tasklet_hi_action 등록하여 초기화 수행

    timekeeping_init();
    // ntp 관련 전역변수 초기화, timekeeper, shadow_timekeeper의 맴버값 초기화 수행

    time_init();

time.c::time_init()->of_clk_init(NULL)

  • called: start_kernel()->time_init()->of_clk_init()
// ARM10C 20150103
void __init time_init(void)
{
    // machine_desc->init_time: __mach_desc_EXYNOS5_DT.init_time: NULL
    if (machine_desc->init_time) {
        machine_desc->init_time();
    } else {
#ifdef CONFIG_COMMON_CLK // CONFIG_COMMON_CLK=y
        of_clk_init(NULL);
#endif
        clocksource_of_init();
  • called: start_kernel()->time_init()->clocksource_of_init()

clksrc-of.c::time_init()->clocksource_of_init()

  • called: start_kernel()->time_init()->clocksource_of_init()
// ARM10C 20150307
void __init clocksource_of_init(void)
{
    struct device_node *np;
    const struct of_device_id *match;
    clocksource_of_init_fn init_func;

    for_each_matching_node_and_match(np, __clksrc_of_table, &match) {
    // for (np = of_find_matching_node_and_match(NULL, __clksrc_of_table, &match);
    //      np; np = of_find_matching_node_and_match(np, __clksrc_of_table, &match))

        // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, match: __clksrc_of_table_exynos4210

        // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
        // of_device_is_available(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소): 1
        if (!of_device_is_available(np))
            continue;

        // match->data: __clksrc_of_table_exynos4210.data: mct_init_spi
        init_func = match->data;
        // init_func: mct_init_spi

        // init_func: mct_init_spi
        // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
        // mct_init_spi(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소)
        init_func(np);
3* init_func(np) 에서 호출하는 함수.
  • DTB에 보면 exynos4210-mct 로 mct_init_spi()가 정의되었다.
// ARM10C 20150307
// #define CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi):
// static const struct of_device_id __clksrc_of_table_exynos4210 __used __section(__clksrc_of_table)
// = { .compatible = "samsung,exynos4210-mct",
//     .data = (mct_init_spi == (clocksource_of_init_fn)NULL) ? mct_init_spi : mct_init_spi }
CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);

// ARM10C 20150307
// #define CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi):
// static const struct of_device_id __clksrc_of_table_exynos4412 __used __section(__clksrc_of_table)
// = { .compatible = "samsung,exynos4412-mct",
//     .data = (mct_init_ppi == (clocksource_of_init_fn)NULL) ? mct_init_ppi : mct_init_ppi }
CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);
  • call: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • init_func(np);
    • mct_init_spi(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소)

exynos_mct.c::mct_init_spi()

  • called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • init_func(np);
    • // mct_init_spi(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소)
    • init_func(np)->mct_init_spi()
// ARM10C 20150307
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
static void __init mct_init_spi(struct device_node *np)
{
    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    return mct_init_dt(np, MCT_INT_SPI);
}
  • call: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • init_func(np):mct_init_spi()->mct_init_dt()

exynos_mct.c::mct_init_dt()

  • called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • init_func(np):mct_init_spi()->mct_init_dt()
// ARM10C 20150307
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
{
    u32 nr_irqs, i;

    // int_type: 0
    mct_int_type = int_type;
    // mct_int_type: 0

    /* This driver uses only one global timer interrupt */
    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_G0_IRQ: 0
    // irq_of_parse_and_map(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0): 347
    mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
    // mct_irqs[0]: 347

    // irq_of_parse_and_map(mct node, 0)에서 한일:
    // devtree의 mct node의 interrupt의 property의 값을 dtb에  분석하여 oirq 값을 가져옴
    //
    // (&oirq)->np: combiner node의 주소
    // (&oirq)->args_count: 2
    // (&oirq)->args[0]: 23
    // (&oirq)->args[1]: 3
    //
    // oirq 값을 사용하여 combiner domain에서 virq 값을 찾음
    // virq: 347

    /*
     * Find out the number of local irqs specified. The local
     * timer irqs are specified after the four global timer
     * irqs are specified.
     */
#ifdef CONFIG_OF // CONFIG_OF=y
    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_irq_count(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소): 8
    nr_irqs = of_irq_count(np);
    // nr_irqs: 8

    // of_irq_count(mct node)에서 한일:
    // devtree에 등록된 mct node에 irq 의 갯수를 구함
#else
    nr_irqs = 0;
#endif

    // nr_irqs: 8, MCT_L0_IRQ: 4
    for (i = MCT_L0_IRQ; i < nr_irqs; i++)
        // i: 4, np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
        // irq_of_parse_and_map(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 4): 152
        mct_irqs[i] = irq_of_parse_and_map(np, i);
        // mct_irqs[4]: 152

        // irq_of_parse_and_map(mct node, 4)에서 한일:
        // devtree의 mct node의 interrupt의 property의 값을 dtb에  분석하여 oirq 값을 가져옴
        //
        // (&oirq)->np: gic node의 주소
        // (&oirq)->args_count: 3
        // (&oirq)->args[0]: 0
        // (&oirq)->args[1]: 120
        // (&oirq)->args[2]: 0
        //
        // oirq 값을 사용하여 gic domain에서 virq 값을 찾음
        // virq: 152

        // i: 5...7 loop 수행

    // 위 loop의 수행 결과
    // mct_irqs[4]: 152
    // mct_irqs[5]: 153
    // mct_irqs[6]: 154
    // mct_irqs[7]: 155

    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_iomap(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0): 0xf0006000
    exynos4_timer_resources(np, of_iomap(np, 0));
// cache의 값을 전부 메모리에 반영
  • call: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • ->init_func(np)->mct_init_spi()->mct_init_dt()
    • exynos4_timer_resources()

exynos_mct.c::exynos4_timer_resources()

  • called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • ->init_func(np)->mct_init_spi()->mct_init_dt()
    • exynos4_timer_resources()
// ARM10C 20150321
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0xf0006000
static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{
    int err;
    struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
    // mevt: [pcp0] &percpu_mct_tick

    struct clk *mct_clk, *tick_clk;

    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_clk_get_by_name(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "fin_pll"): kmem_cache#29-oX (fin_pll)
    tick_clk = np ? of_clk_get_by_name(np, "fin_pll") :
                clk_get(NULL, "fin_pll");
    // tick_clk: kmem_cache#29-oX (fin_pll)

    // of_clk_get_by_name에서 한일:
    // mct node의 property "clock-names" 의 값을 찾아서 "fin_pll" 이 있는 위치를 찾고
    // 몇번째 값인지 index를 구함
    //
    // mct node 에서 "clocks" property의 이용하여 devtree의 값을 파싱하여 clkspec에 값을 가져옴
    // (&clkspec)->np: clock node의 주소
    // (&clkspec)->args_count: 1
    // (&clkspec)->args[0]: 1
    //
    // list of_clk_providers 에 등록된 정보들 중에 clkspec 와 매치되는 정보를 찾음
    // 이전에 만들어 놓은 clk_data의 clk_table 정보를 이용하여 clkspec에 있는 arg 값을 이용하여 clk을 찾음
    // tick_clk: kmem_cache#29-oX (fin_pll)

    // tick_clk: kmem_cache#29-oX (fin_pll), IS_ERR(kmem_cache#29-oX (fin_pll)): 0
    if (IS_ERR(tick_clk))
        panic("%s: unable to determine tick clock rate\n", __func__);

    // tick_clk: kmem_cache#29-oX (fin_pll)
    // clk_get_rate(kmem_cache#29-oX (fin_pll)): 24000000
    clk_rate = clk_get_rate(tick_clk);
    // clk_rate: 24000000

    // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_clk_get_by_name(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "mct"):  kmem_cache#29-oX (mct)
    mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct");
    // mct_clk: kmem_cache#29-oX (mct)

    // of_clk_get_by_name에서 한일:
    // mct node의 property "clock-names" 의 값을 찾아서 "mct" 이 있는 위치를 찾고
    // 몇번째 값인지 index를 구함
    //
    // mct node 에서 "clocks" property의 이용하여 devtree의 값을 파싱하여 clkspec에 값을 가져옴
    // (&clkspec)->np: clock node의 주소
    // (&clkspec)->args_count: 1
    // (&clkspec)->args[0]: 315
    //
    // list of_clk_providers 에 등록된 정보들 중에 clkspec 와 매치되는 정보를 찾음
    // 이전에 만들어 놓은 clk_data의 clk_table 정보를 이용하여 clkspec에 있는 arg 값을 이용하여 clk을 찾음
    // mct_clk: kmem_cache#29-oX (mct)

    // mct_clk: kmem_cache#29-oX (mct), IS_ERR(kmem_cache#29-oX (mct)): 0
    if (IS_ERR(mct_clk))
        panic("%s: unable to retrieve mct clock instance\n", __func__);

    // mct_clk: kmem_cache#29-oX (mct)
    // clk_prepare_enable(kmem_cache#29-oX (mct)): 0
    clk_prepare_enable(mct_clk);

    // clk_prepare_enable에서 한일:
    // mct clock의 상위 clock 들의 ops->prepare 함수들을 수행.
    // mct clock의 상위 clock 들의 ops->enable 함수들을 수행.
    // sck_cpll -- Group1_p -- mout_aclk66 -- dout_aclk66 -- mct
    // sck_ppll -|
    // sck_mpll -|
    //
    // sck_cpll, mout_aclk66, dout_aclk66 의 주석을 만들지 않았기 때문에
    // 분석내용을 skip 하도록함

    // base: 0xf0006000
    reg_base = base;
    // reg_base: 0xf0006000

    // reg_base: 0xf0006000
    if (!reg_base)
        panic("%s: unable to ioremap mct address space\n", __func__);

    // mct_int_type: 0, MCT_INT_PPI: 1
    if (mct_int_type == MCT_INT_PPI) {

        err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
                     exynos4_mct_tick_isr, "MCT",
                     &percpu_mct_tick);
        WARN(err, "MCT: can't request IRQ %d (%d)\n",
             mct_irqs[MCT_L0_IRQ], err);
    } else {
        // MCT_L0_IRQ: 4, mct_irqs[4]: 152, cpumask_of(0): &cpu_bit_bitmap[1][0]
        // irq_set_affinity(152, &cpu_bit_bitmap[1][0]): 0
        irq_set_affinity(mct_irqs[MCT_L0_IRQ], cpumask_of(0));

        // irq_set_affinity에서 한일:
        //
        // Interrupt pending register인 GICD_ITARGETSR38 값을 읽고
        // 그 값과 mask 값인 cpu_bit_bitmap[1][0] 을 or 연산한 값을 GICD_ITARGETSR38에
        // 다시 write함
        //
        // GICD_ITARGETSR38 값을 모르기 때문에 0x00000000 로
        // 읽히는 것으로 가정하고 GICD_ITARGETSR38에 0x00000001를 write 함
        // CPU interface 0에 interrupt가 발생을 나타냄
        //
        // (&(kmem_cache#28-oX (irq 152))->irq_data)->affinity->bits[0]: 1
        // (&(kmem_cache#28-oX (irq 152))->irq_data)->state_use_accessors: 0x11000
    }

    // register_cpu_notifier(&exynos4_mct_cpu_nb): 0
    err = register_cpu_notifier(&exynos4_mct_cpu_nb);
    // err: 0

    // register_cpu_notifier 에서 한일:
    // (&cpu_chain)->head: &exynos4_mct_cpu_nb 포인터 대입
    // (&exynos4_mct_cpu_nb)->next은 (&hrtimers_nb)->next로 대입

    // err: 0
    if (err)
        goto out_irq;

    /* Immediately configure the timer on the boot CPU */
    // &mevt->evt: [pcp0] &(&percpu_mct_tick)->evt
    exynos4_local_timer_setup(&mevt->evt);
  • called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • ->init_func(np)->mct_init_spi()->mct_init_dt()
    • exynos4_timer_resources()
    • exynos4_local_timer_setup(&mevt->evt);

exynos_mct.c::exynos4_local_timer_setup()

  • called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
    • // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
    • ->init_func(np)->mct_init_spi()->mct_init_dt()
    • exynos4_timer_resources()
    • exynos4_local_timer_setup(&mevt->evt);
// ARM10C 20150404
// &mevt->evt: [pcp0] &(&percpu_mct_tick)->evt
static int exynos4_local_timer_setup(struct clock_event_device *evt)
{
    struct mct_clock_event_device *mevt;

    // smp_processor_id(): 0
    unsigned int cpu = smp_processor_id();
    // cpu: 0

    // evt: [pcp0] &(&percpu_mct_tick)->evt
    // container_of([pcp0] &(&percpu_mct_tick)->evt, struct mct_clock_event_device, evt): [pcp0] &percpu_mct_tick
    mevt = container_of(evt, struct mct_clock_event_device, evt);
    // mevt: [pcp0] &percpu_mct_tick

    // mevt->base: [pcp0] (&percpu_mct_tick)->base,
    // cpu: 0, EXYNOS4_MCT_L_BASE(0): 0x300
    mevt->base = EXYNOS4_MCT_L_BASE(cpu);
    // mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300

    // mevt->name: [pcp0] (&percpu_mct_tick)->name, cpu: 0
    sprintf(mevt->name, "mct_tick%d", cpu);
    // mevt->name: [pcp0] (&percpu_mct_tick)->name: "mct_tick0"

    // evt->name: [pcp0] (&(&percpu_mct_tick)->evt)->name,
    // mevt->name: [pcp0] (&percpu_mct_tick)->name: "mct_tick0"
    evt->name = mevt->name;
    // evt->name: [pcp0] (&(&percpu_mct_tick)->evt)->name: "mct_tick0"

    // evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask,
    // cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
    evt->cpumask = cpumask_of(cpu);
    // evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]

    // evt->set_next_event: [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event
    evt->set_next_event = exynos4_tick_set_next_event;
    // evt->set_next_event: [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event: exynos4_tick_set_next_event

    // evt->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode
    evt->set_mode = exynos4_tick_set_mode;
    // evt->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode

    // evt->features: [pcp0] (&(&percpu_mct_tick)->evt)->features,
    // CLOCK_EVT_FEAT_PERIODIC: 0x000001, CLOCK_EVT_FEAT_ONESHOT: 0x000002
    evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
    // evt->features: [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3

    // evt->rating: [pcp0] (&(&percpu_mct_tick)->evt)->rating
    evt->rating = 450;
    // evt->rating: [pcp0] (&(&percpu_mct_tick)->evt)->rating: 450

    // evt: [pcp0] &(&percpu_mct_tick)->evt, clk_rate: 24000000, TICK_BASE_CNT: 1
    clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
                    0xf, 0x7fffffff);

clockevents.c::clockevents_config_and_register()

// ARM10C 20150404
// evt: [pcp0] &(&percpu_mct_tick)->evt, 12000000, 0xf, 0x7fffffff
void clockevents_config_and_register(struct clock_event_device *dev,
                     u32 freq, unsigned long min_delta,
                     unsigned long max_delta)
{
    // dev->min_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks, min_delta: 0xf
    dev->min_delta_ticks = min_delta;
    // dev->min_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks: 0xf

    // dev->max_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks, max_delta: 0x7fffffff
    dev->max_delta_ticks = max_delta;
    // dev->max_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks: 0x7fffffff

    // dev: [pcp0] &(&percpu_mct_tick)->evt, freq: 12000000
    // clockevents_config(&(&percpu_mct_tick)->evt, 12000000)
    clockevents_config(dev, freq);

    // clockevents_config에서 한일:
    // [pcp0] (&(&percpu_mct_tick)->evt)->mult: 0x3126E98
    // [pcp0] (&(&percpu_mct_tick)->evt)->shift: 32
    // [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ns: 0x4E2
    // [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ns: 0x29AAAAA444

    // dev: [pcp0] &(&percpu_mct_tick)->evt
    clockevents_register_device(dev);
}
EXPORT_SYMBOL_GPL(clockevents_config_and_register);

clockevents.c::clockevents_register_device()

// ARM10C 20150411
// dev: [pcp0] &(&percpu_mct_tick)->evt
void clockevents_register_device(struct clock_event_device *dev)
{
    unsigned long flags;

    // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, CLOCK_EVT_MODE_UNUSED: 0
    BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);

    // evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
    if (!dev->cpumask) {
        WARN_ON(num_possible_cpus() > 1);
        dev->cpumask = cpumask_of(smp_processor_id());
    }

    raw_spin_lock_irqsave(&clockevents_lock, flags);

    // raw_spin_lock_irqsave에서 한일:
    // clockevents_lock를 사용하여 spin lock을 수행하고 cpsr을 flags에 저장

    // &dev->list: [pcp0] (&(&percpu_mct_tick)->evt)->list
    list_add(&dev->list, &clockevent_devices);

    // list_add에서 한일:
    // list clockevent_devices에 [pcp0] (&(&percpu_mct_tick)->evt)->list를 추가함

    // dev: [pcp0] &(&percpu_mct_tick)->evt
    tick_check_new_device(dev);
    clockevents_notify_released();

    raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
EXPORT_SYMBOL_GPL(clockevents_register_device);

tick-common.c:tick_check_new_device()

// ARM10C 20150411
// dev: [pcp0] &(&percpu_mct_tick)->evt
void tick_check_new_device(struct clock_event_device *newdev)
{
    struct clock_event_device *curdev;
    struct tick_device *td;
    int cpu;

    // smp_processor_id(): 0
    cpu = smp_processor_id();
    // cpu: 0

    // cpu: 0, newdev->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
    // cpumask_test_cpu(0, &cpu_bit_bitmap[1][0]): 1
    if (!cpumask_test_cpu(cpu, newdev->cpumask))
        goto out_bc;

    // cpu: 0, per_cpu(tick_cpu_device, 0): [pcp0] tick_cpu_device
    td = &per_cpu(tick_cpu_device, cpu);
    // td: [pcp0] &tick_cpu_device

    // td->evtdev: [pcp0] (&tick_cpu_device)->evtdev
    curdev = td->evtdev;
    // curdev: [pcp0] (&tick_cpu_device)->evtdev

    /* cpu local device ? */
    // curdev: [pcp0] (&tick_cpu_device)->evtdev, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0
    // tick_check_percpu([pcp0] (&tick_cpu_device)->evtdev, [pcp0] &(&percpu_mct_tick)->evt, 0): true
    if (!tick_check_percpu(curdev, newdev, cpu))
        goto out_bc;

    /* Preference decision */
    // curdev: [pcp0] (&tick_cpu_device)->evtdev, newdev: [pcp0] &(&percpu_mct_tick)->evt
    // tick_check_preferred([pcp0] (&tick_cpu_device)->evtdev, [pcp0] &(&percpu_mct_tick)->evt): 1
    if (!tick_check_preferred(curdev, newdev))
        goto out_bc;

    // newdev->owner: [pcp0] (&(&percpu_mct_tick)->evt)->owner,
    // try_module_get([pcp0] (&(&percpu_mct_tick)->evt)->owner): true
    if (!try_module_get(newdev->owner))
        return;

    /*
     * Replace the eventually existing device by the new
     * device. If the current device is the broadcast device, do
     * not give it back to the clockevents layer !
     */
    // curdev: [pcp0] (&tick_cpu_device)->evtdev: NULL
    // tick_is_broadcast_device([pcp0] (&tick_cpu_device)->evtdev): 0
    if (tick_is_broadcast_device(curdev)) {
        clockevents_shutdown(curdev);
        curdev = NULL;
    }

    // curdev: [pcp0] (&tick_cpu_device)->evtdev: NULL, newdev: [pcp0] &(&percpu_mct_tick)->evt
    // clockevents_exchange_device(NULL, [pcp0] &(&percpu_mct_tick)->evt)
    clockevents_exchange_device(curdev, newdev);

    // clockevents_exchange_device에서 한일:
    // timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
    // 동작하지 않도록 변경함
    // L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
    //
    // [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1
    // [pcp0] (&(&percpu_mct_tick)->evt)->next_event.tv64: 0x7FFFFFFFFFFFFFFF

    // td: [pcp0] &tick_cpu_device, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
    tick_setup_device(td, newdev, cpu, cpumask_of(cpu));

tick-common.c::tick_setup_device()

// ARM10C 20150418
// td: [pcp0] &tick_cpu_device, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
static void tick_setup_device(struct tick_device *td,
                  struct clock_event_device *newdev, int cpu,
                  const struct cpumask *cpumask)
{
    ktime_t next_event;
    void (*handler)(struct clock_event_device *) = NULL;
    // handler: NULL

    /*
     * First device setup ?
     */
    // td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: NULL
    if (!td->evtdev) {
        /*
         * If no cpu took the do_timer update, assign it to
         * this cpu:
         */
        // tick_do_timer_cpu: -2, TICK_DO_TIMER_BOOT: -2
        if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
            // cpu: 0, tick_nohz_full_cpu(0): false
            if (!tick_nohz_full_cpu(cpu))
                // tick_do_timer_cpu: -2, cpu: 0
                tick_do_timer_cpu = cpu;
                // tick_do_timer_cpu: 0
            else
                tick_do_timer_cpu = TICK_DO_TIMER_NONE;

            // ktime_get(): (ktime_t) { .tv64 = 0}
            tick_next_period = ktime_get();
            // tick_next_period.tv64: 0

            // NSEC_PER_SEC: 1000000000L, HZ: 100
            // ktime_set(0, 10000000): (ktime_t) { .tv64 = 10000000}
            tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
            // tick_period.tv64: 10000000
        }

        /*
         * Startup in periodic mode first.
         */
        // td->mode: [pcp0] (&tick_cpu_device)->mode, TICKDEV_MODE_PERIODIC: 0
        td->mode = TICKDEV_MODE_PERIODIC;
        // td->mode: [pcp0] (&tick_cpu_device)->mode: 0
    } else {
        handler = td->evtdev->event_handler;
        next_event = td->evtdev->next_event;
        td->evtdev->event_handler = clockevents_handle_noop;
    }

    // td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: NULL, newdev: [pcp0] &(&percpu_mct_tick)->evt
    td->evtdev = newdev;
    // td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: [pcp0] &(&percpu_mct_tick)->evt

    /*
     * When the device is not per cpu, pin the interrupt to the
     * current cpu:
     */
    // newdev->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0],
    // cpumask: &cpu_bit_bitmap[1][0]
    // cpumask_equal(&cpu_bit_bitmap[1][0], &cpu_bit_bitmap[1][0]): 1
    if (!cpumask_equal(newdev->cpumask, cpumask))
        irq_set_affinity(newdev->irq, cpumask);

    /*
     * When global broadcasting is active, check if the current
     * device is registered as a placeholder for broadcast mode.
     * This allows us to handle this x86 misfeature in a generic
     * way. This function also returns !=0 when we keep the
     * current active broadcast state for this CPU.
     */
    // newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0
    // tick_device_uses_broadcast([pcp0] &(&percpu_mct_tick)->evt, 0): 0
    if (tick_device_uses_broadcast(newdev, cpu))
        return;

    // td->mode: [pcp0] (&tick_cpu_device)->mode: 0, TICKDEV_MODE_PERIODIC: 0
    if (td->mode == TICKDEV_MODE_PERIODIC)
        // newdev: [pcp0] &(&percpu_mct_tick)->evt
        tick_setup_periodic(newdev, 0);
    else
        tick_setup_oneshot(newdev, handler, next_event);
}
  • call: tick_setup_periodic()

tick-common.c:tick_setup_periodic()

  • called: tick_setup_periodic()
// ARM10C 20150418
// newdev: [pcp0] &(&percpu_mct_tick)->evt, 0
void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
    // dev: [pcp0] &(&percpu_mct_tick)->evt, broadcast: 0
    tick_set_periodic_handler(dev, broadcast);

    // tick_set_periodic_handler에서 한일:
    // [pcp0] (&(&percpu_mct_tick)->evt)->event_handler: tick_handle_periodic

    /* Broadcast setup ? */
    // dev: [pcp0] &(&percpu_mct_tick)->evt,
    // tick_device_is_functional([pcp0] &(&percpu_mct_tick)->evt): 1
    if (!tick_device_is_functional(dev))
        return;

// 2015/04/18 종료
// 2015/05/09 시작

    // dev->features: [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3, CLOCK_EVT_FEAT_ONESHOT: 0x000002,
    // tick_broadcast_oneshot_active(): 0
    if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
        !tick_broadcast_oneshot_active()) {
        // dev: [pcp0] &(&percpu_mct_tick)->evt, CLOCK_EVT_MODE_PERIODIC: 2
        clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);

        // clockevents_set_mode에서 한일:
        // timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
        // 동작하지 않도록 변경함
        // L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
        //
        // register L_ICNTB 에 0x8001D4C0 write함
        // local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
        // interrupt manual update를 enable 시킴
        //
        // register L_INT_ENB 에 0x1 write함
        // local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
        // interrupt counter expired interrupt 가 발생하도록 함
        //
        // register L_TCON 에 0x7 write함
        // local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
        //
        // [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2

clockevents.c::clockevents_set_mode()

// ARM10C 20150509
// dev: [pcp0] &(&percpu_mct_tick)->evt, CLOCK_EVT_MODE_PERIODIC: 2
void clockevents_set_mode(struct clock_event_device *dev,
                 enum clock_event_mode mode)
{
// 2015/04/11 종료
// 2015/04/18 시작

    // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, mode: 1
    // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1, mode: 2
    if (dev->mode != mode) {
        // dev->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
        // mode: 1, dev: [pcp0] &(&percpu_mct_tick)->evt
        // exynos4_tick_set_mode(1, [pcp0] &(&percpu_mct_tick)->evt)
        // dev->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
        // mode: 2, dev: [pcp0] &(&percpu_mct_tick)->evt
        // exynos4_tick_set_mode(2, [pcp0] &(&percpu_mct_tick)->evt)
        dev->set_mode(mode, dev);

        // exynos4_tick_set_mode(1)에서 한일:
        // timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
        // 동작하지 않도록 변경함
        // L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임

        // exynos4_tick_set_mode(2)에서 한일:
        // timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
        // 동작하지 않도록 변경함
        // L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
        //
        // register L_ICNTB 에 0x8001D4C0 write함
        // local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
        // interrupt manual update를 enable 시킴
        //
        // register L_INT_ENB 에 0x1 write함
        // local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
        // interrupt counter expired interrupt 가 발생하도록 함
        //
        // register L_TCON 에 0x7 write함
        // local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴

        // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, mode: 1
        // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1, mode: 2
        dev->mode = mode;
        // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1
        // dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2

        /*
         * A nsec2cyc multiplicator of 0 is invalid and we'd crash
         * on it, so fix it up and emit a warning:
         */
        // mode: 1, CLOCK_EVT_MODE_ONESHOT: 3
        // mode: 2, CLOCK_EVT_MODE_ONESHOT: 3
        if (mode == CLOCK_EVT_MODE_ONESHOT) {
            if (unlikely(!dev->mult)) {
                dev->mult = 1;
                WARN_ON(1);
            }
        }
    }
}

log

  • 1st log
    5de994b..02918e4  master     -> origin/master
Updating 5de994b..02918e4
Fast-forward
drivers/clocksource/exynos_mct.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/proc/internal.h               |   1 +
include/linux/cpumask.h          |   2 ++
include/linux/interrupt.h        |  23 +++++++++++++++
include/linux/irq.h              |   6 ++++
include/linux/irqdesc.h          |   1 +
include/linux/spinlock.h         |   1 +
kernel/irq/internals.h           |   6 +++-
kernel/irq/irqdesc.c             |   2 ++
kernel/irq/manage.c              |  73 +++++++++++++++++++++++++++++++++++++++++++++--
kernel/irq/proc.c                |  41 +++++++++++++++++++++++++++
11 files changed, 365 insertions(+), 4 deletions(-)
  • 2nd log
    02918e4..3d96381  master     -> origin/master
Updating 02918e4..3d96381
Fast-forward
drivers/clocksource/exynos_mct.c | 113 ++++++++++++++++++++++++++++++++++++---
include/asm-generic/param.h      |   1 +
init/main.c                      |   2 +
3 files changed, 109 insertions(+), 7 deletions(-)

댓글 없음:

댓글 쓰기