2015년 4월 18일 토요일

[Linux Kernel] 99주차(2015.04.18)

ARM10C 99주차
일시 : 2015.04.18 (99주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 3명

99주차 진도

  • start_kernel 1 ~/kernel/iamroot/linux-stable/init/main.c
    • time_init 741 ~/kernel/iamroot/linux-stable/init/main.c
    • clocksource_of_init 557 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
      • mct_init_spi 56 ~/kernel/iamroot/linux-stable/drivers/clocksource/clksrc-of.c
        • mct_init_dt 877 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
          • exynos4_timer_resources 806 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
            • exynos4_local_timer_setup 714 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
              • clockevents_config_and_register 525 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
                • clockevents_register_device 580 ~/kernel/iamroot/linux-stable/kernel/time/clockevents.c
                  • tick_check_new_device 485 ~/kernel/iamroot/linux-stable/kernel/time/clockevents.c
                    • tick_setup_device 385 tick_setup_device(td, newdev, cpu, cpumask_of(cpu));

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 종료

log

  • 1st log
   cbe0317..46c6178  master     -> origin/master
Updating cbe0317..46c6178
Fast-forward
arch/arm/kernel/vmlinux.lds.S       |  1 +
drivers/clocksource/exynos_mct.c    | 48 +++++++++++++++++++++++++++++++++++++++++++-----
include/linux/clockchips.h          |  4 ++++
include/linux/clocksource.h         |  2 ++
include/linux/compiler.h            |  1 +
include/linux/cpumask.h             |  1 +
include/linux/jiffies.h             |  1 +
include/linux/ktime.h               |  2 ++
include/linux/percpu-defs.h         |  4 ++++
include/linux/seqlock.h             | 25 +++++++++++++++++++++++--
include/linux/tick.h                |  1 +
include/linux/time.h                |  1 +
include/linux/timekeeper_internal.h |  1 +
kernel/time/clockevents.c           | 32 +++++++++++++++++++++++++++++++-
kernel/time/jiffies.c               |  2 ++
kernel/time/tick-common.c           | 23 +++++++++++++++++++++++
kernel/time/tick-internal.h         |  2 ++
kernel/time/timekeeping.c           | 25 +++++++++++++++++++++++++
kernel/timer.c                      |  1 +
19 files changed, 169 insertions(+), 8 deletions(-)
  • 2nd log
   46c6178..8299c20  master     -> origin/master
Updating 46c6178..8299c20
Fast-forward
include/asm-generic/param.h  |  1 +
include/linux/bitmap.h       |  7 ++++++
include/linux/clockchips.h   |  5 ++++
include/linux/cpumask.h      | 11 +++++++++
include/linux/ktime.h        |  6 +++++
include/linux/seqlock.h      | 29 +++++++++++++++--------
include/linux/spinlock.h     |  1 +
include/linux/tick.h         |  4 ++++
include/linux/time.h         |  1 +
kernel/time/tick-broadcast.c | 55 ++++++++++++++++++++++++++++++++++++++++++--
kernel/time/tick-common.c    | 32 ++++++++++++++++++++++++++
kernel/time/tick-internal.h  | 10 ++++++--
kernel/time/tick-oneshot.c   |  1 +
kernel/time/timekeeping.c    | 26 +++++++++++++++++----
14 files changed, 170 insertions(+), 19 deletions(-)

2015년 4월 11일 토요일

[Linux Kernel] 98주차(2015.04.11)

ARM10C 98주차
일시 : 2015.04.11 (98주차)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 3명

98주차 진도

  • start_kernel() init/main.c
- ->time_init() 741 /init/main.c
- ->of_clk_init(NULL) 154 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
- ->clocksource_of_init() 557 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
- ->mct_init_spi   56  ~/kernel/iamroot/linux-stable/drivers/clocksource/clksrc-of.c
- ->mct_init_dt  583  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
  • exynos4_local_timer_setup(struct clock_event_device *evt)
- clockevents_config_and_register(struct clock_event_device *dev,
- clockevents_register_device(struct clock_event_device *dev)
- tick_check_new_device(dev);
- clockevents_exchange_device(curdev, newdev);
- local_irq_save(flags);
  • start_kernel() init/main.c
- ->time_init() 741 /init/main.c
- ->of_clk_init(NULL) 154 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
- ->clocksource_of_init() 557 ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
- ->mct_init_spi   56  ~/kernel/iamroot/linux-stable/drivers/clocksource/clksrc-of.c
- ->mct_init_dt  583  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
- ->exynos4_timer_resources()  768 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
- -> exynos4_local_timer_setup() 676 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
- clockevents_config_and_register(struct clock_event_device *dev,
- clockevents_register_device(struct clock_event_device *dev)
- tick_check_new_device(dev);
- clockevents_exchange_device(curdev, newdev);
- local_irq_save(flags);
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       698  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
6  1 exynos4_timer_resources   627  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
7  1 of_clk_get_by_name   504  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
8  1 of_clk_get        108  ~/kernel/iamroot/linux-stable/drivers/clk/clkdev.c
9  1 of_clk_get_from_provider    62  ~/kernel/iamroot/linux-stable/drivers/clk/clkdev.c
>10  1 list_for_each_entry  3634  list_for_each_entry(provider, &of_clk_providers, link) {
1  1 time_init         741  init/main.c
2  1 clocksource_of_init   557  ~/kernel/iamroot/linux-stable/arch/arm/kernel/time.c
3  1 mct_init_spi       56  ~/kernel/iamroot/linux-stable/drivers/clocksource/clksrc-of.c
4  1 mct_init_dt       583  ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
5  1 irq_of_parse_and_map   557 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c
6  1 of_irq_parse_one    49  if (of_irq_parse_one(dev, index, &oirq))

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();

exynos5420_clk_init()

  • called: start_kernel()->time_init()->of_clk_init()->exynos5420_clk_init()
    • // clk_init_cb: exynos5420_clk_init,
    • // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
    • // exynos5420_clk_init(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소)
    • clk_init_cb(np);
// ARM10C 20150103
typedef void (*of_clk_init_cb_t)(struct device_node *);
/* register exynos5420 clocks */
// ARM10C 20150103
// devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
static void __init exynos5420_clk_init(struct device_node *np)
{
    void __iomem *reg_base;

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0);
        // reg_base: 0xf0040000
  • of_iomap에서 한일:
    • // device tree 있는 clock node에서 node의 resource 값을 가져옴
    • // of_address_to_resource에서 한일(index: 0):
    • // (&res)->start: 0x10010000
    • // (&res)->end: 0x1003ffff
    • // (&res)->flags: IORESOURCE_MEM: 0x00000200
    • // (&res)->name: "/clock-controller@10010000"
    • /*
    • // alloc area (CLK) 를 만들고 rb tree에 alloc area 를 추가
    • // 가상주소 va_start 기준으로 CLK 를 RB Tree 추가한 결과
//                                  CHID-b
//                               (0xF8000000)
//                              /            \
//                         TMR-b               PMU-b
//                    (0xF6300000)             (0xF8180000)
//                      /      \               /           \
//                GIC#1-r      WDT-b         CMU-b         SRAM-b
//            (0xF0002000)   (0xF6400000)  (0xF8100000)   (0xF8400000)
//             /       \                                          \
//        GIC#0-b     CLK-b                                        ROMC-r
//    (0xF0000000)   (0xF0040000)                                 (0xF84C0000)
//                   /      \
//               COMB-r     SYSC-r
//          (0xF0004000)   (0xF6100000)
//
  • // vmap_area_list에 GIC#0 - GIC#1 - COMB - CLK - SYSC -TMR - WDT - CHID - CMU - PMU - SRAM - ROMC
  • // 순서로 리스트에 연결이 됨
  • //
  • // (kmem_cache#30-oX (vm_struct))->flags: GFP_KERNEL: 0xD0
  • // (kmem_cache#30-oX (vm_struct))->addr: 0xf0040000
  • // (kmem_cache#30-oX (vm_struct))->size: 0x31000
  • // (kmem_cache#30-oX (vm_struct))->caller: __builtin_return_address(0)
  • //
  • // (kmem_cache#30-oX (vmap_area CLK))->vm: kmem_cache#30-oX (vm_struct)
  • // (kmem_cache#30-oX (vmap_area CLK))->flags: 0x04
  • */
  • // device tree 있는 clock node에서 node의 resource 값을 pgtable에 매핑함
  • // 0xc0004780이 가리키는 pte의 시작주소에 0x10010653 값을 갱신
  • // (linux pgtable과 hardware pgtable의 값 같이 갱신)
//  pgd                   pte
// |              |
// +--------------+
// |              |       +--------------+ +0
// |              |       |  0xXXXXXXXX  | ---> 0x10010653 에 매칭되는 linux pgtable 값
// +- - - - - - - +       |  Linux pt 0  |
// |              |       +--------------+ +1024
// |              |       |              |
// +--------------+ +0    |  Linux pt 1  |
// | *(c0004780)  |-----> +--------------+ +2048
// |              |       |  0x10010653  | ---> 2308
// +- - - - - - - + +4    |   h/w pt 0   |
// | *(c0004784)  |-----> +--------------+ +3072
// |              |       +              +
// +--------------+ +8    |   h/w pt 1   |
// |              |       +--------------+ +4096
  • // cache의 값을 전부 메모리에 반영
  • samsung_clk_init()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);
  • samsung_clk_init() 에서 한일:
    • // struct samsung_clk_reg_dump를 59개 만큼 메모리를 할당 받아
    • // exynos5420_clk_regs의 값으로 맴버값 세팅
    • // (kmem_cache#26-oX)[0...58].offset: exynos5420_clk_regs[0...58]
    • //
    • // syscore_ops_list의 tail에 (&samsung_clk_syscore_ops)->node 를 추가
    • //
    • // struct clk * 를 769개 만큼 메모리를 clk_table에 할당 받음
    • // clk_table: kmem_cache#23-o0
    • //
    • // clk_data.clks: kmem_cache#23-o0 (clk_table)
    • // clk_data.clk_num: 769
    • //
    • // struct of_clk_provider 의 메모리(kmem_cache#30-oX)를 할당 받고 맴버값 초기화 수행
    • //
    • // (kmem_cache#30-oX)->node: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
    • // (kmem_cache#30-oX)->data: &clk_data
    • // (kmem_cache#30-oX)->get: of_clk_src_onecell_get
    • //
    • // list인 of_clk_providers의 head에 (kmem_cache#30-oX)->link를 추가
  • samsung_clk_of_register_fixed_ext()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);
  • samsung_clk_of_register_fixed_ext() 에서 한일:
    • // devtree에서 allnext로 순회 하면서 찾은 fixed-rate-clocks node 에서
    • // fixed-rate-clocks node에서 "clock-frequency" property값을 freq에 읽어옴
    • // freq: 24000000
    • // exynos5420_fixed_rate_ext_clks[0].fixed_rate: 24000000
    • //
    • // struct clk_fixed_rate 만큼 메모리를 kmem_cache#30-oX 할당 받고 struct clk_fixed_rate 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#30-oX)->fixed_rate: 24000000
    • // (kmem_cache#30-oX)->hw.init: &init
    • // (&(kmem_cache#30-oX)->hw)->clk: kmem_cache#29-oX
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX)->name: kmem_cache#30-oX ("fin_pll")
    • // (kmem_cache#29-oX)->ops: &clk_fixed_rate_ops
    • // (kmem_cache#29-oX)->hw: &(kmem_cache#30-oX)->hw
    • // (kmem_cache#29-oX)->flags: 0x30
    • // (kmem_cache#29-oX)->num_parents: 0
    • // (kmem_cache#29-oX)->parent_names: ((void *)16)
    • // (kmem_cache#29-oX)->parent: NULL
    • // (kmem_cache#29-oX)->rate: 24000000
    • //
    • // (&(kmem_cache#29-oX)->child_node)->next: NULL
    • // (&(kmem_cache#29-oX)->child_node)->pprev: &(&(kmem_cache#29-oX)->child_node)
    • //
    • // (&clk_root_list)->first: &(kmem_cache#29-oX)->child_node
    • //
    • // clk_table[1]: (kmem_cache#23-o0)[1]: kmem_cache#29-oX
    • //
    • // struct clk_lookup_alloc 의 메모리를 kmem_cache#30-oX 할당 받고
    • // struct clk_lookup_alloc 맴버값 초기화 수행
    • //
    • // (kmem_cache#30-oX)->cl.clk: kmem_cache#29-oX
    • // (kmem_cache#30-oX)->con_id: "fin_pll"
    • // (kmem_cache#30-oX)->cl.con_id: (kmem_cache#30-oX)->con_id: "fin_pll"
    • //
    • // list clocks에 &(&(kmem_cache#30-oX)->cl)->nade를 tail로 추가
  • samsung_clk_register_pll()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);
  • samsung_clk_register_pll() 에서 한일:
    • // exynos5420_plls에 정의되어 있는 PLL 값들을 초기화 수행
    • //
    • // [apll] 의 초기화 값 수행 결과:
    • // struct clk_fixed_rate 만큼 메모리를 kmem_cache#30-oX (apll) 할당 받고 struct clk_fixed_rate 의 멤버 값을 아래와 같이 초기화 수행
    • // pll: kmem_cache#30-oX (apll)
    • //
    • // (kmem_cache#30-oX (apll))->hw.init: &init
    • // (kmem_cache#30-oX (apll))->type: pll_2550: 2
    • // (kmem_cache#30-oX (apll))->lock_reg: 0xf0040000
    • // (kmem_cache#30-oX (apll))->con_reg: 0xf0040100
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX (apll) 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX (apll))->name: kmem_cache#30-oX ("fout_apll")
    • // (kmem_cache#29-oX (apll))->ops: &samsung_pll35xx_clk_min_ops
    • // (kmem_cache#29-oX (apll))->hw: &(kmem_cache#30-oX (apll))->hw
    • // (kmem_cache#29-oX (apll))->flags: 0x40
    • // (kmem_cache#29-oX (apll))->num_parents: 1
    • // (kmem_cache#29-oX (apll))->parent_names: kmem_cache#30-oX
    • // (kmem_cache#29-oX (apll))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "fin_pll"
    • // (kmem_cache#29-oX (apll))->parent: kmem_cache#29-oX (fin_pll)
    • // (kmem_cache#29-oX (apll))->rate: 1000000000 (1 Ghz)
    • //
    • // (&(kmem_cache#29-oX (apll))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (apll))->child_node)->pprev: &(&(kmem_cache#29-oX (apll))->child_node)
    • //
    • // (&(kmem_cache#29-oX (fin_pll))->children)->first: &(kmem_cache#29-oX (apll))->child_node
    • //
    • // (&(kmem_cache#30-oX (apll))->hw)->clk: kmem_cache#29-oX (apll)
    • //
    • // clk_table[2]: (kmem_cache#23-o0)[2]: kmem_cache#29-oX (apll)
    • //
    • // struct clk_lookup_alloc 의 메모리를 kmem_cache#30-oX (apll) 할당 받고
    • // struct clk_lookup_alloc 맴버값 초기화 수행
    • //
    • // (kmem_cache#30-oX)->cl.clk: kmem_cache#29-oX (apll)
    • // (kmem_cache#30-oX)->con_id: "fout_apll"
    • // (kmem_cache#30-oX)->cl.con_id: (kmem_cache#30-oX)->con_id: "fout_apll"
    • //
    • // list clocks에 &(&(kmem_cache#30-oX (apll))->cl)->nade를 tail로 추가
    • //
    • // cpll, dpll, epll, rpll, ipll, spll, vpll, mpll, bpll, kpll 초기화 수행 결과는 생략.
  • samsung_clk_register_fixed_rate()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));
  • samsung_clk_register_fixed_rate() 에서 한일:
    • // exynos5420_fixed_rate_clks에 정의되어 있는 fixed rate 값들을 초기화 수행
    • //
    • // sclk_hdmiphy 의 초기화 값 수행 결과
    • // struct clk_fixed_rate 만큼 메모리를 kmem_cache#30-oX 할당 받고 struct clk_fixed_rate 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#30-oX)->fixed_rate: 24000000
    • // (kmem_cache#30-oX)->hw.init: &init
    • // (&(kmem_cache#30-oX)->hw)->clk: kmem_cache#29-oX
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX)->name: kmem_cache#30-oX ("sclk_hdmiphy")
    • // (kmem_cache#29-oX)->ops: &clk_fixed_rate_ops
    • // (kmem_cache#29-oX)->hw: &(kmem_cache#30-oX)->hw
    • // (kmem_cache#29-oX)->flags: 0x30
    • // (kmem_cache#29-oX)->num_parents: 0
    • // (kmem_cache#29-oX)->parent_names: ((void *)16)
    • // (kmem_cache#29-oX)->parent: NULL
    • // (kmem_cache#29-oX)->rate: 24000000
    • //
    • // (&(kmem_cache#29-oX)->child_node)->next: NULL
    • // (&(kmem_cache#29-oX)->child_node)->pprev: &(&(kmem_cache#29-oX)->child_node)
    • //
    • // (&clk_root_list)->first: &(kmem_cache#29-oX)->child_node
    • //
    • // clk_table[158]: (kmem_cache#23-o0)[158]: kmem_cache#29-oX
    • //
    • // struct clk_lookup_alloc 의 메모리를 kmem_cache#30-oX 할당 받고
    • // struct clk_lookup_alloc 맴버값 초기화 수행
    • //
    • // (kmem_cache#30-oX)->cl.clk: kmem_cache#29-oX
    • // (kmem_cache#30-oX)->con_id: "fin_pll"
    • // (kmem_cache#30-oX)->cl.con_id: (kmem_cache#30-oX)->con_id: "fin_pll"
    • //
    • // list clocks에 &(&(kmem_cache#30-oX)->cl)->nade를 tail로 추가
    • //
    • // "sclk_pwi", "sclk_usbh20", "mphy_refclk_ixtal24", "sclk_usbh20_scan_clk" 초기화 수행 결과는 생략.
  • samsung_clk_register_fixed_factor()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));

    // ARRAY_SIZE(exynos5420_fixed_factor_clks): 1
    samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks,
            ARRAY_SIZE(exynos5420_fixed_factor_clks));
  • samsung_clk_register_fixed_factor() 에서 한일:
    • // struct clk_fixed_factor 만큼 메모리를 kmem_cache#30-oX 할당 받고 struct clk_fixed_factor 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#30-oX)->mult: 1
    • // (kmem_cache#30-oX)->div: 2
    • // (kmem_cache#30-oX)->hw.init: &init
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX (sclk_hsic_12m) 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX (sclk_hsic_12m))->name: kmem_cache#30-oX ("sclk_hsic_12m")
    • // (kmem_cache#29-oX (sclk_hsic_12m))->ops: &clk_fixed_factor_ops
    • // (kmem_cache#29-oX (sclk_hsic_12m))->hw: &(kmem_cache#30-oX (sclk_hsic_12m))->hw
    • // (kmem_cache#29-oX (sclk_hsic_12m))->flags: 0x20
    • // (kmem_cache#29-oX (sclk_hsic_12m))->num_parents: 1
    • // (kmem_cache#29-oX (sclk_hsic_12m))->parent_names: kmem_cache#30-oX
    • // (kmem_cache#29-oX (sclk_hsic_12m))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "fin_pll"
    • // (kmem_cache#29-oX (sclk_hsic_12m))->parent: kmem_cache#29-oX (fin_pll)
    • // (kmem_cache#29-oX (sclk_hsic_12m))->rate: 12000000
    • //
    • // (&(kmem_cache#29-oX (sclk_hsic_12m))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (sclk_hsic_12m))->child_node)->pprev: &(&(kmem_cache#29-oX (sclk_hsic_12m))->child_node)
    • //
    • // (&(kmem_cache#29-oX (fin_pll))->children)->first: &(kmem_cache#29-oX (sclk_hsic_12m))->child_node
    • //
    • // (&(kmem_cache#30-oX (sclk_hsic_12m))->hw)->clk: kmem_cache#29-oX (sclk_hsic_12m)
    • //
    • // clk_table[0]: (kmem_cache#23-o0)[0]: kmem_cache#29-oX (sclk_hsic_12m)

