2015년 5월 30일 토요일

[Linux Kernel] 103주차(2015.05.30)

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

103 주차 진도

1  1 start_kernel        1  ~/kernel/iamroot/linux-stable/init/main.c
2  1 time_init         743  ~/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      1420  return mct_init_dt(np, MCT_INT_SPI);
6  1 exynos4_clocksource_init  1410  exynos4_clocksource_init();
7  1 exynos4_mct_frc_start   425  exynos4_mct_frc_start(0, 0)

main.c::main.c()

  • called: start_kernel()->time_init()
asmlinkage void __init start_kernel(void)
{
...
        sched_clock_postinit();

sched_clock.c::sched_clockinit()

  • call: start_kernel()->sched_clock_postinit()
void __init sched_clock_postinit(void)
{
    /*
     * If no sched_clock function has been provided at that point,
     * make it the final one one.
     */
    if (read_sched_clock == jiffy_sched_clock_read)
        sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);
  • call: start_kernel()->sched_clock_postinit()->sched_clock_register()

sched_clock.c::sched_clock_register()

  • called: start_kernel()->sched_clock_postinit()->sched_clock_register()
void __init sched_clock_register(u64 (*read)(void), int bits,
                 unsigned long rate)
{
    unsigned long r;
    u64 res, wrap;
    char r_unit;

    // cd.rate: 0, rate: 100
    if (cd.rate > rate)
        return;

    // irqs_disabled(): 1
    WARN_ON(!irqs_disabled());
    // read_sched_clock: read: jiffy_sched_clock_read
    read_sched_clock = read;
    // sched_clock_mask: CLOCKSOURCE_MASK(bits: 32):
    // #define CLOCKSOURCE_MASK(bits) (cycle_t)((bits) < 64 ? ((1ULL<<(bits))-1) : -1)
    // sched_clock_mask: 0xffffffff
    sched_clock_mask = CLOCKSOURCE_MASK(bits);
    // cd.rate: rate: 100
    cd.rate = rate;

    /* calculate the mult/shift to convert counter ticks to ns. */
    clocks_calc_mult_shift(&cd.mult, &cd.shift, rate, NSEC_PER_SEC, 3600);
    // clock_calc_mult_shift():
    // (&cd)->mult: 0x98968000
    // (&cd)->shift: 8

    r = rate;
    if (r >= 4000000) {
        r /= 1000000;
        r_unit = 'M';
    } else if (r >= 1000) {
        r /= 1000;
        r_unit = 'k';
    } else
        r_unit = ' ';

    /* calculate how many ns until we wrap */
    wrap = clocks_calc_max_nsecs(cd.mult, cd.shift, 0, sched_clock_mask);
    //

clocksource.cc::clocks_calc_max_nsecs()

u64 clocks_calc_max_nsecs(u32 mult, u32 shift, u32 maxadj, u64 mask)
{
    u64 max_nsecs, max_cycles;

    /*
     * Calculate the maximum number of cycles that we can pass to the
     * cyc2ns function without overflowing a 64-bit signed result. The
     * maximum number of cycles is equal to ULLONG_MAX/(mult+maxadj)
     * which is equivalent to the below.
     * max_cycles < (2^63)/(mult + maxadj)
     * max_cycles < 2^(log2((2^63)/(mult + maxadj)))
     * max_cycles < 2^(log2(2^63) - log2(mult + maxadj))
     * max_cycles < 2^(63 - log2(mult + maxadj))
     * max_cycles < 1 << (63 - log2(mult + maxadj))
     * Please note that we add 1 to the result of the log2 to account for
     * any rounding errors, ensure the above inequality is satisfied and
     * no overflow will occur.
     */
    // mult: 0x98968000, maxadj: 0, ilog2(0x98968000): 31   
    max_cycles = 1ULL << (63 - (ilog2(mult + maxadj) + 1));
    // max_cycles: 0x80000000   

    /*
     * The actual maximum number of cycles we can defer the clocksource is
     * determined by the minimum of max_cycles and mask.
     * Note: Here we subtract the maxadj to make sure we don't sleep for
     * too long if there's a large negative adjustment.
     */
    // max_cycles: 0x80000000, 0xFFFFFFFF   
    max_cycles = min(max_cycles, mask);
    // max_cycles: 0x80000000   

    // max_cycles: 0x80000000, mult: 0x98968000, maxadj: 0x0, shift: 8,
    // clocksource_cyc2ns(0x80000000, 0x98968000, 8): 0x4c4b4000 000000
    max_nsecs = clocksource_cyc2ns(max_cycles, mult - maxadj, shift);
    // max_nsecs: 0x4c4b400000000 

    // max_nsecs: 0x4c4b400000000 
    return max_nsecs;
    // return 0x4c4b400000000 
}

sched_clock.c::

// ARM10C 20150530
// jiffy_sched_clock_read, BITS_PER_LONG: 32, HZ: 100
void __init sched_clock_register(u64 (*read)(void), int bits,
                 unsigned long rate)
{
...
    /* calculate how many ns until we wrap */
    wrap = clocks_calc_max_nsecs(cd.mult, cd.shift, 0, sched_clock_mask);
    // wrap: 0x4c4b400000000

    // wrap: 0x4c4b400000000, 0x9896800000000
    // ns_to_ktime(0x42c1d800000000)
    cd.wrap_kt = ns_to_ktime(wrap - (wrap >> 3));

ktime.h::ns_to_ktime()

static inline ktime_t ns_to_ktime(u64 ns)
{
    static const ktime_t ktime_zero = { .tv64 = 0 };
    // ktime_zero.tv64: 0

    // ktime_zero: 0x1000000000000000
    // ns: 0x42c1d800000000 
    // ktime_add_ns(ktime_zero, 0x42c1d800000000):
    // ({ (ktime_t){ .tv64 = (ktime_zero).tv64 + (0x42c1d800000000) }; })

    return ktime_add_ns(ktime_zero, ns);
    // return 0x42c1d8389aca00
}

sched_clock.c::sched_clock_register()

// ARM10C 20150530
// jiffy_sched_clock_read, BITS_PER_LONG: 32, HZ: 100
void __init sched_clock_register(u64 (*read)(void), int bits,
                 unsigned long rate)
{
...
    /* calculate how many ns until we wrap */
    wrap = clocks_calc_max_nsecs(cd.mult, cd.shift, 0, sched_clock_mask);
    // wrap: 0x4c4b400000000

    // wrap: 0x4c4b400000000, 0x9896800000000
    // ns_to_ktime(0x42c1d800000000)
    cd.wrap_kt = ns_to_ktime(wrap - (wrap >> 3));
    // cd.wrap_kt: 0x42c1d8389aca00

    /* calculate the ns resolution of this counter */
    res = cyc_to_ns(1ULL, cd.mult, cd.shift);

sched_clock.c::cyc_to_ns()

static inline u64 notrace cyc_to_ns(u64 cyc, u32 mult, u32 shift)
{
    // cyc: 1, mult: 0x98968000, shift: 8
    return (cyc * mult) >> shift;
    // return 0x989860
}

sched_clock.c::sched_clock_register()

  • return 0x989860
// ARM10C 20150530
// jiffy_sched_clock_read, BITS_PER_LONG: 32, HZ: 100
void __init sched_clock_register(u64 (*read)(void), int bits,
                 unsigned long rate)
{
...
    /* calculate the ns resolution of this counter */
    res = cyc_to_ns(1ULL, cd.mult, cd.shift);
    // res: 0x989860
    pr_info("sched_clock: %u bits at %lu%cHz, resolution %lluns, wraps every %lluns\n",
        bits, r, r_unit, res, wrap);
    // bits:32, r: 100, r_unit: ' ' res: 989680, wrap: 0x4c48400000000
    // sched_clock: 0x20 bits at 100 hz resolution 10000000ns wraps every 214748364800000ns"

    update_sched_clock();

sched_clock.c::update_sched_clock()

static void notrace update_sched_clock(void)
{
    unsigned long flags;
    u64 cyc;
    u64 ns;

    // read_sched_clock(): 0
    cyc = read_sched_clock();
    // cyc: 0

    // cd.epoch_ns: 0
    // cyc_to_ns((cyc: 0 - cd.epoch_cyc: 0) & sched_clock_mask: 0xffffffff , cd.mult: 0x98968000, cd.shift:0
    ns = cd.epoch_ns +
        cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
              cd.mult, cd.shift);
    // ns: 0

    raw_local_irq_save(flags);
    raw_write_seqcount_begin(&cd.seq);
    // static inline void raw_write_seqcount_begin(seqcount_t *s)
    //   s->sequence++;
    //   smp_wmb();
    // cd.seq: 1
    cd.epoch_ns = ns;
    // cd.epoch_ns: 0
    cd.epoch_cyc = cyc;
    // cd.epoch_cyc: 0
    raw_write_seqcount_end(&cd.seq);
    // static inline void raw_write_seqcount_begin(seqcount_t *s)
    //   smp_wmb();
    //   s->sequence++;
    // cd.seq: 2
    raw_local_irq_restore(flags);
}

sched_clock.c::sched_clock_register()

  • return 0x989860
// ARM10C 20150530
// jiffy_sched_clock_read, BITS_PER_LONG: 32, HZ: 100
void __init sched_clock_register(u64 (*read)(void), int bits,
                 unsigned long rate)
{
...
    update_sched_clock();
    // cd.epoch_ns: 0
    // cd.epoch_cyc: 0
    // (&cd.seq): 2


    /*
     * Ensure that sched_clock() starts off at 0ns
     */
    cd.epoch_ns = 0;
    // cd.epoch_ns: 0

    /* Enable IRQ time accounting if we have a fast enough sched_clock */
    // irqtime: -1
    // rate: 100 >= 100000
    if (irqtime > 0 || (irqtime == -1 && rate >= 1000000))
        enable_sched_clock_irqtime();

    pr_debug("Registered %pF as sched_clock source\n", read);
    // Registered xxx as sched_clock source
}
// ARM10C 20150530
void __init sched_clock_postinit(void)
{
    /*
     * If no sched_clock function has been provided at that point,
     * make it the final one one.
     */
    // read_sched_clock: jiffy_sched_clock_read
    if (read_sched_clock == jiffy_sched_clock_read)
        // BITS_PER_LONG: 32, HZ: 100
        sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);

    update_sched_clock();

sched_clock.c::update_sched_clock()

static void notrace update_sched_clock(void)
{
    unsigned long flags;
    u64 cyc;
    u64 ns;

    // read_sched_clock(): 0
    cyc = read_sched_clock();
    // cyc: 0

    // cd.epoch_ns: 0
    // cyc_to_ns((cyc: 0 - cd.epoch_cyc: 0) & sched_clock_mask: 0xffffffff , cd.mult: 0x98968000, cd.shift:0
    ns = cd.epoch_ns +
        cyc_to_ns((cyc - cd.epoch_cyc) & sched_clock_mask,
              cd.mult, cd.shift);
    // ns: 0

    raw_local_irq_save(flags);
    raw_write_seqcount_begin(&cd.seq);
    // static inline void raw_write_seqcount_begin(seqcount_t *s)
    //   s->sequence++;
    //   smp_wmb();
    // cd.seq: 1
    cd.epoch_ns = ns;
    // cd.epoch_ns: 0
    cd.epoch_cyc = cyc;
    // cd.epoch_cyc: 0
    raw_write_seqcount_end(&cd.seq);
    // static inline void raw_write_seqcount_begin(seqcount_t *s)
    //   smp_wmb();
    //   s->sequence++;
    // cd.seq: 2
    raw_local_irq_restore(flags);
}

sched_clock.c::sched_clock_postinit()

// ARM10C 20150530
void __init sched_clock_postinit(void)
{
    /*
     * If no sched_clock function has been provided at that point,
     * make it the final one one.
     */
    // read_sched_clock: jiffy_sched_clock_read
    if (read_sched_clock == jiffy_sched_clock_read)
        // BITS_PER_LONG: 32, HZ: 100
        sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);

    update_sched_clock();

    hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

hrtimer.c::hrtimer_init()

void hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
          enum hrtimer_mode mode)
{
    // timer: &(&def_rt_bandwidth)->rt_period_timer, clock_id: 1, mode: 1
    // timer: &(&runqueues)->hrtick_timer, clock_id: 1, mode: 1
    debug_init(timer, clock_id, mode);

    // timer: &(&def_rt_bandwidth)->rt_period_timer, clock_id: 1, mode: 1
    // timer: &(&runqueues)->hrtick_timer, clock_id: 1, mode: 1
    __hrtimer_init(timer, clock_id, mode);
    // __hrtimer_init(rt_period_timer) 한일:
    // (&def_rt_bandwidth)->rt_period_timer의 값을 0으로 초기화
    // (&(&def_rt_bandwidth)->rt_period_timer)->base: &hrtimer_bases->clock_base[0]
    // RB Tree의 (&(&(&def_rt_bandwidth)->rt_period_timer)->node)->node 를 초기화
    //
    // __hrtimer_init(hrtick_timer) 한일:
    // (&runqueues)->hrtick_timer의 값을 0으로 초기화
    // (&(&runqueues)->hrtick_timer)->base: &hrtimer_bases->clock_base[0]
    // RB Tree의 (&(&(&runqueues)->hrtick_timer)->node)->node 를 초기화
}
EXPORT_SYMBOL_GPL(hrtimer_init);

hrtimer.c::debug_init()

static inline void
debug_init(struct hrtimer *timer, clockid_t clockid,
       enum hrtimer_mode mode)
{
    // timer: &(&def_rt_bandwidth)->rt_period_timer
    // timer: &(&runqueues)->hrtick_timer
    debug_hrtimer_init(timer);
    // debug_hrtimer_init(): null function

    // timer: &(&def_rt_bandwidth)->rt_period_timer, clock_id: 1, mode: 1
    // timer: &(&runqueues)->hrtick_timer, clock_id: 1, mode: 1
    trace_hrtimer_init(timer, clockid, mode);
}

sched_clock.c::sched_clock_postinit()

// ARM10C 20150530
void __init sched_clock_postinit(void)
{
    /*
     * If no sched_clock function has been provided at that point,
     * make it the final one one.
     */
    // read_sched_clock: jiffy_sched_clock_read
    if (read_sched_clock == jiffy_sched_clock_read)
        // BITS_PER_LONG: 32, HZ: 100
        sched_clock_register(jiffy_sched_clock_read, BITS_PER_LONG, HZ);

    update_sched_clock();

    hrtimer_init(&sched_clock_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);

hrtimer.c::__hrtimer_init()

static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
               enum hrtimer_mode mode)
{
    struct hrtimer_cpu_base *cpu_base;
    int base;

    // timer: &(&def_rt_bandwidth)->rt_period_timer, sizeof(struct hrtimer): 40 bytes
    // timer: &(&runqueues)->hrtick_timer, sizeof(struct hrtimer): 40 bytes
    memset(timer, 0, sizeof(struct hrtimer));
    // (&def_rt_bandwidth)->rt_period_timer의 값을 0으로 초기화
    // (&runqueues)->hrtick_timer의 값을 0으로 초기화

    // __raw_get_cpu_var(hrtimer_bases):
    // *({
    //      do {
    //      const void __percpu *__vpp_verify = (typeof((&(hrtimer_bases))))NULL;
    //      (void)__vpp_verify;
    //      } while (0)
    //      &(hrtimer_bases) + __my_cpu_offset;
    // })
    cpu_base = &__raw_get_cpu_var(hrtimer_bases);
    // cpu_base:
    // ({
    //      do {
    //      const void __percpu *__vpp_verify = (typeof((&(hrtimer_bases))))NULL;
    //      (void)__vpp_verify;
    //      } while (0)
    //      &(hrtimer_bases) + __my_cpu_offset;
    // })

    // clock_id: 1, CLOCK_REALTIME: 0, mode: 1, HRTIMER_MODE_ABS: 0
    // clock_id: 1, CLOCK_REALTIME: 0, mode: 1, HRTIMER_MODE_ABS: 0
    if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS)
        clock_id = CLOCK_MONOTONIC;

    // clock_id: 1, hrtimer_clockid_to_base(1): 0
    // clock_id: 1, hrtimer_clockid_to_base(1): 0
    base = hrtimer_clockid_to_base(clock_id);
    // base: 0
    // base: 0

    // timer->base: (&(&def_rt_bandwidth)->rt_period_timer)->base,
    // base: 0, &cpu_base->clock_base: &hrtimer_bases->clock_base
    // timer->base: (&(&runqueues)->hrtick_timer)->base,
    // base: 0, &cpu_base->clock_base: &hrtimer_bases->clock_base
    timer->base = &cpu_base->clock_base[base];
    // timer->base: (&(&def_rt_bandwidth)->rt_period_timer)->base: &hrtimer_bases->clock_base[0]
    // timer->base: (&(&runqueues)->hrtick_timer)->base: &hrtimer_bases->clock_base[0]

    // &timer->node: &(&(&def_rt_bandwidth)->rt_period_timer)->node
    // &timer->node: &(&(&runqueues)->hrtick_timer)->node
    timerqueue_init(&timer->node);
    // RB Tree의 (&(&(&def_rt_bandwidth)->rt_period_timer)->node)->node 를 초기화
    // RB Tree의 &(&(&runqueues)->hrtick_timer)->node 를 초기화

#ifdef CONFIG_TIMER_STATS // CONFIG_TIMER_STATS=n
    timer->start_site = NULL;
    timer->start_pid = -1;
    memset(timer->start_comm, 0, TASK_COMM_LEN);
#endif
}

log

  • 1st log
   cf3996e..db7acca  master     -> origin/master
Updating cf3996e..db7acca
Fast-forward
include/asm-generic/bitsperlong.h |   1 +
include/asm-generic/param.h       |   1 +
include/linux/clocksource.h       |   2 +
include/linux/irqflags.h          |   3 +
include/linux/sched_clock.h       |   2 +-
include/linux/time.h              |   1 +
init/main.c                       |   3 +
kernel/time/clocksource.c         | 341 +++++++++++++++++++++++++++++---------
kernel/time/sched_clock.c         |  26 +++
9 files changed, 302 insertions(+), 78 deletions(-)
  • 2nd log
   db7acca..e6b188d  master     -> origin/master
Merge made by the 'recursive' strategy.
include/linux/clocksource.h  |   4 ++++
include/linux/hrtimer.h      |   2 ++
include/linux/irqflags.h     |   1 +
include/linux/ktime.h        |  16 +++++++++++++---
include/linux/log2.h         |   2 ++
include/linux/rbtree.h       |   2 ++
include/linux/seqlock.h      |  20 ++++++++++++++++++++
include/linux/timerqueue.h   |   6 ++++++
include/trace/events/timer.h |   2 ++
include/uapi/linux/time.h    |   1 +
kernel/hrtimer.c             |  46 ++++++++++++++++++++++++++++++++++++++++++++--
kernel/time/clocksource.c    |  13 ++++++++++++-
kernel/time/sched_clock.c    | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 223 insertions(+), 6 deletions(-)

2015년 5월 29일 금요일

OpenWRT on RaspberryPI 2

OpenWRT on RasberryPI 2

build env.

  • To build SD card image using Ubuntu 14.04.2 LTS (64-bit)
I: Preparing Ubuntu:
sudo apt-get update
sudo apt-get install git-core build-essential libssl-dev
sudo apt-get install subversion mercurial
II: Download the OpenWRT with git:
git clone git://git.openwrt.org/openwrt.git
  • check created a directory 'openwrt'
III: Download and install all available "feeds":
cd openwrt
./scripts/feeds update -a
./scripts/feeds install -a
iV: Make OpenWrt
make defconfig
make prereq
make menuconfig
V: Setup Wi-Fi kernel modules
  • select your Wifi USB stick module on config windows
Vi: set component in LuCI->Collections
  • select LuCI->Collections
Vii: Save and exit
  • select save
  • select exit
Viii: build
make -j 3
  • if you have some errors. please check message it
make V=s 2>&1 | tee build.log | grep -i error
iX: make SD card
cd openwrt/bin/brcm2708
You will be find "openwrt-brcm2708-bcm2709-sdcard-vfat-ext4.img"
sudo dd if=openwrt-brcm2708-bcm2709-sdcard-vfat-ext4.img of=/dev/sdX bs=2M conv=fsync
  • sdX - put your SD card dev (sdb, sdc, ..).
X: start OpenWRT
  • Insert SD on Raspberry PI 2
  • Connect UART-USB cable
  • Insert WiPi stick
  • Connect power.

    Reference Raspberry PI2 GPIO

  • Pin#08: TXD0
  • Pin#10: RXD0
  • Pin#14: GND
Xi: setup new passwd On UART-USB console
passwd
Xii: Reboot Raspberry PI 2 On UART-USB console
reboot
Xiii: Install LuCI On UART-USB console
vi /etc/opkg.conf
  • fix opkg.conf file
opkg update
opkg install luci
opkg install luci-ssl
opkg list | grep luci-i18n-
/etc/init.d/uhttpd start
/etc/init.d/uhttpd enable
  • WiFi connect to use Access Point
mkdir /etc/wpa_supplicant
vi /etc/wpa_supplicant/wpa_supplicant.conf
  • /etc/wpa_supplicant/wpa_supplicant.conf
update_config=1
network={
        ssid="AP name"
        psk="your key"
        scan_ssid=1
        proto=WPA RSN
        key_mgmt=WPA-PSK
        group=CCMP TKIP
        pairwise=CCMP TKIP
        priority=5
}
  • Start Wi-Fi connection in background task:
wpa_supplicant -Dnl80211 -iwlan0 -c/etc/wpa_supplicant/wpa_supplicant.conf &
  • Setting resolving server in /etc/resolv.conf:
vi /etc/resolv.conf
nameserver <put your router IP here>
nameserver 8.8.8.8
  • Save file run:
route add default gw <put your router IP here>

2015년 5월 23일 토요일

[Linux Kernel] 102주차(2015.05.23)

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

102 주차 진도

1 1 start_kernel 1 ~/kernel/iamroot/linux-stable/init/main.c 2 1 time_init 743 ~/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 1420 return mct_init_dt(np, MCT_INT_SPI); 6 1 exynos4_clocksource_init 1410 exynos4_clocksource_init(); 7 1 exynos4_mct_frc_start 425 exynos4_mct_frc_start(0, 0)

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


    exynos4_clocksource_init();
    exynos4_clockevent_init();
}

exynos4_timer_resources() 에서 한일

    // of_iomap에서 한일:
    // device tree 있는  mct node에서 node의 resource 값을 가져옴
    // (&res)->start: 0x101C0000
    // (&res)->end: 0x101C07ff
    // (&res)->flags: IORESOURCE_MEM: 0x00000200
    // (&res)->name: "/mct@101C0000"
    /*
    // alloc area (MCT) 를 만들고 rb tree에 alloc area 를 추가
    // 가상주소 va_start 기준으로 MCT 를 RB Tree 추가한 결과
    //
    //                                      CHID-b
    //                                    (0xF8000000)
    //                                  /              \
    //                            CLK-b                  PMU-b
    //                         (0xF0040000)              (0xF8180000)
    //                        /          \                /        \
    //                 GIC#1-r            TMR-r        CMU-b         SRAM-b
    //             (0xF0002000)         (0xF6300000)   (0xF8100000)  (0xF8400000)
    //              /       \              /    \                         \
    //        GIC#0-b       COMB-b     SYSC-b     WDT-b                   ROMC-r
    //    (0xF0000000) (0xF0004000) (0xF6100000)  (0xF6400000)            (0xF84C0000)
    //                          \
    //                          MCT-r
    //                       (0xF0006000)
    //
    // vmap_area_list에 GIC#0 - GIC#1 - COMB - MCT - 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: 0xf0006000
    // (kmem_cache#30-oX (vm_struct))->size: 0x2000
    // (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 있는 mct node에서 node의 resource 값을 pgtable에 매핑함
    // 0xc0004780이 가리키는 pte의 시작주소에 0x101C0653 값을 갱신
    // (linux pgtable과 hardware pgtable의 값 같이 갱신)
    //
    //  pgd                   pte
    // |              |
    // +--------------+
    // |              |       +--------------+ +0
    // |              |       |  0xXXXXXXXX  | ---> 0x101C0653 에 매칭되는 linux pgtable 값
    // +- - - - - - - +       |  Linux pt 0  |
    // |              |       +--------------+ +1024
    // |              |       |              |
    // +--------------+ +0    |  Linux pt 1  |
    // | *(c0004780)  |-----> +--------------+ +2048
    // |              |       |  0x101C0653  | ---> 2076
    // +- - - - - - - + +4    |   h/w pt 0   |
    // | *(c0004784)  |-----> +--------------+ +3072
    // |              |       +              +
    // +--------------+ +8    |   h/w pt 1   |
    // |              |       +--------------+ +4096
    //
    // cache의 값을 전부 메모리에 반영

    // exynos4_timer_resources에서 한일:
    //
    // 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)
    //
    // 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)
    //
    // 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 하도록함
    //
    // 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 에서 한일:
    // (&cpu_chain)->head: &exynos4_mct_cpu_nb 포인터 대입
    // (&exynos4_mct_cpu_nb)->next은 (&hrtimers_nb)->next로 대입
    //
    // [pcp0] (&percpu_mct_tick)->base: 0x300
    // [pcp0] (&percpu_mct_tick)->name: "mct_tick0"
    // [pcp0] (&(&percpu_mct_tick)->evt)->name: "mct_tick0"
    // [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
    // [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event: exynos4_tick_set_next_event
    // [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
    // [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3
    // [pcp0] (&(&percpu_mct_tick)->evt)->rating: 450
    // [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks: 0xf
    // [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks: 0x7fffffff
    // [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
    // [pcp0] (&(&percpu_mct_tick)->evt)->next_event.tv64: 0x7FFFFFFFFFFFFFFF
    // [pcp0] (&(&percpu_mct_tick)->evt)->event_handler: tick_handle_periodic
    // [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2
    // [pcp0] (&(&percpu_mct_tick)->evt)->irq: 152
    //
    // [pcp0] (&tick_cpu_device)->mode: 0
    // [pcp0] (&tick_cpu_device)->evtdev: [pcp0] &(&percpu_mct_tick)->evt
    //
    // [pcp0] (&tick_cpu_sched)->check_clocks: 1
    //
    // list clockevent_devices에 [pcp0] (&(&percpu_mct_tick)->evt)->list를 추가함
    //
    // tick_do_timer_cpu: 0
    // tick_next_period.tv64: 0
    // tick_period.tv64: 10000000
    //
    // timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
    // 동작하지 않도록 변경함
    // L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
    //
    // register L_ICNTB 에 0x8001D4C0 write함
    // local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
    // interrupt manual update를 enable 시킴
    //
    // register L_INT_ENB 에 0x1 write함
    // local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
    // interrupt counter expired interrupt 가 발생하도록 함
    //
    // register L_TCON 에 0x7 write함
    // local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
    //
    // register L_TCNTB 에 0x1 write함
    // local timer 0 의 tick count 값을 1로 write 함
    //
    // struct irqaction의 메모리 공간을 할당 받고 맴버값 세팅
    // (kmem_cache#30-oX)->handler: exynos4_mct_tick_isr
    // (kmem_cache#30-oX)->thread_fn: NULL
    // (kmem_cache#30-oX)->flags: 0x14A00
    // (kmem_cache#30-oX)->name: "mct_tick0"
    // (kmem_cache#30-oX)->dev_id: [pcp0] &percpu_mct_tick
    // (kmem_cache#30-oX)->irq: 152
    // (kmem_cache#30-oX)->dir: NULL
    //
    // irq_desc 152의 맴버값을 초기화
    // &(&(kmem_cache#28-oX (irq 152))->wait_for_threads)->lock을 사용한 spinlock 초기화
    // &(&(kmem_cache#28-oX (irq 152))->wait_for_threads)->task_list를 사용한 list 초기화
    // (kmem_cache#28-oX (irq 152))->istate: 0
    // (kmem_cache#28-oX (irq 152))->depth: 1
    // (kmem_cache#28-oX (irq 152))->status_use_accessors: 0x3400
    // (kmem_cache#28-oX (irq 152))->irq_count: 0
    // (kmem_cache#28-oX (irq 152))->irqs_unhandled: 0
    // (&(kmem_cache#28-oX (irq 152))->irq_data)->state_use_accessors: 0x11400

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()
    • exynos4_clocksource_init()
// 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

#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
        // 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));


    exynos4_clocksource_init();
  • 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_clocksource_init()

exynos_mct.c::exynos4_clocksource_init()

  • 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_clocksource_init()
// ARM10C 20150516
static void __init exynos4_clocksource_init(void)
{
    exynos4_mct_frc_start(0, 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()
    • exynos4_timer_resources()
    • exynos4_clocksource_init()
      • exynos4_mct_frc_start()

exyos_mct.c::exynos4_mct_frc_start()

  • 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_clocksource_init()
      • exynos4_mct_frc_start()
/* Clocksource handling */
// ARM10C 20150516
// 0, 0
static void exynos4_mct_frc_start(u32 hi, u32 lo)
{
    u32 reg;

    // lo: 0, EXYNOS4_MCT_G_CNT_L: 0x100
    exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);

    // exynos4_mct_write 에서 한일:
    // register G_CNT_L 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함
    //
    // register G_CNT_WSTAT 에 0x1 write함
    // G_CNT_L write status 의  값을 1로 write 함

    // hi: 0, EXYNOS4_MCT_G_CNT_U: 0x104
    exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);

    // exynos4_mct_write 에서 한일:
    // register G_CNT_U 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함
    //
    // register G_CNT_WSTAT 에 0x1 write함
    // G_CNT_U write status 의  값을 1로 write 함

    reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
  • 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()
    • exynos4_clocksource_init()
      • exynos4_mct_frc_start()
        • __raw_readl()

