ARM10C 100 주차 2번째 진행
일시 : 2015.05.09 (100 주차, 스터디 진행)
모임명 : NAVER개발자커뮤니티지원_IAMROOT.ORG_10차ARM-C
장소 : 토즈 타워점
장소지원 : NAVER 개발자 커뮤니티 지원 프로그램
참여인원 : 3명
100 주차 기념 4월 25일
- 12기 오리엔테이션 진행
- IoT관련 기술
- 3D 프린팅, 4D 프린팅
100 주차 진도 5월 9일 스터디 진행
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 1073 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 6 1 exynos4_timer_resources 1002 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 7 1 exynos4_local_timer_setup 910 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 8 1 clockevents_config_and_register 684 ~/kernel/iamroot/linux-stable/drivers/clocksource/exynos_mct.c 9 1 clockevents_register_device 641 ~/kernel/iamroot/linux-stable/kernel/time/clockevents.c 10 1 tick_check_new_device 513 ~/kernel/iamroot/linux-stable/kernel/time/clockevents.c 11 1 tick_setup_device 456 tick_setup_device(td, newdev, cpu, cpumask_of(cpu)); 12 1 tick_setup_periodic 284 tick_setup_periodic(newdev, 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 20150321
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, 0xf0006000
static void __init exynos4_timer_resources(struct device_node *np, void __iomem *base)
{
int err;
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
// mevt: [pcp0] &percpu_mct_tick
struct clk *mct_clk, *tick_clk;
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
// of_clk_get_by_name(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "fin_pll"): kmem_cache#29-oX (fin_pll)
tick_clk = np ? of_clk_get_by_name(np, "fin_pll") :
clk_get(NULL, "fin_pll");
// tick_clk: kmem_cache#29-oX (fin_pll)
// of_clk_get_by_name에서 한일:
// mct node의 property "clock-names" 의 값을 찾아서 "fin_pll" 이 있는 위치를 찾고
// 몇번째 값인지 index를 구함
//
// mct node 에서 "clocks" property의 이용하여 devtree의 값을 파싱하여 clkspec에 값을 가져옴
// (&clkspec)->np: clock node의 주소
// (&clkspec)->args_count: 1
// (&clkspec)->args[0]: 1
//
// list of_clk_providers 에 등록된 정보들 중에 clkspec 와 매치되는 정보를 찾음
// 이전에 만들어 놓은 clk_data의 clk_table 정보를 이용하여 clkspec에 있는 arg 값을 이용하여 clk을 찾음
// tick_clk: kmem_cache#29-oX (fin_pll)
// tick_clk: kmem_cache#29-oX (fin_pll), IS_ERR(kmem_cache#29-oX (fin_pll)): 0
if (IS_ERR(tick_clk))
panic("%s: unable to determine tick clock rate\n", __func__);
// tick_clk: kmem_cache#29-oX (fin_pll)
// clk_get_rate(kmem_cache#29-oX (fin_pll)): 24000000
clk_rate = clk_get_rate(tick_clk);
// clk_rate: 24000000
// np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소
// of_clk_get_by_name(devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, "mct"): kmem_cache#29-oX (mct)
mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct");
// mct_clk: kmem_cache#29-oX (mct)
// of_clk_get_by_name에서 한일:
// mct node의 property "clock-names" 의 값을 찾아서 "mct" 이 있는 위치를 찾고
// 몇번째 값인지 index를 구함
//
// mct node 에서 "clocks" property의 이용하여 devtree의 값을 파싱하여 clkspec에 값을 가져옴
// (&clkspec)->np: clock node의 주소
// (&clkspec)->args_count: 1
// (&clkspec)->args[0]: 315
//
// list of_clk_providers 에 등록된 정보들 중에 clkspec 와 매치되는 정보를 찾음
// 이전에 만들어 놓은 clk_data의 clk_table 정보를 이용하여 clkspec에 있는 arg 값을 이용하여 clk을 찾음
// mct_clk: kmem_cache#29-oX (mct)
// mct_clk: kmem_cache#29-oX (mct), IS_ERR(kmem_cache#29-oX (mct)): 0
if (IS_ERR(mct_clk))
panic("%s: unable to retrieve mct clock instance\n", __func__);
// mct_clk: kmem_cache#29-oX (mct)
// clk_prepare_enable(kmem_cache#29-oX (mct)): 0
clk_prepare_enable(mct_clk);
// clk_prepare_enable에서 한일:
// mct clock의 상위 clock 들의 ops->prepare 함수들을 수행.
// mct clock의 상위 clock 들의 ops->enable 함수들을 수행.
// sck_cpll -- Group1_p -- mout_aclk66 -- dout_aclk66 -- mct
// sck_ppll -|
// sck_mpll -|
//
// sck_cpll, mout_aclk66, dout_aclk66 의 주석을 만들지 않았기 때문에
// 분석내용을 skip 하도록함
// base: 0xf0006000
reg_base = base;
// reg_base: 0xf0006000
// reg_base: 0xf0006000
if (!reg_base)
panic("%s: unable to ioremap mct address space\n", __func__);
// mct_int_type: 0, MCT_INT_PPI: 1
if (mct_int_type == MCT_INT_PPI) {
err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
exynos4_mct_tick_isr, "MCT",
&percpu_mct_tick);
WARN(err, "MCT: can't request IRQ %d (%d)\n",
mct_irqs[MCT_L0_IRQ], err);
} else {
// MCT_L0_IRQ: 4, mct_irqs[4]: 152, cpumask_of(0): &cpu_bit_bitmap[1][0]
// irq_set_affinity(152, &cpu_bit_bitmap[1][0]): 0
irq_set_affinity(mct_irqs[MCT_L0_IRQ], cpumask_of(0));
// irq_set_affinity에서 한일:
//
// Interrupt pending register인 GICD_ITARGETSR38 값을 읽고
// 그 값과 mask 값인 cpu_bit_bitmap[1][0] 을 or 연산한 값을 GICD_ITARGETSR38에
// 다시 write함
//
// GICD_ITARGETSR38 값을 모르기 때문에 0x00000000 로
// 읽히는 것으로 가정하고 GICD_ITARGETSR38에 0x00000001를 write 함
// CPU interface 0에 interrupt가 발생을 나타냄
//
// (&(kmem_cache#28-oX (irq 152))->irq_data)->affinity->bits[0]: 1
// (&(kmem_cache#28-oX (irq 152))->irq_data)->state_use_accessors: 0x11000
}
// register_cpu_notifier(&exynos4_mct_cpu_nb): 0
err = register_cpu_notifier(&exynos4_mct_cpu_nb);
// err: 0
// register_cpu_notifier 에서 한일:
// (&cpu_chain)->head: &exynos4_mct_cpu_nb 포인터 대입
// (&exynos4_mct_cpu_nb)->next은 (&hrtimers_nb)->next로 대입
// err: 0
if (err)
goto out_irq;
/* Immediately configure the timer on the boot CPU */
// &mevt->evt: [pcp0] &(&percpu_mct_tick)->evt
exynos4_local_timer_setup(&mevt->evt);
- called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
- // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
- ->init_func(np)->mct_init_spi()->mct_init_dt()
- exynos4_timer_resources()
- exynos4_local_timer_setup(&mevt->evt);
exynos_mct.c::exynos4_local_timer_setup()
- called: start_kernel()->time_init()->clocksource_of_init()->init_func(np)
- // np: devtree에서 allnext로 순회 하면서 찾은 mct node의 주소, MCT_INT_SPI: 0
- ->init_func(np)->mct_init_spi()->mct_init_dt()
- exynos4_timer_resources()
- exynos4_local_timer_setup(&mevt->evt);
// ARM10C 20150404
// &mevt->evt: [pcp0] &(&percpu_mct_tick)->evt
static int exynos4_local_timer_setup(struct clock_event_device *evt)
{
struct mct_clock_event_device *mevt;
// smp_processor_id(): 0
unsigned int cpu = smp_processor_id();
// cpu: 0
// evt: [pcp0] &(&percpu_mct_tick)->evt
// container_of([pcp0] &(&percpu_mct_tick)->evt, struct mct_clock_event_device, evt): [pcp0] &percpu_mct_tick
mevt = container_of(evt, struct mct_clock_event_device, evt);
// mevt: [pcp0] &percpu_mct_tick
// mevt->base: [pcp0] (&percpu_mct_tick)->base,
// cpu: 0, EXYNOS4_MCT_L_BASE(0): 0x300
mevt->base = EXYNOS4_MCT_L_BASE(cpu);
// mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300
// mevt->name: [pcp0] (&percpu_mct_tick)->name, cpu: 0
sprintf(mevt->name, "mct_tick%d", cpu);
// mevt->name: [pcp0] (&percpu_mct_tick)->name: "mct_tick0"
// evt->name: [pcp0] (&(&percpu_mct_tick)->evt)->name,
// mevt->name: [pcp0] (&percpu_mct_tick)->name: "mct_tick0"
evt->name = mevt->name;
// evt->name: [pcp0] (&(&percpu_mct_tick)->evt)->name: "mct_tick0"
// evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask,
// cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
evt->cpumask = cpumask_of(cpu);
// evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
// evt->set_next_event: [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event
evt->set_next_event = exynos4_tick_set_next_event;
// evt->set_next_event: [pcp0] (&(&percpu_mct_tick)->evt)->set_next_event: exynos4_tick_set_next_event
// evt->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode
evt->set_mode = exynos4_tick_set_mode;
// evt->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
// evt->features: [pcp0] (&(&percpu_mct_tick)->evt)->features,
// CLOCK_EVT_FEAT_PERIODIC: 0x000001, CLOCK_EVT_FEAT_ONESHOT: 0x000002
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
// evt->features: [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3
// evt->rating: [pcp0] (&(&percpu_mct_tick)->evt)->rating
evt->rating = 450;
// evt->rating: [pcp0] (&(&percpu_mct_tick)->evt)->rating: 450
// evt: [pcp0] &(&percpu_mct_tick)->evt, clk_rate: 24000000, TICK_BASE_CNT: 1
clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
0xf, 0x7fffffff);
clockevents.c::clockevents_config_and_register()
// ARM10C 20150404
// evt: [pcp0] &(&percpu_mct_tick)->evt, 12000000, 0xf, 0x7fffffff
void clockevents_config_and_register(struct clock_event_device *dev,
u32 freq, unsigned long min_delta,
unsigned long max_delta)
{
// dev->min_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks, min_delta: 0xf
dev->min_delta_ticks = min_delta;
// dev->min_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ticks: 0xf
// dev->max_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks, max_delta: 0x7fffffff
dev->max_delta_ticks = max_delta;
// dev->max_delta_ticks: [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ticks: 0x7fffffff
// dev: [pcp0] &(&percpu_mct_tick)->evt, freq: 12000000
// clockevents_config(&(&percpu_mct_tick)->evt, 12000000)
clockevents_config(dev, freq);
// clockevents_config에서 한일:
// [pcp0] (&(&percpu_mct_tick)->evt)->mult: 0x3126E98
// [pcp0] (&(&percpu_mct_tick)->evt)->shift: 32
// [pcp0] (&(&percpu_mct_tick)->evt)->min_delta_ns: 0x4E2
// [pcp0] (&(&percpu_mct_tick)->evt)->max_delta_ns: 0x29AAAAA444
// dev: [pcp0] &(&percpu_mct_tick)->evt
clockevents_register_device(dev);
}
EXPORT_SYMBOL_GPL(clockevents_config_and_register);
clockevents.c::clockevents_register_device()
// ARM10C 20150411
// dev: [pcp0] &(&percpu_mct_tick)->evt
void clockevents_register_device(struct clock_event_device *dev)
{
unsigned long flags;
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, CLOCK_EVT_MODE_UNUSED: 0
BUG_ON(dev->mode != CLOCK_EVT_MODE_UNUSED);
// evt->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
if (!dev->cpumask) {
WARN_ON(num_possible_cpus() > 1);
dev->cpumask = cpumask_of(smp_processor_id());
}
raw_spin_lock_irqsave(&clockevents_lock, flags);
// raw_spin_lock_irqsave에서 한일:
// clockevents_lock를 사용하여 spin lock을 수행하고 cpsr을 flags에 저장
// &dev->list: [pcp0] (&(&percpu_mct_tick)->evt)->list
list_add(&dev->list, &clockevent_devices);
// list_add에서 한일:
// list clockevent_devices에 [pcp0] (&(&percpu_mct_tick)->evt)->list를 추가함
// dev: [pcp0] &(&percpu_mct_tick)->evt
tick_check_new_device(dev);
clockevents_notify_released();
raw_spin_unlock_irqrestore(&clockevents_lock, flags);
}
EXPORT_SYMBOL_GPL(clockevents_register_device);
tick-common.c:tick_check_new_device()
// ARM10C 20150411
// dev: [pcp0] &(&percpu_mct_tick)->evt
void tick_check_new_device(struct clock_event_device *newdev)
{
struct clock_event_device *curdev;
struct tick_device *td;
int cpu;
// smp_processor_id(): 0
cpu = smp_processor_id();
// cpu: 0
// cpu: 0, newdev->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0]
// cpumask_test_cpu(0, &cpu_bit_bitmap[1][0]): 1
if (!cpumask_test_cpu(cpu, newdev->cpumask))
goto out_bc;
// cpu: 0, per_cpu(tick_cpu_device, 0): [pcp0] tick_cpu_device
td = &per_cpu(tick_cpu_device, cpu);
// td: [pcp0] &tick_cpu_device
// td->evtdev: [pcp0] (&tick_cpu_device)->evtdev
curdev = td->evtdev;
// curdev: [pcp0] (&tick_cpu_device)->evtdev
/* cpu local device ? */
// curdev: [pcp0] (&tick_cpu_device)->evtdev, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0
// tick_check_percpu([pcp0] (&tick_cpu_device)->evtdev, [pcp0] &(&percpu_mct_tick)->evt, 0): true
if (!tick_check_percpu(curdev, newdev, cpu))
goto out_bc;
/* Preference decision */
// curdev: [pcp0] (&tick_cpu_device)->evtdev, newdev: [pcp0] &(&percpu_mct_tick)->evt
// tick_check_preferred([pcp0] (&tick_cpu_device)->evtdev, [pcp0] &(&percpu_mct_tick)->evt): 1
if (!tick_check_preferred(curdev, newdev))
goto out_bc;
// newdev->owner: [pcp0] (&(&percpu_mct_tick)->evt)->owner,
// try_module_get([pcp0] (&(&percpu_mct_tick)->evt)->owner): true
if (!try_module_get(newdev->owner))
return;
/*
* Replace the eventually existing device by the new
* device. If the current device is the broadcast device, do
* not give it back to the clockevents layer !
*/
// curdev: [pcp0] (&tick_cpu_device)->evtdev: NULL
// tick_is_broadcast_device([pcp0] (&tick_cpu_device)->evtdev): 0
if (tick_is_broadcast_device(curdev)) {
clockevents_shutdown(curdev);
curdev = NULL;
}
// curdev: [pcp0] (&tick_cpu_device)->evtdev: NULL, newdev: [pcp0] &(&percpu_mct_tick)->evt
// clockevents_exchange_device(NULL, [pcp0] &(&percpu_mct_tick)->evt)
clockevents_exchange_device(curdev, newdev);
// clockevents_exchange_device에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
//
// [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1
// [pcp0] (&(&percpu_mct_tick)->evt)->next_event.tv64: 0x7FFFFFFFFFFFFFFF
// td: [pcp0] &tick_cpu_device, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
tick_setup_device(td, newdev, cpu, cpumask_of(cpu));
tick-common.c::tick_setup_device()
// ARM10C 20150418
// td: [pcp0] &tick_cpu_device, newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0, cpumask_of(0): &cpu_bit_bitmap[1][0]
static void tick_setup_device(struct tick_device *td,
struct clock_event_device *newdev, int cpu,
const struct cpumask *cpumask)
{
ktime_t next_event;
void (*handler)(struct clock_event_device *) = NULL;
// handler: NULL
/*
* First device setup ?
*/
// td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: NULL
if (!td->evtdev) {
/*
* If no cpu took the do_timer update, assign it to
* this cpu:
*/
// tick_do_timer_cpu: -2, TICK_DO_TIMER_BOOT: -2
if (tick_do_timer_cpu == TICK_DO_TIMER_BOOT) {
// cpu: 0, tick_nohz_full_cpu(0): false
if (!tick_nohz_full_cpu(cpu))
// tick_do_timer_cpu: -2, cpu: 0
tick_do_timer_cpu = cpu;
// tick_do_timer_cpu: 0
else
tick_do_timer_cpu = TICK_DO_TIMER_NONE;
// ktime_get(): (ktime_t) { .tv64 = 0}
tick_next_period = ktime_get();
// tick_next_period.tv64: 0
// NSEC_PER_SEC: 1000000000L, HZ: 100
// ktime_set(0, 10000000): (ktime_t) { .tv64 = 10000000}
tick_period = ktime_set(0, NSEC_PER_SEC / HZ);
// tick_period.tv64: 10000000
}
/*
* Startup in periodic mode first.
*/
// td->mode: [pcp0] (&tick_cpu_device)->mode, TICKDEV_MODE_PERIODIC: 0
td->mode = TICKDEV_MODE_PERIODIC;
// td->mode: [pcp0] (&tick_cpu_device)->mode: 0
} else {
handler = td->evtdev->event_handler;
next_event = td->evtdev->next_event;
td->evtdev->event_handler = clockevents_handle_noop;
}
// td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: NULL, newdev: [pcp0] &(&percpu_mct_tick)->evt
td->evtdev = newdev;
// td->evtdev: [pcp0] (&tick_cpu_device)->evtdev: [pcp0] &(&percpu_mct_tick)->evt
/*
* When the device is not per cpu, pin the interrupt to the
* current cpu:
*/
// newdev->cpumask: [pcp0] (&(&percpu_mct_tick)->evt)->cpumask: &cpu_bit_bitmap[1][0],
// cpumask: &cpu_bit_bitmap[1][0]
// cpumask_equal(&cpu_bit_bitmap[1][0], &cpu_bit_bitmap[1][0]): 1
if (!cpumask_equal(newdev->cpumask, cpumask))
irq_set_affinity(newdev->irq, cpumask);
/*
* When global broadcasting is active, check if the current
* device is registered as a placeholder for broadcast mode.
* This allows us to handle this x86 misfeature in a generic
* way. This function also returns !=0 when we keep the
* current active broadcast state for this CPU.
*/
// newdev: [pcp0] &(&percpu_mct_tick)->evt, cpu: 0
// tick_device_uses_broadcast([pcp0] &(&percpu_mct_tick)->evt, 0): 0
if (tick_device_uses_broadcast(newdev, cpu))
return;
// td->mode: [pcp0] (&tick_cpu_device)->mode: 0, TICKDEV_MODE_PERIODIC: 0
if (td->mode == TICKDEV_MODE_PERIODIC)
// newdev: [pcp0] &(&percpu_mct_tick)->evt
tick_setup_periodic(newdev, 0);
else
tick_setup_oneshot(newdev, handler, next_event);
}
- call: tick_setup_periodic()
tick-common.c:tick_setup_periodic()
- called: tick_setup_periodic()
// ARM10C 20150418
// newdev: [pcp0] &(&percpu_mct_tick)->evt, 0
void tick_setup_periodic(struct clock_event_device *dev, int broadcast)
{
// dev: [pcp0] &(&percpu_mct_tick)->evt, broadcast: 0
tick_set_periodic_handler(dev, broadcast);
// tick_set_periodic_handler에서 한일:
// [pcp0] (&(&percpu_mct_tick)->evt)->event_handler: tick_handle_periodic
/* Broadcast setup ? */
// dev: [pcp0] &(&percpu_mct_tick)->evt,
// tick_device_is_functional([pcp0] &(&percpu_mct_tick)->evt): 1
if (!tick_device_is_functional(dev))
return;
// 2015/04/18 종료
// 2015/05/09 시작
// dev->features: [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3, CLOCK_EVT_FEAT_ONESHOT: 0x000002,
// tick_broadcast_oneshot_active(): 0
if ((dev->features & CLOCK_EVT_FEAT_PERIODIC) &&
!tick_broadcast_oneshot_active()) {
// dev: [pcp0] &(&percpu_mct_tick)->evt, CLOCK_EVT_MODE_PERIODIC: 2
clockevents_set_mode(dev, CLOCK_EVT_MODE_PERIODIC);
// clockevents_set_mode에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
//
// register L_ICNTB 에 0x8001D4C0 write함
// local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
// interrupt manual update를 enable 시킴
//
// register L_INT_ENB 에 0x1 write함
// local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
// interrupt counter expired interrupt 가 발생하도록 함
//
// register L_TCON 에 0x7 write함
// local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
//
// [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2
clockevents.c::clockevents_set_mode()
// ARM10C 20150509
// dev: [pcp0] &(&percpu_mct_tick)->evt, CLOCK_EVT_MODE_PERIODIC: 2
void clockevents_set_mode(struct clock_event_device *dev,
enum clock_event_mode mode)
{
// 2015/04/11 종료
// 2015/04/18 시작
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, mode: 1
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1, mode: 2
if (dev->mode != mode) {
// dev->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
// mode: 1, dev: [pcp0] &(&percpu_mct_tick)->evt
// exynos4_tick_set_mode(1, [pcp0] &(&percpu_mct_tick)->evt)
// dev->set_mode: [pcp0] (&(&percpu_mct_tick)->evt)->set_mode: exynos4_tick_set_mode
// mode: 2, dev: [pcp0] &(&percpu_mct_tick)->evt
// exynos4_tick_set_mode(2, [pcp0] &(&percpu_mct_tick)->evt)
dev->set_mode(mode, dev);
// exynos4_tick_set_mode(1)에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
// exynos4_tick_set_mode(2)에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
//
// register L_ICNTB 에 0x8001D4C0 write함
// local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
// interrupt manual update를 enable 시킴
//
// register L_INT_ENB 에 0x1 write함
// local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
// interrupt counter expired interrupt 가 발생하도록 함
//
// register L_TCON 에 0x7 write함
// local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 0, mode: 1
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1, mode: 2
dev->mode = mode;
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 1
// dev->mode: [pcp0] (&(&percpu_mct_tick)->evt)->mode: 2
/*
* A nsec2cyc multiplicator of 0 is invalid and we'd crash
* on it, so fix it up and emit a warning:
*/
// mode: 1, CLOCK_EVT_MODE_ONESHOT: 3
// mode: 2, CLOCK_EVT_MODE_ONESHOT: 3
if (mode == CLOCK_EVT_MODE_ONESHOT) {
if (unlikely(!dev->mult)) {
dev->mult = 1;
WARN_ON(1);
}
}
}
}
exynos_mct.c::exynos4_tick_set_mode()
// ARM10C 20150509
// mode: 2, dev: [pcp0] &(&percpu_mct_tick)->evt
static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
// this_cpu_ptr(&percpu_mct_tick): [pcp0] &percpu_mct_tick
// this_cpu_ptr(&percpu_mct_tick): [pcp0] &percpu_mct_tick
struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
// mevt: [pcp0] &percpu_mct_tick
// mevt: [pcp0] &percpu_mct_tick
unsigned long cycles_per_jiffy;
// mevt: [pcp0] &percpu_mct_tick
// mevt: [pcp0] &percpu_mct_tick
exynos4_mct_tick_stop(mevt);
// exynos4_mct_tick_stop에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
// exynos4_mct_tick_stop에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
// mode: 1
// mode: 2
switch (mode) {
case CLOCK_EVT_MODE_PERIODIC: // CLOCK_EVT_MODE_PERIODIC: 2
// NSEC_PER_SEC: 1000000000L, HZ: 100,
// evt->mult: [pcp0] (&(&percpu_mct_tick)->evt)->mult: 0x3126E98,
// evt->shift: [pcp0] (&(&percpu_mct_tick)->evt)->shift: 32
cycles_per_jiffy =
(((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
// cycles_per_jiffy: 120000 (0x1D4C0)
// cycles_per_jiffy: 120000 (0x1D4C0), mevt: [pcp0] &percpu_mct_tick
exynos4_mct_tick_start(cycles_per_jiffy, mevt);
// exynos4_mct_tick_start에서 한일:
// 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 시킴
break;
// break 수행
case CLOCK_EVT_MODE_ONESHOT: // CLOCK_EVT_MODE_ONESHOT: 3
case CLOCK_EVT_MODE_UNUSED: // CLOCK_EVT_MODE_UNUSED: 0
case CLOCK_EVT_MODE_SHUTDOWN: // CLOCK_EVT_MODE_SHUTDOWN: 1
case CLOCK_EVT_MODE_RESUME: // CLOCK_EVT_MODE_RESUME: 4
break;
// break 수행
}
}
- call: exynos4_mct_tick_start()
exynos_mct.c::exynos4_mct_tick_start()
// ARM10C 20150509
// cycles_per_jiffy: 120000 (0x1D4C0), mevt: [pcp0] &percpu_mct_tick
static void exynos4_mct_tick_start(unsigned long cycles,
struct mct_clock_event_device *mevt)
{
unsigned long tmp;
// mevt: [pcp0] &percpu_mct_tick
exynos4_mct_tick_stop(mevt);
// exynos4_mct_tick_stop에서 한일:
// timer control register L0_TCON 값을 읽어 timer start, timer interrupt 설정을
// 동작하지 않도록 변경함
// L0_TCON 값이 0 으로 가정하였으므로 timer는 동작하지 않은 상태임
// cycles: 0x1D4C0
tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
// tmp: 0x8001D4C0
/* update interrupt count buffer */
// tmp: 0x8001D4C0, mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300, MCT_L_ICNTB_OFFSET: 0x08
exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
// exynos4_mct_write에서 한일:
// register L_ICNTB 에 0x8001D4C0 write함
// local timer 0 의 interrupt count buffer 값을 120000 (0x1D4C0) write 하고
// interrupt manual update를 enable 시킴
/* enable MCT tick interrupt */
// mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300, MCT_L_INT_ENB_OFFSET: 0x34
exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
// exynos4_mct_write에서 한일:
// register L_INT_ENB 에 0x1 write함
// local timer 0 의 ICNTEIE 값을 0x1을 write 하여 L0_INTCNT 값이 0 이 되었을 때
// interrupt counter expired interrupt 가 발생하도록 함
// 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
// NOTE:
// register L0_TCON 값이 reset 값인 0x0으로 읽히는 것으로 가정하고 코드 분석 진행
// reg_base: 0xf0006000, mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300, MCT_L_TCON_OFFSET: 0x20
tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
// tmp: 0
// tmp: 0, MCT_L_TCON_INT_START: 0x2, MCT_L_TCON_TIMER_START: 0x1, MCT_L_TCON_INTERVAL_MODE: 0x4
tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
MCT_L_TCON_INTERVAL_MODE;
// tmp: 0x7
// tmp: 0x7, mevt->base: [pcp0] (&percpu_mct_tick)->base: 0x300, MCT_L_TCON_OFFSET: 0x20
exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
// exynos4_mct_write에서 한일:
// register L_TCON 에 0x7 write함
// local timer 0 의 interrupt type을 interval mode로 설정하고 interrupt, timer 를 start 시킴
}
exynos_mct.c::exynos4_mct_write()
// ARM10C 20150509
// tmp: 0x7, 0x320
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
// value: 0x8001D4C0, reg_base: 0xf0006000, offset: 0x308
// value: 0x1, reg_base: 0xf0006000, offset: 0x334
// value: 0x7, reg_base: 0xf0006000, offset: 0x320
__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 시킴
// offset: 0x308, EXYNOS4_MCT_L_BASE(0): 0x300
// offset: 0x334, EXYNOS4_MCT_L_BASE(0): 0x300
// offset: 0x320, 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
stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
// stat_addr: 0x48
// stat_addr: 0x74
// stat_addr: 0x60
// offset: 0x308, EXYNOS4_MCT_L_MASK: 0xffffff00
// offset: 0x334, EXYNOS4_MCT_L_MASK: 0xffffff00
// offset: 0x320, 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 수행
}
} else {
switch (offset) {
case EXYNOS4_MCT_G_TCON:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 16; /* G_TCON write status */
break;
case EXYNOS4_MCT_G_COMP0_L:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 0; /* G_COMP0_L write status */
break;
case EXYNOS4_MCT_G_COMP0_U:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 1; /* G_COMP0_U write status */
break;
case EXYNOS4_MCT_G_COMP0_ADD_INCR:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
break;
case EXYNOS4_MCT_G_CNT_L:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 0; /* G_CNT_L write status */
break;
case EXYNOS4_MCT_G_CNT_U:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 1; /* G_CNT_U write status */
break;
default:
return;
}
}
/* Wait maximum 1 ms until written values are applied */
for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
if (__raw_readl(reg_base + stat_addr) & mask) {
__raw_writel(mask, reg_base + stat_addr);
return;
}
panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
}
- call: exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
exynos_mct.c::exynos_mct_write()
// ARM10C 20150509
// tmp: 0x7, 0x320
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
// value: 0x8001D4C0, reg_base: 0xf0006000, offset: 0x308
// value: 0x1, reg_base: 0xf0006000, offset: 0x334
// value: 0x7, reg_base: 0xf0006000, offset: 0x320
__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 시킴
// offset: 0x308, EXYNOS4_MCT_L_BASE(0): 0x300
// offset: 0x334, EXYNOS4_MCT_L_BASE(0): 0x300
// offset: 0x320, 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
stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
// stat_addr: 0x48
// stat_addr: 0x74
// stat_addr: 0x60
// offset: 0x308, EXYNOS4_MCT_L_MASK: 0xffffff00
// offset: 0x334, EXYNOS4_MCT_L_MASK: 0xffffff00
// offset: 0x320, 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 수행
}
} else {
switch (offset) {
case EXYNOS4_MCT_G_TCON:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 16; /* G_TCON write status */
break;
case EXYNOS4_MCT_G_COMP0_L:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 0; /* G_COMP0_L write status */
break;
case EXYNOS4_MCT_G_COMP0_U:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 1; /* G_COMP0_U write status */
break;
case EXYNOS4_MCT_G_COMP0_ADD_INCR:
stat_addr = EXYNOS4_MCT_G_WSTAT;
mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
break;
case EXYNOS4_MCT_G_CNT_L:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 0; /* G_CNT_L write status */
break;
case EXYNOS4_MCT_G_CNT_U:
stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
mask = 1 << 1; /* G_CNT_U write status */
break;
default:
return;
}
}
/* Wait maximum 1 ms until written values are applied */
for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
if (__raw_readl(reg_base + stat_addr) & mask) {
__raw_writel(mask, reg_base + stat_addr);
return;
}
panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
}
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)
{
...
// newdev->features: [pcp0] (&(&percpu_mct_tick)->evt)->features: 0x3, CLOCK_EVT_FEAT_ONESHOT: 0x000002
if (newdev->features & CLOCK_EVT_FEAT_ONESHOT)
tick_oneshot_notify();
tick-sched.c::tick_check_new_device()
// ARM10C 20150509
void tick_oneshot_notify(void)
{
struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
// ts: [pcp0] &tick_cpu_sched
// &ts->check_clocks: [pcp0] &(&tick_cpu_sched)->check_clocks
set_bit(0, &ts->check_clocks);
// set_bit에서 한일:
// ts->check_clocks: [pcp0] &(&tick_cpu_sched)->check_clocks: 1
}
exynos_mct.c::exynos4_local_timer_setup()
// ARM10C 20150404
// &mevt->evt: [pcp0] &(&percpu_mct_tick)->evt
static int exynos4_local_timer_setup(struct clock_event_device *evt)
{
...
// 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);
exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
if (mct_int_type == MCT_INT_SPI) {
evt->irq = mct_irqs[MCT_L0_IRQ + cpu];
if (request_irq(evt->irq, exynos4_mct_tick_isr,
IRQF_TIMER | IRQF_NOBALANCING,
evt->name, mevt)) {
pr_err("exynos-mct: cannot register IRQ %d\n",
evt->irq);
return -EIO;
}
} else {
enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
}
interrupt.h::
static inline int __must_check
request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev)
{
return request_threaded_irq(irq, handler, NULL, flags, name, dev);
}
manage.c::
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
EXPORT_SYMBOL(request_threaded_irq);
manage.c::request_threaded_irq()
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
{
struct irqaction *action;
struct irq_desc *desc;
int retval;
/*
* Sanity-check: shared interrupts must pass in a real dev-ID,
* otherwise we'll have trouble later trying to figure out
* which interrupt is which (messes up the interrupt freeing
* logic etc).
*/
if ((irqflags & IRQF_SHARED) && !dev_id)
return -EINVAL;
desc = irq_to_desc(irq);
if (!desc)
return -EINVAL;
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
if (!thread_fn)
return -EINVAL;
handler = irq_default_primary_handler;
}
action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
if (!action)
return -ENOMEM;
action->handler = handler;
action->thread_fn = thread_fn;
action->flags = irqflags;
action->name = devname;
action->dev_id = dev_id;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, action);
chip_bus_sync_unlock(desc);
if (retval)
kfree(action);
#ifdef CONFIG_DEBUG_SHIRQ_FIXME
if (!retval && (irqflags & IRQF_SHARED)) {
/*
* It's a shared IRQ -- the driver ought to be prepared for it
* to happen immediately, so let's make sure....
* We disable the irq to make sure that a 'real' IRQ doesn't
* run in parallel with our fake.
*/
unsigned long flags;
disable_irq(irq);
local_irq_save(flags);
handler(irq, dev_id);
local_irq_restore(flags);
enable_irq(irq);
}
#endif
return retval;
}
EXPORT_SYMBOL(request_threaded_irq);
log
- 1st log
57ed0da..a82e39e master -> origin/master
Updating 57ed0da..a82e39e
Fast-forward
arch/arm/include/asm/bitops.h | 2 ++
arch/arm/include/asm/io.h | 2 ++
drivers/clocksource/exynos_mct.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
include/asm-generic/param.h | 1 +
include/asm-generic/percpu.h | 2 ++
include/linux/clockchips.h | 3 ++
include/linux/list.h | 1 +
include/linux/tick.h | 1 +
include/linux/time.h | 1 +
kernel/time/clockevents.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
kernel/time/tick-common.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++-
kernel/time/tick-internal.h | 1 +
kernel/time/tick-sched.c | 7 +++++
13 files changed, 389 insertions(+), 7 deletions(-)
댓글 없음:
댓글 쓰기