* samsung_clk_register_mux()

```clk-exynos5420.c
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));

    // ARRAY_SIZE(exynos5420_fixed_factor_clks): 1
    samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks,
            ARRAY_SIZE(exynos5420_fixed_factor_clks));

    // ARRAY_SIZE(exynos5420_mux_clks): 85
    samsung_clk_register_mux(exynos5420_mux_clks,
            ARRAY_SIZE(exynos5420_mux_clks));
  • samsung_clk_register_mux() 에서 한일:
    • // mout_mspll_kfc, sclk_spll를 수행한 결과:
    • //
    • // (mout_mspll_kfc) 에서 한일:
      • // struct clk_mux 만큼 메모리를 kmem_cache#30-oX (mout_mspll_kfc) 할당 받고 struct clk_mux 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#30-oX)->reg: 0xf005021c
    • // (kmem_cache#30-oX)->shift: 8
    • // (kmem_cache#30-oX)->mask: 0x3
    • // (kmem_cache#30-oX)->flags: 0
    • // (kmem_cache#30-oX)->lock: &lock
    • // (kmem_cache#30-oX)->table: NULL
    • // (kmem_cache#30-oX)->hw.init: &init
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX (mout_mspll_kfc) 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX (mout_mspll_kfc))->name: kmem_cache#30-oX ("mout_mspll_kfc")
    • // (kmem_cache#29-oX (mout_mspll_kfc))->ops: &clk_mux_ops
    • // (kmem_cache#29-oX (mout_mspll_kfc))->hw: &(kmem_cache#30-oX (mout_mspll_kfc))->hw
    • // (kmem_cache#29-oX (mout_mspll_kfc))->flags: 0xa0
    • // (kmem_cache#29-oX (mout_mspll_kfc))->num_parents 4
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent_names: kmem_cache#30-oX
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "sclk_cpll"
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent_names[1]: (kmem_cache#30-oX)[1]: kmem_cache#30-oX: "sclk_dpll"
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent_names[2]: (kmem_cache#30-oX)[2]: kmem_cache#30-oX: "sclk_mpll"
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent_names[3]: (kmem_cache#30-oX)[3]: kmem_cache#30-oX: "sclk_spll"
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent: NULL
    • // (kmem_cache#29-oX (mout_mspll_kfc))->rate: 0
    • //
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parents: kmem_cache#30-oX
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parents[0...3]: (kmem_cache#30-oX)[0...3]: NULL
    • //
    • // (&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)->pprev: &(&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)
    • //
    • // (&clk_orphan_list)->first: &(kmem_cache#29-oX (mout_mspll_kfc))->child_node
    • //
    • // (&(kmem_cache#30-oX (mout_mspll_kfc))->hw)->clk: kmem_cache#29-oX (mout_mspll_kfc)
    • // (sclk_spll) 에서 한일:
      • // struct clk_mux 만큼 메모리를 kmem_cache#30-oX (sclk_spll) 할당 받고 struct clk_mux 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#30-oX)->reg: 0xf0050218
    • // (kmem_cache#30-oX)->shift: 8
    • // (kmem_cache#30-oX)->mask: 0x3
    • // (kmem_cache#30-oX)->flags: 0
    • // (kmem_cache#30-oX)->lock: &lock
    • // (kmem_cache#30-oX)->table: NULL
    • // (kmem_cache#30-oX)->hw.init: &init
    • //
    • // struct clk 만큼 메모리를 kmem_cache#29-oX (sclk_spll) 할당 받고 struct clk 의 멤버 값을 아래와 같이 초기화 수행
    • //
    • // (kmem_cache#29-oX (sclk_spll))->name: kmem_cache#30-oX ("sclk_spll")
    • // (kmem_cache#29-oX (sclk_spll))->ops: &clk_mux_ops
    • // (kmem_cache#29-oX (sclk_spll))->hw: &(kmem_cache#30-oX (sclk_spll))->hw
    • // (kmem_cache#29-oX (sclk_spll))->flags: 0xa0
    • // (kmem_cache#29-oX (sclk_spll))->num_parents 2
    • // (kmem_cache#29-oX (sclk_spll))->parent_names: kmem_cache#30-oX
    • // (kmem_cache#29-oX (sclk_spll))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "fin_pll"
    • // (kmem_cache#29-oX (sclk_spll))->parent_names[1]: (kmem_cache#30-oX)[1]: kmem_cache#30-oX: "fout_spll"
    • // (kmem_cache#29-oX (sclk_spll))->parent: NULL
    • // (kmem_cache#29-oX (sclk_spll))->rate: 600000000
    • //
    • // (kmem_cache#29-oX (sclk_spll))->parents: kmem_cache#30-oX
    • // (kmem_cache#29-oX (sclk_spll))->parents[0]: (kmem_cache#30-oX)[0]: kmem_cache#29-oX (fin_pll)
    • // (kmem_cache#29-oX (sclk_spll))->parents[1]: (kmem_cache#30-oX)[1]: kmem_cache#29-oX (fout_spll)
    • //
    • // parents 인 "fin_pll", "fout_spll" 값들 중에
    • // register CLK_SRC_TOP6 의 값을 읽어서 mux 할 parent clock 을 선택함
    • // return된 값이 선택된 parent clock의 index 값임
    • // parent clock 중에 선택된 parent clock의 이름으로 등록된 clk struct를 반환함
    • //
    • // (&(kmem_cache#29-oX (sclk_spll))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (sclk_spll))->child_node)->pprev: &(&(kmem_cache#29-oX (sclk_spll))->child_node)
    • //
    • // (&(kmem_cache#29-oX (fout_spll))->children)->first: &(kmem_cache#29-oX (sclk_spll))->child_node
    • //
    • // (&(kmem_cache#30-oX (sclk_spll))->hw)->clk: kmem_cache#29-oX (sclk_spll)
    • //
    • // orphan 으로 등록된 mout_mspll_kfc의 값을 갱신
    • // (&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)->pprev: &(&(kmem_cache#29-oX (mout_mspll_kfc))->child_node)
    • //
    • // (&(kmem_cache#29-oX (sclk_spll))->children)->first: &(kmem_cache#29-oX (mout_mspll_kfc))->child_node
    • //
    • // (kmem_cache#29-oX (mout_mspll_kfc))->parent: kmem_cache#29-oX (sclk_spll)
    • //
    • // parent가 있는지 확인후 parent의 clock rate 값으로 clock rate 값을 세팅
    • // (kmem_cache#29-oX (mout_mspll_kfc))->rate: 600000000
  • samsung_clk_register_div()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));

    // ARRAY_SIZE(exynos5420_fixed_factor_clks): 1
    samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks,
            ARRAY_SIZE(exynos5420_fixed_factor_clks));

    // ARRAY_SIZE(exynos5420_mux_clks): 85
    samsung_clk_register_mux(exynos5420_mux_clks,
            ARRAY_SIZE(exynos5420_mux_clks));

    // ARRAY_SIZE(exynos5420_div_clks): 53
    samsung_clk_register_div(exynos5420_div_clks,
            ARRAY_SIZE(exynos5420_div_clks));
  • samsung_clk_register_div() 에서 한일:
    • // exynos5420_div_clks의 div 들 중에 array index 1번의
    • // DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3) 을 가지고 분석 진행
    • //
    • // struct clk_divider 만큼 메모리를 할당 받아 맴버값 초기화 수행
    • // kmem_cache#30-oX (sclk_apll)
    • // (kmem_cache#30-oX (sclk_apll))->reg: 0xf0040500
    • // (kmem_cache#30-oX (sclk_apll))->shift: 24
    • // (kmem_cache#30-oX (sclk_apll))->width: 3
    • // (kmem_cache#30-oX (sclk_apll))->flags: 0
    • // (kmem_cache#30-oX (sclk_apll))->lock: &lock
    • // (kmem_cache#30-oX (sclk_apll))->hw.init: &init
    • // (kmem_cache#30-oX (sclk_apll))->table: NULL
    • //
    • // struct clk 만큼 메모리를 할당 받아 맴버값 초기화 수행
    • // kmem_cache#29-oX (sclk_apll)
    • // (kmem_cache#29-oX (sclk_apll))->name: kmem_cache#30-oX ("sclk_apll")
    • // (kmem_cache#29-oX (sclk_apll))->ops: &clk_divider_ops
    • // (kmem_cache#29-oX (sclk_apll))->hw: &(kmem_cache#30-oX (sclk_apll))->hw
    • // (kmem_cache#29-oX (sclk_apll))->flags: 0x0
    • // (kmem_cache#29-oX (sclk_apll))->num_parents 1
    • // (kmem_cache#29-oX (sclk_apll))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "mout_apll"
    • // (kmem_cache#29-oX (sclk_apll))->parent: kmem_cache#29-oX (mout_apll)
    • // (kmem_cache#29-oX (sclk_apll))->rate: 800000000
    • //
    • // clk 의 이름이 "mout_apll"인 메모리 값을 clk_root_list 에서 찾아 리턴 수행
    • //
    • // (&(kmem_cache#29-oX (sclk_apll))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (sclk_apll))->child_node)->pprev: &(&(kmem_cache#29-oX (sclk_apll))->child_node)
    • //
    • // (&(kmem_cache#29-oX (mout_apll))->children)->first: &(kmem_cache#29-oX (sclk_apll))->child_node
    • //
    • // exynos5420_div_clks의 idx 0, 2...52 까지 loop 수행
  • samsung_clk_register_gate 에서 한일:
    • // exynos5420_gate_clks의 gate 들 중에 array index 36번의
    • // GATE(sclk_fimd1, "sclk_fimd1", "dout_fimd1", GATE_TOP_SCLK_PERIC, 0, CLK_SET_RATE_PARENT, 0) 을 가지고 분석 진행
    • //
    • // struct clk_gate 만큼 메모리를 할당 받아 맴버값 초기화 수행
    • // kmem_cache#30-oX (sclk_fimd1)
    • // (kmem_cache#30-oX (sclk_fimd1))->reg: 0xf0050828
    • // (kmem_cache#30-oX (sclk_fimd1))->bit_idx: 0
    • // (kmem_cache#30-oX (sclk_fimd1))->flags: 0
    • // (kmem_cache#30-oX (sclk_fimd1))->lock: &lock
    • // (kmem_cache#30-oX (sclk_fimd1))->hw.init: &init
    • // (kmem_cache#30-oX (sclk_fimd1))->table: NULL
    • //
    • // struct clk 만큼 메모리를 할당 받아 맴버값 초기화 수행
    • // kmem_cache#29-oX (sclk_fimd1)
    • // (kmem_cache#29-oX (sclk_fimd1))->name: kmem_cache#30-oX ("sclk_fimd1")
    • // (kmem_cache#29-oX (sclk_fimd1))->ops: &clk_gate_ops
    • // (kmem_cache#29-oX (sclk_fimd1))->hw: &(kmem_cache#30-oX (sclk_fimd1))->hw
    • // (kmem_cache#29-oX (sclk_fimd1))->flags: 0x24
    • // (kmem_cache#29-oX (sclk_fimd1))->num_parents 1
    • // (kmem_cache#29-oX (sclk_fimd1))->parent_names[0]: (kmem_cache#30-oX)[0]: kmem_cache#30-oX: "mout_apll"
    • // (kmem_cache#29-oX (sclk_fimd1))->parent: kmem_cache#29-oX (dout_fimd0)
    • // (kmem_cache#29-oX (sclk_fimd1))->rate: 266000000
    • //
    • // clk 의 이름이 "dout_fimd1"인 메모리 값을 clk_root_list 에서 찾아 리턴 수행
    • //
    • // (&(kmem_cache#29-oX (sclk_fimd1))->child_node)->next: NULL
    • // (&(kmem_cache#29-oX (sclk_fimd1))->child_node)->pprev: &(&(kmem_cache#29-oX (sclk_fimd1))->child_node)
    • //
    • // (&(kmem_cache#29-oX (dout_fimd1))->children)->first: &(kmem_cache#29-oX (sclk_fimd1))->child_node
    • //
    • // clk_table[136]: (kmem_cache#23-o0)[136]: kmem_cache#29-oX (sclk_fimd1)
    • //
    • // exynos5420_gate_clks의 idx: 0...12...136 loop 수행
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));

    // ARRAY_SIZE(exynos5420_fixed_factor_clks): 1
    samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks,
            ARRAY_SIZE(exynos5420_fixed_factor_clks));

    // ARRAY_SIZE(exynos5420_mux_clks): 85
    samsung_clk_register_mux(exynos5420_mux_clks,
            ARRAY_SIZE(exynos5420_mux_clks));

    // ARRAY_SIZE(exynos5420_div_clks): 53
    samsung_clk_register_div(exynos5420_div_clks,
            ARRAY_SIZE(exynos5420_div_clks));

    // ARRAY_SIZE(exynos5420_gate_clks): 136
    samsung_clk_register_gate(exynos5420_gate_clks,
            ARRAY_SIZE(exynos5420_gate_clks));
  • call: start_kernel()->time_init()->of_clk_init()->exynos5420_clk_init()
    • // clk_init_cb: exynos5420_clk_init,
    • // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
    • // exynos5420_clk_init(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소)
    • clk_init_cb(np);
      • reg_base = of_iomap(np, 0);
    • samsung_clk_init()
    • samsung_clk_of_register_fixed_ext()
    • samsung_clk_register_pll()
    • samsung_clk_register_fixed_rate()
    • samsung_clk_register_fixed_factor()
    • samsung_clk_register_mux()
    • samsung_clk_register_div()
    • samsung_clk_register_gate()
static void __init exynos5420_clk_init(struct device_node *np)
{
    if (np) {
        // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소
        // of_iomap(devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, 0): 0xf0040000
        reg_base = of_iomap(np, 0); 
        // reg_base: 0xf0040000

        // reg_base: 0xf0040000
        if (!reg_base)
            panic("%s: failed to map registers\n", __func__);
    } else {
        panic("%s: unable to determine soc\n", __func__);
    }

    // np: devtree에서 allnext로 순회 하면서 찾은 clock node의 주소, reg_base: 0xf0040000, nr_clks: 769
    // ARRAY_SIZE(exynos5420_clk_regs): 59
    samsung_clk_init(np, reg_base, nr_clks,
            exynos5420_clk_regs, ARRAY_SIZE(exynos5420_clk_regs),
            NULL, 0);

    // ARRAY_SIZE(exynos5420_fixed_rate_ext_clks): 1
    samsung_clk_of_register_fixed_ext(exynos5420_fixed_rate_ext_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_ext_clks),
            ext_clk_match);

    // ARRAY_SIZE(exynos5420_plls): 11, reg_base: 0xf0040000
    samsung_clk_register_pll(exynos5420_plls, ARRAY_SIZE(exynos5420_plls),
                    reg_base);

    // ARRAY_SIZE(exynos5420_fixed_rate_clks): 5
    samsung_clk_register_fixed_rate(exynos5420_fixed_rate_clks,
            ARRAY_SIZE(exynos5420_fixed_rate_clks));

    // ARRAY_SIZE(exynos5420_fixed_factor_clks): 1
    samsung_clk_register_fixed_factor(exynos5420_fixed_factor_clks,
            ARRAY_SIZE(exynos5420_fixed_factor_clks));

    // ARRAY_SIZE(exynos5420_mux_clks): 85
    samsung_clk_register_mux(exynos5420_mux_clks,
            ARRAY_SIZE(exynos5420_mux_clks));

    // ARRAY_SIZE(exynos5420_div_clks): 53
    samsung_clk_register_div(exynos5420_div_clks,
            ARRAY_SIZE(exynos5420_div_clks));

    // ARRAY_SIZE(exynos5420_gate_clks): 136
    samsung_clk_register_gate(exynos5420_gate_clks,
            ARRAY_SIZE(exynos5420_gate_clks));
}

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
    mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
  • 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()
    • mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
      • ->exynos_mct.c::mct_init_dt()->irq_of_parse_and_map()

irq.c::irq_of_parse_and_map()

  • 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()
      • exynos_mct.c::mct_init_dt()->irq_of_parse_and_map()
// ARM10C 20150307
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_G0_IRQ: 0
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
    struct of_phandle_args oirq;

    // dev: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, index: 0
    // of_irq_parse_one(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0, &oirq): 0
    if (of_irq_parse_one(dev, index, &oirq))
        return 0;
  • exynos5420 device tree 에 있는 mct 장치 정보
    mct@101C0000 {
        compatible = "samsung,exynos4210-mct";
        reg = <0x101C0000 0x800>;
        interrupt-controller;
        #interrups-cells = <1>;
        interrupt-parent = <&mct_map>;
        interrupts = <0>, <1>, <2>, <3>, <4>, <5>, <6>, <7>;
        clocks = <&clock 1>, <&clock 315>;
        clock-names = "fin_pll", "mct";

        mct_map: mct-map {
            #interrupt-cells = <1>;
            #address-cells = <0>;
            #size-cells = <0>;
            interrupt-map = <0 &combiner 23 3>,
                    <1 &combiner 23 4>,
                    <2 &combiner 25 2>,
                    <3 &combiner 25 3>,
                    <4 &gic 0 120 0>,
                    <5 &gic 0 121 0>,
                    <6 &gic 0 122 0>,
                    <7 &gic 0 123 0>;
        };
    };
  • 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()->irq_of_parse_and_map()
    • if (of_irq_parse_one(dev, index, &oirq))
      • irq.c::irq_of_parse_and_map()->of_irq_parse_one()

irq.c::of_irq_parse_one()

  • 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()
      • exynos_mct.c::mct_init_dt()->irq_of_parse_and_map()
        • irq.c::irq_of_parse_and_map()->of_irq_parse_one()
// ARM10C 20150307
// dev: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, index: 0, &oirq
int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{
    struct device_node *p;
    const __be32 *intspec, *tmp, *addr;
    u32 intsize, intlen;

    // EINVAL: 23
    int i, res = -EINVAL;
    // res: -23

    // device: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_node_full_name(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소):
    // "mct@101C0000", index: 0
    pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
    // "of_irq_parse_one: dev=mct@101C0000, index=0\n"

    /* OldWorld mac stuff is "special", handle out of line */
    // of_irq_workarounds: 0, OF_IMAP_OLDWORLD_MAC: 0x00000001
    if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
        return of_irq_parse_oldworld(device, index, out_irq);

    /* Get the reg property (if any) */
    // device: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_get_property(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "reg", NULL):
    // mct node의reg의 property의 값의 주소
    addr = of_get_property(device, "reg", NULL);
    // addr: mct node의reg의 property의 값의 주소

    /* Get the interrupts property */
    // device: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_get_property(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "interrups", &intlen):
    // mct node의 rinterrupts의 property의 값의 주소
    intspec = of_get_property(device, "interrupts", &intlen);
    // intspec: mct node의 interrupts의 property의 값의 주소, intlen: 32

    // intspec: mct node의 interrupts의 property의 값의 주소
    if (intspec == NULL) {
        /* Try the new-style interrupts-extended */
        res = of_parse_phandle_with_args(device, "interrupts-extended",
                        "#interrupt-cells", index, out_irq);
        if (res)
            return -EINVAL;
        return of_irq_parse_raw(addr, out_irq);
    }

    // intspec: mct node의 interrupts의 property의 값의 주소, intlen: 32
    // intlen: 32, sizeof(*mct node의 interrupts의 property의 값의 주소): 4
    intlen /= sizeof(*intspec);
    // intlen: 8

    // intspec: mct node의 interrupts의 property의 값의 주소, intlen: 32
    // be32_to_cpup(combiner node의 interrupts의 property의 값의 주소): 0, intlen: 8
    pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
    // " intspec=0 intlen=8\n"

    /* Look for the interrupt parent. */
    // device: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
    // of_irq_find_parent(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소)
    // mct_map node의 주소
    p = of_irq_find_parent(device);
    // p: mct_map node의 주소

    // p: mct_map node의 주소
    if (p == NULL)
        return -EINVAL;

    /* Get size of interrupt specifier */
    // p: mct_map node의 주소
    // of_get_property(mct_map node의 주소 "#interrupt-cells", NULL):
    // mct_map node의 주소 #interrupt-cells의 property의 값의 주소
    tmp = of_get_property(p, "#interrupt-cells", NULL);
    // tmp: mct_map node의 주소 #interrupt-cells의 property의 값의 주소

    // tmp: mct_map node의 주소 #interrupt-cells의 property의 값의 주소
    if (tmp == NULL)
        goto out;

    // tmp: mct_map node의 주소 #interrupt-cells의 property의 값의 주소
    // be32_to_cpu(*(mct_map node의 주소 #interrupt-cells의 property의 값의 주소)): 1
    intsize = be32_to_cpu(*tmp);
    // intsize: 1

    // intsize: 1, intlen: 8
    pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
    // " intsize=1 intlen=8\n"

    /* Check index */
    // index: 0, intsize: 1, intlen: 8
    if ((index + 1) * intsize > intlen)
        goto out;

    /* Copy intspec into irq structure */
    // intspec: mct node의 interrupts의 property의 값의 주소, index: 0, intsize: 1
    intspec += index * intsize;
    // intspec: mct node의 interrupts의 property의 값의 주소

    // out_irq->np: (&oirq)->np, p: mct_map node의 주소
    out_irq->np = p;
    // out_irq->np: (&oirq)->np: mct_map node의 주소

    // out_irq->args_count: (&oirq)->args_count, intsize: 1
    out_irq->args_count = intsize;
    // out_irq->args_count: (&oirq)->args_count: 1

    // intsize: 1
    for (i = 0; i < intsize; i++)
        // i: 0, out_irq->args[0]: (&oirq)->args[0], intspec: mct node의 interrupts의 property의 값의 주소
        // be32_to_cpup(mct node의 interrupts의 property의 값의 주소): 0
        out_irq->args[i] = be32_to_cpup(intspec++);
        // out_irq->args[0]: (&oirq)->args[0]: 0, intspec: mct node의 interrupts의 property의 값의 주소 + 1

    /* Check if there are any interrupt-map translations to process */
    // addr: mct node의reg의 property의 값의 주소
    res = of_irq_parse_raw(addr, out_irq);
  • 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()->irq_of_parse_and_map()
    • res = of_irq_parse_raw(addr, out_irq);
      • ->irq.c::irq_of_parse_and_map()->of_irq_parse_one()
        • ->irq.c::of_irq_parse_raw()

irq.c::of_irq_parse_raw()

  • 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()
      • exynos_mct.c::mct_init_dt()->irq_of_parse_and_map()
    • res = of_irq_parse_raw(addr, out_irq);
      • ->irq.c::irq_of_parse_and_map()->of_irq_parse_one()
        • ->irq.c::of_irq_parse_raw()
// ARM10C 20150307
// addr: mct node의 reg의 property의 값의 주소, out_irq: &oirq
int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
{
    struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
    // old: NULL, newpar: NULL

    // MAX_PHANDLE_ARGS: 8
    __be32 initial_match_array[MAX_PHANDLE_ARGS];
    const __be32 *match_array = initial_match_array;
    // match_array: initial_match_array

    // MAX_PHANDLE_ARGS: 8
    const __be32 *tmp, *imap, *imask, dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
    // dummy_imask[0...7]: 0xffffffff

    u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
    // intsize: 1, newintsize: 0, newaddrsize: 0

    int imaplen, match, i;

#ifdef DEBUG // undefined
    of_print_phandle_args("of_irq_parse_raw: ", out_irq);
#endif

    // out_irq->np: (&oirq)->np: mct_map node의 주소
    // of_node_get((&oirq)->np): mct_map node의 주소
    ipar = of_node_get(out_irq->np);
    // ipar: mct_map node의 주소

    /* First get the #interrupt-cells property of the current cursor
     * that tells us how to interpret the passed-in intspec. If there
     * is none, we are nice and just walk up the tree
     */
    do {
        // ipar: mct_map node의 주소
        // of_get_property(mct_map node의 주소, "#interrupt-cells", NULL):
        // mct_map node의 #interrupt-cells의 property 값의 주소
        tmp = of_get_property(ipar, "#interrupt-cells", NULL);
        // tmp: mct_map node의 #interrupt-cells의 property 값의 주소

        // tmp: mct_map node의 #interrupt-cells의 property 값의 주소
        if (tmp != NULL) {
            // tmp: mct_map node의 #interrupt-cells의 property 값의 주소
            // be32_to_cpu(*(mct_map node의 #interrupt-cells의 property 값의 주소)): 1
            intsize = be32_to_cpu(*tmp);
            // intsize: 1
            break;
            // break 수행
        }
        tnode = ipar;
        ipar = of_irq_find_parent(ipar);
        of_node_put(tnode);
    } while (ipar);

    // ipar: mct_map node의 주소
    if (ipar == NULL) {
        pr_debug(" -> no parent found !\n");
        goto fail;
    }

    // ipar: mct_map node의 주소
    // of_node_full_name(mct_map node의 주소): "mct-map", intsize: 1
    pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);
    // "of_irq_parse_raw: ipar=mct-map, size=1\n"

    // out_irq->args_count: (&oirq)->args_count: 1, intsize: 1
    if (out_irq->args_count != intsize)
        return -EINVAL;

    /* Look for this #address-cells. We have to implement the old linux
     * trick of looking for the parent here as some device-trees rely on it
     */
    // ipar: mct_map node의 주소, of_node_get(mct_map node의 주소): mct_map node의 주소
    old = of_node_get(ipar);
    // old: mct_map node의 주소

    do {
        // old: mct_map node의 주소,
        // of_get_property(mct_map node의 주소, "#address-cells", NULL): mct_map node의 #address-cells의 property 값의 주소
        tmp = of_get_property(old, "#address-cells", NULL);
        // tmp: mct_map node의 #address-cells의 property 값의 주소

        // FIXME:
        // of_get_parent(mct_map node의 주소): XXX
        // of_get_parent(mct_map node의 주소)의 값은 확인 필요,
        // 코드 분석상 확인 할수 없는 상태라 XXX 로 써놓고 분석

        // old: mct_map node의 주소, of_get_parent(mct_map node의 주소): XXX
        tnode = of_get_parent(old);
        // tnode: XXX

        // old: mct_map node의 주소
        of_node_put(old); // null function

        // tnode: XXX
        old = tnode;
        // old: XXX

        // old: XXX, tmp: mct_map node의 #address-cells의 property 값의 주소
    } while (old && tmp == NULL);

    // old: XXX
    of_node_put(old); // null function

    // old: XXX
    old = NULL;
    // old: NULL

    // tmp: mct_map node의 #address-cells의 property 값의 주소
    // be32_to_cpu(*(mct_map node의 #address-cells의 property 값의 주소)): 0
    addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);
    // addrsize: 0

    // addrsize: 0
    pr_debug(" -> addrsize=%d\n", addrsize);
    // " -> addrsize=0\n"

    /* Range check so that the temporary buffer doesn't overflow */
    // addrsize: 0, intsize: 1, MAX_PHANDLE_ARGS: 8
    if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS))
        goto fail;

    /* Precalculate the match array - this simplifies match loop */
    // addrsize: 0
    for (i = 0; i < addrsize; i++)
        // i: 0, initial_match_array[0], addr: reg의 property의 값의 주소
        // addr[0]: (reg의 property의 값의 주소)[0]: 0x10440000
        initial_match_array[i] = addr ? addr[i] : 0;
        // initial_match_array[0]: 0x10440000

    // intsize: 1
    for (i = 0; i < intsize; i++)
        // i: 0, addrsize: 0, initial_match_array[0],
        // out_irq->args[0]: (&oirq)->args[0], cpu_to_be32((&oirq)->args[0]): 0
        initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);
        // initial_match_array[0]: 0

    /* Now start the actual "proper" walk of the interrupt tree */
    // ipar: mct_map node의 주소
    while (ipar != NULL) {
        /* Now check if cursor is an interrupt-controller and if it is
         * then we are done
         */
        // ipar: mct_map node의 주소
        // of_get_property(mct_map node의 주소, "interrupt-controller", NULL): NULL
        // ipar: exynos5420 dtb상의 combiner node의 주소
        // of_get_property(exynos5420 dtb상의 combiner node의 주소, "interrupt-controller", NULL): NULL 아닌값
        if (of_get_property(ipar, "interrupt-controller", NULL) !=
                NULL) {
            pr_debug(" -> got it !\n");
            // " -> got it !\n"

            return 0;
            // return 0
        }

        /*
         * interrupt-map parsing does not work without a reg
         * property when #address-cells != 0
         */
        // addrsize: 0, addr: mct node의 reg의 property의 값의 주소
        if (addrsize && !addr) {
            pr_debug(" -> no reg passed in when needed !\n");
            goto fail;
        }

        /* Now look for an interrupt-map */
        // ipar: mct_map node의 주소
        // of_get_property(mct_map node의 주소, "interrupt-map", &imaplen):
        // mct_map node의 interrupt-map의 property 값의 주소, imaplen: 128
        imap = of_get_property(ipar, "interrupt-map", &imaplen);
        // imap: mct_map node의 interrupt-map의 property 값의 주소

        /* No interrupt map, check for an interrupt parent */
        // imap: mct_map node의 interrupt-map의 property 값의 주소
        if (imap == NULL) {
            pr_debug(" -> no map, getting parent\n");
            newpar = of_irq_find_parent(ipar);
            goto skiplevel;
        }

        // imaplen: 128, sizeof(u32): 4
        imaplen /= sizeof(u32);
        // imaplen: 32

        /* Look for a mask */
        // ipar: mct_map node의 주소
        // of_get_property(mct_map node의 주소, "interrupt-map-mask", NULL): NULL
        imask = of_get_property(ipar, "interrupt-map-mask", NULL);
        // imask: NULL

        // imask: NULL
        if (!imask)
            imask = dummy_imask;
            // imask: &dummy_imask

        /* Parse interrupt-map */
        match = 0;
        // match: 0

        // imaplen: 32, addrsize: 0, intsize: 1, match: 0
        while (imaplen > (addrsize + intsize + 1) && !match) {
            /* Compare specifiers */
            match = 1;
            // match: 1

            // addrsize: 0, intsize: 1, imaplen: 32
            for (i = 0; i < (addrsize + intsize); i++, imaplen--)
                // match: 1
                // i: 0, match_array[0]: initial_match_array[0]: 0,
                // *imap: *(mct_map node의 interrupt-map의 property 값의 주소): 0
                // imask[0]: dummy_imask[0]: 0xffffffff
                match &= !((match_array[i] ^ *imap++) & imask[i]);
                // match: 1, imap: mct_map node의 interrupt-map의 property 값의 주소+4

            // match: 1, imaplen: 31,
            pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
            // " -> match=1 (imaplen=31)\n"

            /* Get the interrupt parent */
            // of_irq_workarounds: 0, OF_IMAP_NO_PHANDLE: 0x00000002
            if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
                newpar = of_node_get(of_irq_dflt_pic);
            else
                // imap: mct_map node의 interrupt-map의 property 값의 주소+4
                // be32_to_cpup(mct_map node의 interrupt-map의 property 값의 주소+4):
                // mct_map node의 interrupt-map의 property 값의 주소+4
                // of_find_node_by_phandle(mct_map node의 interrupt-map의 property 값의 주소+4):
                // exynos5420 dtb상의 combiner node의 주소
                newpar = of_find_node_by_phandle(be32_to_cpup(imap));
                // newpar: exynos5420 dtb상의 combiner node의 주소

            // imap: mct_map node의 interrupt-map의 property 값의 주소+4
            imap++;
            // imap: mct_map node의 interrupt-map의 property 값의 주소+8

            // imaplen: 31
            --imaplen;
            // imaplen: 30

            /* Check if not found */
            // newpar: exynos5420 dtb상의 combiner node의 주소
            if (newpar == NULL) {
                pr_debug(" -> imap parent not found !\n");
                goto fail;
            }

            /* Get #interrupt-cells and #address-cells of new
             * parent
             */
            // newpar: exynos5420 dtb상의 combiner node의 주소
            // of_get_property(exynos5420 dtb상의 combiner node의 주소, "#interrupt-cells", NULL):
            // combiner node의 #interrupt-cells의 property 값의 주소
            tmp = of_get_property(newpar, "#interrupt-cells", NULL);
            // tmp: combiner node의 #interrupt-cells의 property 값의 주소

            // tmp: combiner node의 #interrupt-cells의 property 값의 주소
            if (tmp == NULL) {
                pr_debug(" -> parent lacks #interrupt-cells!\n");
                goto fail;
            }

            // tmp: combiner node의 #interrupt-cells의 property 값의 주소
            // be32_to_cpu(*(combiner node의 #interrupt-cells의 property 값의 주소)): 2
            newintsize = be32_to_cpu(*tmp);
            // newintsize: 2

            // newpar: exynos5420 dtb상의 combiner node의 주소
            // of_get_property(exynos5420 dtb상의 combiner node의 주소, "#address-cells", NULL): NULL
            tmp = of_get_property(newpar, "#address-cells", NULL);
            // tmp: NULL

            // tmp: NULL
            newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp);
            // newaddrsize: 0

            // newintsize: 2, newaddrsize: 0
            pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
                newintsize, newaddrsize);
            // " -> newintsize=2, newaddrsize=0\n"

            /* Check for malformed properties */
            // newintsize: 2, newaddrsize: 0, MAX_PHANDLE_ARGS: 8
            if (WARN_ON(newaddrsize + newintsize > MAX_PHANDLE_ARGS))
                goto fail;

            // imaplen: 30, newintsize: 2, newaddrsize: 0
            if (imaplen < (newaddrsize + newintsize))
                goto fail;

            // imap: mct_map node의 interrupt-map의 property 값의 주소+8,
            // newintsize: 2, newaddrsize: 0
            imap += newaddrsize + newintsize;
            // imap: mct_map node의 interrupt-map의 property 값의 주소+16

            // imaplen: 30, newintsize: 2, newaddrsize: 0
            imaplen -= newaddrsize + newintsize;
            // imaplen: 28

            // imaplen: 28
            pr_debug(" -> imaplen=%d\n", imaplen);
            // " -> imaplen=28\n"

            // imaplen: 28, addrsize: 0, intsize: 1, match: 1
        }

        // match: 1
        if (!match)
            goto fail;

        /*
         * Successfully parsed an interrrupt-map translation; copy new
         * interrupt specifier into the out_irq structure
         */
        // out_irq->np: (&oirq)->np, newpar: exynos5420 dtb상의 combiner node의 주소
        out_irq->np = newpar;
        // out_irq->np: (&oirq)->np: exynos5420 dtb상의 combiner node의 주소

        // imap: mct_map node의 interrupt-map의 property 값의 주소+16, newaddrsize: 0, newintsize: 2
        match_array = imap - newaddrsize - newintsize;
        // match_array: mct_map node의 interrupt-map의 property 값의 주소+8

        // newintsize: 2
        for (i = 0; i < newintsize; i++)
            // i: 0, out_irq->args[0]: (&oirq)->args[0],
            // imap: mct_map node의 interrupt-map의 property 값의 주소+16, newintsize: 2
            // be32_to_cpup(mct_map node의 interrupt-map의 property 값의 주소+8): 23
            // i: 1, out_irq->args[1]: (&oirq)->args[1],
            // imap: mct_map node의 interrupt-map의 property 값의 주소+16, newintsize: 2
            // be32_to_cpup(mct_map node의 interrupt-map의 property 값의 주소+12): 3
            out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
            // out_irq->args[0]: (&oirq)->args[0]: 23
            // out_irq->args[1]: (&oirq)->args[1]: 3

        // out_irq->args_count: (&oirq)->args_count, intsize: 1, newintsize: 2
        out_irq->args_count = intsize = newintsize;
        // out_irq->args_count: (&oirq)->args_count: 2, intsize: 2

        // addrsize: 0, newaddrsize: 0
        addrsize = newaddrsize;
        // addrsize: 0

    skiplevel:
        /* Iterate again with new parent */
        // newpar: exynos5420 dtb상의 combiner node의 주소
        // of_node_full_name(exynos5420 dtb상의 combiner node의 주소): interrupt-controller@10440000
        pr_debug(" -> new parent: %s\n", of_node_full_name(newpar));
        // " -> new parent: interrupt-controller@10440000\n"

        // ipar: mct_map node의 주소
        of_node_put(ipar); // null function

        // ipar: mct_map node의 주소, newpar: exynos5420 dtb상의 combiner node의 주소
        ipar = newpar;
        // ipar: exynos5420 dtb상의 combiner node의 주소

        newpar = NULL;
        // newpar: NULL
    }
 fail:
    of_node_put(ipar);
    of_node_put(newpar);

    return -EINVAL;
}
EXPORT_SYMBOL_GPL(of_irq_parse_raw);
...
    // type: 0, IRQ_TYPE_NONE: 0x00000000, virq: 32, irq_get_trigger_type(32): 0
    if (type != IRQ_TYPE_NONE &&
        type != irq_get_trigger_type(virq))
        irq_set_irq_type(virq, type);
  • call::irq_get_trigger_type()

irq.h::irq_get_trigger_type()

// ARM10C 20141213
// virq: 32
static inline u32 irq_get_trigger_type(unsigned int irq)
{
    // irq: 32, irq_get_irq_data(32): &(kmem_cache#28-oX (irq 32))->irq_data
    struct irq_data *d = irq_get_irq_data(irq);
    // d: &(kmem_cache#28-oX (irq 32))->irq_data

    // d: &(kmem_cache#28-oX (irq 32))->irq_data
    // irqd_get_trigger_type(&(kmem_cache#28-oX (irq 32))->irq_data): 0
    return d ? irqd_get_trigger_type(d) : 0;
    // return 0
}
  • irq_get_irq_data()
struct irq_data *irq_get_irq_data(unsigned int irq)
{
    // irq: 16, irq_to_desc(16): kmem_cache#28-oX (irq 16)
    struct irq_desc *desc = irq_to_desc(irq);
    // desc: kmem_cache#28-oX (irq 16)

    // desc: kmem_cache#28-oX (irq 16)
    // &desc->irq_data: &(kmem_cache#28-oX (irq 16))->irq_data
    return desc ? &desc->irq_data : NULL;
    // return &(kmem_cache#28-oX (irq 16))->irq_data
}
EXPORT_SYMBOL_GPL(irq_get_irq_data);
// ARM10C 20141122
// irq: 16
struct irq_desc *irq_to_desc(unsigned int irq)
{
    // irq: 16, radix_tree_lookup(&irq_desc_tree, 16): kmem_cache#28-oX (irq 16)
    return radix_tree_lookup(&irq_desc_tree, irq);
    // return kmem_cache#28-oX (irq 16)
}
EXPORT_SYMBOL(irq_to_desc);
  • chip.c::irq_get_irq_data()
// ARM10C 20141213
// virq: 160
struct irq_data *irq_get_irq_data(unsigned int irq)
{
    // irq: 16, irq_to_desc(16): kmem_cache#28-oX (irq 16)
    struct irq_desc *desc = irq_to_desc(irq);
    // desc: kmem_cache#28-oX (irq 16)

    // desc: kmem_cache#28-oX (irq 16)
    // &desc->irq_data: &(kmem_cache#28-oX (irq 16))->irq_data
    return desc ? &desc->irq_data : NULL;
    // return &(kmem_cache#28-oX (irq 16))->irq_data
}
EXPORT_SYMBOL_GPL(irq_get_irq_data);
  • irq.h::irq_get_trigger_type()
// ARM10C 20141213
// virq: 32
static inline u32 irq_get_trigger_type(unsigned int irq)
{
    // irq: 32, irq_get_irq_data(32): &(kmem_cache#28-oX (irq 32))->irq_data
    struct irq_data *d = irq_get_irq_data(irq);
    // d: &(kmem_cache#28-oX (irq 32))->irq_data

    // d: &(kmem_cache#28-oX (irq 32))->irq_data
    // irqd_get_trigger_type(&(kmem_cache#28-oX (irq 32))->irq_data): 0
    return d ? irqd_get_trigger_type(d) : 0;
    // return 0
}
  • irqd_get_trigger_type()
// ARM10C 20141213
// d: &(kmem_cache#28-oX (irq 32))->irq_data
static inline u32 irqd_get_trigger_type(struct irq_data *d)
{
    // d->state_use_accessors: (&(kmem_cache#28-oX (irq 32))->irq_data)->state_use_accessors: 0x10800
    // IRQD_TRIGGER_MASK: 0xf
    return d->state_use_accessors & IRQD_TRIGGER_MASK;
    // return 0
}
  • irqdomain.c::
    /* Set type if specified and different than the current one */
    // type: 0, IRQ_TYPE_NONE: 0x00000000, virq: 32, irq_get_trigger_type(32): 0
    if (type != IRQ_TYPE_NONE &&
        type != irq_get_trigger_type(virq))
        irq_set_irq_type(virq, type);
    // virq: 32
    return virq;
    // return 32
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);

irq.c::irq_of_parse_and_map()

// ARM10C 20150307
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_G0_IRQ: 0
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
{
    struct of_phandle_args oirq;

    // dev: devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, index: 0
    // of_irq_parse_one(devtree에서 allnext로 순회 하면서 찾은 combiner node의 주소, 0, &oirq): 0
    // dev: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, index: 0
    // of_irq_parse_one(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0, &oirq): 0
    if (of_irq_parse_one(dev, index, &oirq))
        return 0;

    // of_irq_parse_one(0)에서 한일:
    // (&oirq)->np: gic node의 주소
    // (&oirq)->args_count: 3
    // (&oirq)->args[0]: 0
    // (&oirq)->args[1]: 0
    // (&oirq)->args[2]: 0

    // irq_create_of_mapping(&oriq): 32
    return irq_create_of_mapping(&oirq);
    // return 32
}
EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
  • return: exynos_mct.c::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
    mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);

    /*
     * 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
    nr_irqs = of_irq_count(np);
#else
    nr_irqs = 0;
#endif
    for (i = MCT_L0_IRQ; i < nr_irqs; i++)
        mct_irqs[i] = irq_of_parse_and_map(np, i);
  • irq_of_parse_and_map()에서 한일
...
  • exynos_mct.c::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
    mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);

    /*
     * 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
    nr_irqs = of_irq_count(np);
#else
    nr_irqs = 0;
#endif
    for (i = MCT_L0_IRQ; i < nr_irqs; i++)
        mct_irqs[i] = irq_of_parse_and_map(np, i);

    exynos4_timer_resources(np, of_iomap(np, 0));
    exynos4_clocksource_init();
    exynos4_clockevent_init();
}

log

   2c73f62..ad03f3b  master     -> origin/master
Updating 2c73f62..ad03f3b
Fast-forward
arch/arm/include/asm/div64.h     |   2 +
arch/arm/include/asm/irqflags.h  |   1 +
drivers/clocksource/exynos_mct.c |   3 +
include/asm-generic/percpu.h     |  10 ++++
include/linux/clockchips.h       |  16 ++++++
include/linux/cpumask.h          |   6 ++
include/linux/irqflags.h         |   2 +
include/linux/jump_label.h       |   2 +-
include/linux/kernel.h           |   1 +
include/linux/list.h             |   2 +
include/linux/module.h           |  31 ++++++-----
include/linux/preempt.h          |   1 +
include/linux/smp.h              |   1 +
include/linux/spinlock_types.h   |  44 +++++++++++++++
include/linux/tick.h             |   2 +
include/linux/time.h             |   1 +
kernel/module.c                  |   7 +++
kernel/time/clockevents.c        | 117 +++++++++++++++++++++++++++++++++++++++
kernel/time/clocksource.c        |  26 +++++++++
kernel/time/tick-broadcast.c     |   5 ++
kernel/time/tick-common.c        |  40 +++++++++++++
21 files changed, 306 insertions(+), 14 deletions(-)