io.h::__raw_readl()

  • 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()
    • exynos4_clocksource_init()
      • exynos4_mct_frc_start()
        • __raw_readl()
// ARM10C 20150509
static inline u32 __raw_readl(const volatile void __iomem *addr)
{
    u32 val;

    // +, Q, o : inline asm의 문법 
    // FIXME: (*(volatile u32 __force *)addr) 의 문법? addr에 *해서 쓰는 이유?
    // addr: 0xf0000004
    asm volatile("ldr %1, %0"
             : "+Qo" (*(volatile u32 __force *)addr),
               "=r" (val));
    // val: 0x0000FC24
    return val;
    // return 0x0000FC24
}
  • return : 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_clocksource_init()
      • exynos4_mct_frc_start()
        • __raw_readl()

exyos_mct.c::exynos4_mct_frc_start()

  • 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_clocksource_init()
      • exynos4_mct_frc_start()
/* Clocksource handling */
// ARM10C 20150516
// 0, 0
static void exynos4_mct_frc_start(u32 hi, u32 lo)
{
    u32 reg;

    // lo: 0, EXYNOS4_MCT_G_CNT_L: 0x100
    exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);

    // exynos4_mct_write 에서 한일:
    // register G_CNT_L 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함
    //
    // register G_CNT_WSTAT 에 0x1 write함
    // G_CNT_L write status 의  값을 1로 write 함

    // hi: 0, EXYNOS4_MCT_G_CNT_U: 0x104
    exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);

    // exynos4_mct_write 에서 한일:
    // register G_CNT_U 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함
    //
    // register G_CNT_WSTAT 에 0x1 write함
    // G_CNT_U write status 의  값을 1로 write 함

    reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
    reg |= MCT_G_TCON_START;
    exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
}

exynos_mct.c::exynos4_mct_write()

// hi: 0, EXYNOS4_MCT_G_CNT_U: 0x104
static void exynos4_mct_write(unsigned int value, unsigned long offset)
{
    unsigned long stat_addr;
    u32 mask;
    u32 i;

    // E.R.M: 21.4.1.23 L0_ICNTB
    // L_ICNTB: Specifies the interrupt count buffer register
    // 31 bit   - interrupt manual update
    // 30~0 bit - interrupt count buffer

    // E.R.M: 21.4.1.29 L0_INT_ENB
    // L_INT_ENB: Specifies the interrupt enable for L_IRQ0
    // 1 bit  - FRCEIE: free running counter expired (L0_FRCCNT = 0) interrupt enable
    // 0 bit  - ICNTEIE: interrupt counter expired (L0_INTCNT = 0) interrupt enable

    // E.R.M: 21.4.1.27 L0_TCON
    // L_TCON: Specifies the timer control register
    // 3 bit  - frc start/stop
    // 2 bit  - interrupt type
    // 1 bit  - interrupt start/stop
    // 0 bit  - timer start/stop

    // E.R.M: 21.4.1.21 L0_TCNTB
    // L_TCNTB: Specifies the tick integer count buffer register
    // 31~0 bit - tick count buffer

    // E.R.M: 21.4.1.2 G_CNT_L
    // G_CNT_L: Specifies the lower 32 bit value of FRC buffer register
    // 31~0 bit - FRC count buffer

    // E.R.M: 21.4.1.2 G_CNT_U
    // G_CNT_U: Specifies the upper 32 bit value of FRC buffer register
    // 31~0 bit - FRC count buffer

    // value: 0x8001D4C0, reg_base: 0xf0006000, offset: 0x308
    // value: 0x1, reg_base: 0xf0006000, offset: 0x334
    // value: 0x7, reg_base: 0xf0006000, offset: 0x320
    // value: 0x1, reg_base: 0xf0006000, offset: 0x300
    // value: 0x0, reg_base: 0xf0006000, offset: 0x100
    // value: 0x0, reg_base: 0xf0006000, offset: 0x104
    __raw_writel(value, reg_base + offset);

    // __raw_writel에서 한일:
    // register L_ICNTB 에 0x8001D4C0 write함
    // local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0)을 write 하고
    // interrupt manual update를 enable 시킴

    // __raw_writel에서 한일:
    // register L_INT_ENB 에 0x1 write함
    // local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
    // interrupt counter expired interrupt 가 발생하도록 함

    // __raw_writel에서 한일:
    // register L_TCON 에 0x7 write함
    // local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴

    // __raw_writel에서 한일:
    // register L_TCNTB 에 0x1 write함
    // local timer 0 의 tick count 값을 1로 write 함

    // __raw_writel에서 한일:
    // register G_CNT_L 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함

    // __raw_writel에서 한일:
    // register G_CNT_U 에 0x0 write함
    // FRC count buffer 의 tick count 값을 0로 write 함

    // offset: 0x308, EXYNOS4_MCT_L_BASE(0): 0x300
    // offset: 0x334, EXYNOS4_MCT_L_BASE(0): 0x300
    // offset: 0x320, EXYNOS4_MCT_L_BASE(0): 0x300
    // offset: 0x300, EXYNOS4_MCT_L_BASE(0): 0x300
    // offset: 0x100, EXYNOS4_MCT_L_BASE(0): 0x300
    // offset: 0x104, EXYNOS4_MCT_L_BASE(0): 0x300
    if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
        // offset: 0x308, EXYNOS4_MCT_L_MASK: 0xffffff00, MCT_L_WSTAT_OFFSET: 0x40
        // offset: 0x334, EXYNOS4_MCT_L_MASK: 0xffffff00, MCT_L_WSTAT_OFFSET: 0x40
        // offset: 0x320, EXYNOS4_MCT_L_MASK: 0xffffff00, MCT_L_WSTAT_OFFSET: 0x40
        // offset: 0x300, EXYNOS4_MCT_L_MASK: 0xffffff00, MCT_L_WSTAT_OFFSET: 0x40
        stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
        // stat_addr: 0x48
        // stat_addr: 0x74
        // stat_addr: 0x60
        // stat_addr: 0x40

        // offset: 0x308, EXYNOS4_MCT_L_MASK: 0xffffff00
        // offset: 0x334, EXYNOS4_MCT_L_MASK: 0xffffff00
        // offset: 0x320, EXYNOS4_MCT_L_MASK: 0xffffff00
        // offset: 0x300, EXYNOS4_MCT_L_MASK: 0xffffff00
        switch (offset & EXYNOS4_MCT_L_MASK) {
        case MCT_L_TCON_OFFSET:  // MCT_L_TCON_OFFSET: 0x20
            mask = 1 << 3;      /* L_TCON write status */
            break;
        case MCT_L_ICNTB_OFFSET: // MCT_L_ICNTB_OFFSET: 0x08
            mask = 1 << 1;      /* L_ICNTB write status */
            break;
        case MCT_L_TCNTB_OFFSET: // MCT_L_TCNTB_OFFSET: 0x00
            mask = 1 << 0;      /* L_TCNTB write status */
            break;
        default:
            return;
            // return 수행
            // return 수행
            // return 수행
            // return 수행
        }
    } else {
        // offset: 0x100
        // offset: 0x104
        switch (offset) {
        case EXYNOS4_MCT_G_TCON: // EXYNOS4_MCT_G_TCON: 0x240
            stat_addr = EXYNOS4_MCT_G_WSTAT;
            mask = 1 << 16;     /* G_TCON write status */
            break;
        case EXYNOS4_MCT_G_COMP0_L: // EXYNOS4_MCT_G_COMP0_L: 0x200
            stat_addr = EXYNOS4_MCT_G_WSTAT;
            mask = 1 << 0;      /* G_COMP0_L write status */
            break;
        case EXYNOS4_MCT_G_COMP0_U: // EXYNOS4_MCT_G_COMP0_U: 0x204
            stat_addr = EXYNOS4_MCT_G_WSTAT;
            mask = 1 << 1;      /* G_COMP0_U write status */
            break;
        case EXYNOS4_MCT_G_COMP0_ADD_INCR: // EXYNOS4_MCT_G_COMP0_ADD_INCR: 0x208
            stat_addr = EXYNOS4_MCT_G_WSTAT;
            mask = 1 << 2;      /* G_COMP0_ADD_INCR w status */
            break;
        case EXYNOS4_MCT_G_CNT_L: // EXYNOS4_MCT_G_CNT_L: 0x100
            // EXYNOS4_MCT_G_CNT_WSTAT: 0x110
            stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
            // stat_addr: 0x110

            mask = 1 << 0;      /* G_CNT_L write status */
            // mask: 0x1

            break;
            // break 수행
        case EXYNOS4_MCT_G_CNT_U: // EXYNOS4_MCT_G_CNT_U: 0x104
            // EXYNOS4_MCT_G_CNT_WSTAT: 0x110
            stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
            // stat_addr: 0x110
            //
            mask = 1 << 1;      /* G_CNT_U write status */
            // mask: 0x2

            break;
            // break 수행
        default:
            return;
        }
    }

    /* Wait maximum 1 ms until written values are applied */
    // loops_per_jiffy: 4096, HZ: 100
    // loops_per_jiffy: 4096, HZ: 100
    for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
        // E.R.M: 21.4.1.4 G_CNT_WSTAT
        // G_CNT_WSTAT: Specifies G_CNT_L and G_CNT_U SFR write status register
        // 0 bit - G_CNT_L write status

        // E.R.M: 21.4.1.4 G_CNT_WSTAT
        // G_CNT_WSTAT: Specifies G_CNT_L and G_CNT_U SFR write status register
        // 1 bit - G_CNT_U write status

        // reg_base: 0xf0006000, stat_addr: 0x110, mask: 0x1, __raw_readl(0xf0006110): 0x1
        // reg_base: 0xf0006000, stat_addr: 0x110, mask: 0x2, __raw_readl(0xf0006110): 0x1
        if (__raw_readl(reg_base + stat_addr) & mask) {
            // mask: 0x1, reg_base: 0xf0006000, stat_addr: 0x110
            // mask: 0x2, reg_base: 0xf0006000, stat_addr: 0x110
            __raw_writel(mask, reg_base + stat_addr);

            // __raw_writel에서 한일:
            // register G_CNT_WSTAT 에 0x1 write함
            // G_CNT_L write status 의  값을 1로 write 함

            // __raw_writel에서 한일:
            // register G_CNT_WSTAT 에 0x2 write함
            // G_CNT_L write status 의  값을 2로 write 함

            return;
            // return 수행
            // return 수행
        }

    panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
}

exynos_mct.c::exynos4_clocksource_init()

// ARM10C 20150516
static void __init exynos4_clocksource_init(void)
{
    exynos4_mct_frc_start(0, 0);

    if (clocksource_register_hz(&mct_frc, clk_rate))
        panic("%s: can't register clocksource\n", mct_frc.name);
}

clocksource.h::clocksource_registster_hz()

static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
{
    return __clocksource_register_scale(cs, 1, hz);
}

clocksource.c::__clocksource_register_scale()

int __clocksource_register_scale(struct clocksource *cs, u32 scale, u32 freq)
{

    /* Initialize mult/shift and max_idle_ns */
    __clocksource_updatefreq_scale(cs, scale, freq);

    /* Add clocksource to the clcoksource list */
    mutex_lock(&clocksource_mutex);
    clocksource_enqueue(cs);
    clocksource_enqueue_watchdog(cs);
    clocksource_select();
    mutex_unlock(&clocksource_mutex);
    return 0;
}
EXPORT_SYMBOL_GPL(__clocksource_register_scale);

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

    }
}

    // of_clk_init에서 한일:
    //
    // devtree에서 allnext로 순회 하면서 찾은 clock node의 주소에서  match: __clk_of_table_exynos5420_clk 찾아
    // exynos5420_clk_init 함수를 수행
    //
    // exynos5420_clk_init에서 한일:
    //
    // 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 에서 한일:
    // 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 에서 한일:
    //
    // 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에서 한일:
    // 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에서 한일:
    // 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에서 한일:
    // 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 에서 한일:
    // exynos5420_mux_clks에 등록 되어 있는 clock 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에서 한일:
    //
    // 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 수행
  • clocksource_of_init에서 한일:
// mct_int_type: 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
//
// mct_irqs[4]: 152
// mct_irqs[5]: 153
// mct_irqs[6]: 154
// mct_irqs[7]: 155
//
// device tree 있는  mct node에서 node의 resource 값을 가져옴
// (&res)->start: 0x101C0000
// (&res)->end: 0x101C07ff
// (&res)->flags: IORESOURCE_MEM: 0x00000200
// (&res)->name: "/mct@101C0000"
/*
// alloc area (MCT) 를 만들고 rb tree에 alloc area 를 추가
// 가상주소 va_start 기준으로 MCT 를 RB Tree 추가한 결과
//
//                                      CHID-b
//                                    (0xF8000000)
//                                  /              \
//                            CLK-b                  PMU-b
//                         (0xF0040000)              (0xF8180000)
//                        /          \                /        \
//                 GIC#1-r            TMR-r        CMU-b         SRAM-b
//             (0xF0002000)         (0xF6300000)   (0xF8100000)  (0xF8400000)
//              /       \              /    \                         \
//        GIC#0-b       COMB-b     SYSC-b     WDT-b                   ROMC-r
//    (0xF0000000) (0xF0004000) (0xF6100000)  (0xF6400000)            (0xF84C0000)
//                          \
//                          MCT-r
//                       (0xF0006000)
//
// vmap_area_list에 GIC#0 - GIC#1 - COMB - MCT - 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: 0xf0006000
// (kmem_cache#30-oX (vm_struct))->size: 0x2000
// (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 있는 mct node에서 node의 resource 값을 pgtable에 매핑함
// 0xc0004780이 가리키는 pte의 시작주소에 0x101C0653 값을 갱신
// (linux pgtable과 hardware pgtable의 값 같이 갱신)
//
//  pgd                   pte
// |              |
// +--------------+
// |              |       +--------------+ +0
// |              |       |  0xXXXXXXXX  | ---> 0x101C0653 에 매칭되는 linux pgtable 값
// +- - - - - - - +       |  Linux pt 0  |
// |              |       +--------------+ +1024
// |              |       |              |
// +--------------+ +0    |  Linux pt 1  |
// | *(c0004780)  |-----> +--------------+ +2048
// |              |       |  0x101C0653  | ---> 2076
// +- - - - - - - + +4    |   h/w pt 0   |
// | *(c0004784)  |-----> +--------------+ +3072
// |              |       +              +
// +--------------+ +8    |   h/w pt 1   |
// |              |       +--------------+ +4096
//
// cache의 값을 전부 메모리에 반영
//
// 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)
//
// 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)
//
// 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 하도록함
//
// 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 에서 한일:
// (&cpu_chain)->head: &exynos4_mct_cpu_nb 포인터 대입
// (&exynos4_mct_cpu_nb)->next은 (&hrtimers_nb)->next로 대입
//
// [pcp0] (&percpu_mct_tick)->base: 0x300
// [pcp0] (&percpu_mct_tick)->name: "mct_tick0"
// [pcp0] (&(&percpu_mct_tick)->evt)->name: "mct_tick0"
// [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
// [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event: exynos4_tick_set_next_event
// [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
// [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3
// [pcp0] (&(&percpu_mct_tick)->evt)->rating: 450
// [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks: 0xf
// [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks: 0x7fffffff
// [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
// [pcp0] (&(&percpu_mct_tick)->evt)->next_event.tv64: 0x7FFFFFFFFFFFFFFF
// [pcp0] (&(&percpu_mct_tick)->evt)->event_handler: tick_handle_periodic
// [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2
// [pcp0] (&(&percpu_mct_tick)->evt)->irq: 152
//
// [pcp0] (&tick_cpu_device)->mode: 0
// [pcp0] (&tick_cpu_device)->evtdev: [pcp0] &(&percpu_mct_tick)->evt
//
// [pcp0] (&tick_cpu_sched)->check_clocks: 1
//
// list clockevent_devices에 [pcp0] (&(&percpu_mct_tick)->evt)->list를 추가함
//
// tick_do_timer_cpu: 0
// tick_next_period.tv64: 0
// tick_period.tv64: 10000000
//
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
//
// register L_ICNTB 에 0x8001D4C0 write함
// local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
// interrupt manual update를 enable 시킴
//
// register L_INT_ENB 에 0x1 write함
// local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
// interrupt counter expired interrupt 가 발생하도록 함
//
// register L_TCON 에 0x7 write함
// local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
//
// register L_TCNTB 에 0x1 write함
// local timer 0 의 tick count 값을 1로 write 함
//
// struct irqaction의 메모리 공간을 할당 받고 맴버값 세팅
// (kmem_cache#30-oX)->handler: exynos4_mct_tick_isr
// (kmem_cache#30-oX)->thread_fn: NULL
// (kmem_cache#30-oX)->flags: 0x14A00
// (kmem_cache#30-oX)->name: "mct_tick0"
// (kmem_cache#30-oX)->dev_id: [pcp0] &percpu_mct_tick
// (kmem_cache#30-oX)->irq: 152
// (kmem_cache#30-oX)->dir: NULL
//
// irq_desc 152의 맴버값을 초기화
// &(&(kmem_cache#28-oX (irq 152))->wait_for_threads)->lock을 사용한 spinlock 초기화
// &(&(kmem_cache#28-oX (irq 152))->wait_for_threads)->task_list를 사용한 list 초기화
// (kmem_cache#28-oX (irq 152))->istate: 0
// (kmem_cache#28-oX (irq 152))->depth: 1
// (kmem_cache#28-oX (irq 152))->action: kmem_cache#30-oX (irqaction)
// (kmem_cache#28-oX (irq 152))->status_use_accessors: 0x3400
// (kmem_cache#28-oX (irq 152))->irq_count: 0
// (kmem_cache#28-oX (irq 152))->irqs_unhandled: 0
// (&(kmem_cache#28-oX (irq 152))->irq_data)->state_use_accessors: 0x11400
//
// register G_CNT_L 에 0x0 write함
// FRC count buffer 의 tick count 값을 0로 write 함
//
// register G_CNT_WSTAT 에 0x1 write함
// G_CNT_L write status 의  값을 1로 write 함
//
// register G_CNT_U 에 0x0 write함
// FRC count buffer 의 tick count 값을 0로 write 함
//
// register G_CNT_WSTAT 에 0x1 write함
// G_CNT_U write status 의  값을 1로 write 함
//
// register G_TCON 에 0x100 write함
// global timer enable 의 값을 1로 write 함
//
// (&mct_frc)->mult: 0xA6AAAAAA
// (&mct_frc)->shift: 26
// (&mct_frc)->maxadj: 0x12555555
// (&mct_frc)->max_idle_ns: 0x103955554C
// (&mct_frc)->flags: 0x21
//
// list clocksource_list의 next에 &(&mct_frc)->list를 추가함
//
// mct_comp_device.cpumask: &cpu_bit_bitmap[1][0
//
// (&mct_comp_device)->min_delta_ticks: 0xf
// (&mct_comp_device)->max_delta_ticks: 0xffffffff
// (&mct_comp_device)->mult: 0x3126E98
// (&mct_comp_device)->shift: 31
// (&mct_comp_device)->min_delta_ns: 0x3E8
// (&mct_comp_device)->max_delta_ns: 0x29AAAAA46E
// (&mct_comp_device)->mode: 1
// (&mct_comp_device)->next_event.tv64: 0x7FFFFFFFFFFFFFFF
//
// list clockevent_devices에 (&mct_comp_device)->list를 추가함
//
// register G_TCON 에 0x100 write함
// global timer enable 의 값을 1로 write 함
//
// register G_INT_ENB 에 0x0 write함
// global timer interrupt enable 의 값을 0로 write 함
//
// comparator 0의 auto increment0, comp0 enable,comp0 interrupt enable 값을
// 0으로 clear 하여 comparator 0를 동작하지 않도록 함
//
// tick_broadcast_device.evtdev: &mct_comp_device
// [pcp0] &(&tick_cpu_sched)->check_clocks: 0xf
//
// &(&(kmem_cache#28-oX (irq 347))->wait_for_threads)->lock을 사용한 spinlock 초기화
// &(&(kmem_cache#28-oX (irq 347))->wait_for_threads)->task_list를 사용한 list 초기화
// &(kmem_cache#28-oX (irq 347))->istate: 0
// (kmem_cache#28-oX (irq 347))->depth: 0
// (kmem_cache#28-oX (irq 347))->action: &mct_comp_event_irq
// (kmem_cache#28-oX (irq 347))->irq_count: 0
// (kmem_cache#28-oX (irq 347))->irqs_unhandled: 0
//
// (&(kmem_cache#28-oX (irq 347))->irq_data)->state_use_accessors: 0x10000
// (&(kmem_cache#28-oX (irq 347))->irq_data)->affinity->bits[0]: 1
//
// register IESR5의 MCT_G0 bit 를 1 로 write 하여 MCT_G0 의 interrupt 를 enable 시킴
//
// GICD_ITARGETSR46 값을 모르기 때문에 0x00000000 로
// 읽히는 것으로 가정하고 GICD_ITARGETSR46에 0x1000000를 write 함
// CPU interface 0에 interrupt가 발생을 나타냄
//
// struct irqaction 멤버 값 세팅
// (&mct_comp_event_irq)->irq: 347
// (&mct_comp_event_irq)->dir: NULL

log

  • 1st log
    5de994b..02918e4  master     -> origin/master
Updating 5de994b..02918e4
Fast-forward
drivers/clocksource/exynos_mct.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
fs/proc/internal.h               |   1 +
include/linux/cpumask.h          |   2 ++
include/linux/interrupt.h        |  23 +++++++++++++++
include/linux/irq.h              |   6 ++++
include/linux/irqdesc.h          |   1 +
include/linux/spinlock.h         |   1 +
kernel/irq/internals.h           |   6 +++-
kernel/irq/irqdesc.c             |   2 ++
kernel/irq/manage.c              |  73 +++++++++++++++++++++++++++++++++++++++++++++--
kernel/irq/proc.c                |  41 +++++++++++++++++++++++++++
11 files changed, 365 insertions(+), 4 deletions(-)
  • 2nd log
    02918e4..3d96381  master     -> origin/master
Updating 02918e4..3d96381
Fast-forward
drivers/clocksource/exynos_mct.c | 113 ++++++++++++++++++++++++++++++++++++---
include/asm-generic/param.h      |   1 +
init/main.c                      |   2 +
3 files changed, 109 insertions(+), 7 deletions(-)
  • 3th log
   Updating 1e01d49..cf3996e
Fast-forward
README.md                        |  4 +++-
arch/arm/include/asm/irqflags.h  |  3 ++-
drivers/clocksource/exynos_mct.c |  1 +
include/linux/irqflags.h         |  5 ++++-
include/linux/list.h             |  1 +
include/linux/percpu-defs.h      |  9 +++++++++
include/linux/tick.h             |  3 +++
kernel/time/clocksource.c        | 21 ++++++++++++++++++---
kernel/time/tick-common.c        |  1 +
kernel/time/tick-internal.h      |  3 +++
kernel/time/tick-oneshot.c       | 13 +++++++++++++
11 files changed, 58 insertions(+), 6 deletions(-)