接下来该分析time_init了
/arch/arm/kernel/time.c
void __init time_init(void)
{
#ifndef CONFIG_GENERIC_TIME
if (system_timer->offset == NULL)
system_timer->offset = dummy_gettimeoffset;
#endif
system_timer->init();
}
大家看到这是不是傻眼了,
system_timer->init(),此函数在setup_arch中被赋值了,此值在Machine_init中有定义
功能为初始化系统时钟,以及注册各个时钟单元 clk_register();
/arch/arm/mach-mx5/mach-mx51_3stage.c
static void __init mx51_3stack_timer_init(void)
{
struct clk *uart_clk;
/* Change the CPU voltages for TO2*/
if (cpu_is_mx51_rev(CHIP_REV_2_0) <= 1) {
cpu_wp_auto[0].cpu_voltage = 1175000;
cpu_wp_auto[1].cpu_voltage = 1100000;
cpu_wp_auto[2].cpu_voltage = 1000000;
}
mx51_clocks_init(32768, 24000000, 22579200, 24576000);
uart_clk = clk_get(NULL, "uart_clk.0");
early_console_setup(UART1_BASE_ADDR, uart_clk);
}
static struct sys_timer mxc_timer = {
.init = mx51_3stack_timer_init,
};
MACHINE_START(MX51_3DS, "Freescale MX51 3-Stack Board")
/* Maintainer: Freescale Semiconductor, Inc. */
.phys_io = AIPS1_BASE_ADDR,
.io_pg_offst = ((AIPS1_BASE_ADDR_VIRT) >> 18) & 0xfffc,
.fixup = fixup_mxc_board,
.map_io = mx5_map_io,
.init_irq = mx5_init_irq,
.init_machine = mxc_board_init,
.timer = &mxc_timer,
MACHINE_END
/arch/arm/mach-mx5/clock.c
int __init mx51_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2)
{
__iomem void *base;
struct clk **clkp, *tclk;
int i = 0, j = 0, reg;
int wp_cnt = 0;
pll1_base = ioremap(PLL1_BASE_ADDR, SZ_4K);
pll2_base = ioremap(PLL2_BASE_ADDR, SZ_4K);
pll3_base = ioremap(PLL3_BASE_ADDR, SZ_4K);
/* Turn off all possible clocks */
if (mxc_jtag_enabled) {
__raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET |
1 << MXC_CCM_CCGR0_CG1_OFFSET |
1 << MXC_CCM_CCGR0_CG2_OFFSET |
3 << MXC_CCM_CCGR0_CG3_OFFSET |
3 << MXC_CCM_CCGR0_CG4_OFFSET |
3 << MXC_CCM_CCGR0_CG8_OFFSET |
3 << MXC_CCM_CCGR0_CG9_OFFSET |
1 << MXC_CCM_CCGR0_CG12_OFFSET |
1 << MXC_CCM_CCGR0_CG13_OFFSET |
1 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0);
} else {
__raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET |
1 << MXC_CCM_CCGR0_CG1_OFFSET |
1 << MXC_CCM_CCGR0_CG2_OFFSET |
3 << MXC_CCM_CCGR0_CG3_OFFSET |
3 << MXC_CCM_CCGR0_CG8_OFFSET |
3 << MXC_CCM_CCGR0_CG9_OFFSET |
1 << MXC_CCM_CCGR0_CG12_OFFSET |
1 << MXC_CCM_CCGR0_CG13_OFFSET |
3 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0);
}
__raw_writel(0, MXC_CCM_CCGR1);
__raw_writel(0, MXC_CCM_CCGR2);
__raw_writel(0, MXC_CCM_CCGR3);
__raw_writel(1 << MXC_CCM_CCGR4_CG8_OFFSET, MXC_CCM_CCGR4);
__raw_writel(1 << MXC_CCM_CCGR5_CG2_OFFSET |
1 << MXC_CCM_CCGR5_CG6_1_OFFSET |
1 << MXC_CCM_CCGR5_CG6_2_OFFSET |
3 << MXC_CCM_CCGR5_CG7_OFFSET |
1 << MXC_CCM_CCGR5_CG8_OFFSET |
3 << MXC_CCM_CCGR5_CG9_OFFSET |
1 << MXC_CCM_CCGR5_CG10_OFFSET |
3 << MXC_CCM_CCGR5_CG11_OFFSET, MXC_CCM_CCGR5);
__raw_writel(1 << MXC_CCM_CCGR6_CG4_OFFSET, MXC_CCM_CCGR6);
ckil_clk.rate = ckil;
osc_clk.rate = osc;
ckih_clk.rate = ckih1;
ckih2_clk.rate = ckih2;
/* Fix up clocks unique to MX51. */
esdhc2_clk[0].recalc = _clk_esdhc2_recalc;
esdhc2_clk[0].set_rate = _clk_esdhc2_set_rate;
emi_intr_clk[1].name = "emi_garb_clk";
clk_tree_init();
for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++)
clk_register(*clkp);
clk_register(&fpm_clk);
clk_register(&fpm_div2_clk);
clk_register(&hsi2c_clk);
clk_register(&hsi2c_serial_clk);
clk_register(&sim_clk[0]);
clk_register(&sim_clk[1]);
clk_register(&mipi_hsc1_clk);
clk_register(&mipi_hsc2_clk);
clk_register(&mipi_esc_clk);
clk_register(&mipi_hsp_clk);
clk_register(&spdif1_clk[0]);
clk_register(&spdif1_clk[1]);
clk_register(&ddr_hf_clk);
max_axi_a_clk = MAX_AXI_A_CLK_MX51;
max_axi_b_clk = MAX_AXI_B_CLK_MX51;
/* set DDR clock parent */
reg = 0;
if (cpu_is_mx51_rev(CHIP_REV_2_0) >= 1) {
reg = __raw_readl(MXC_CCM_CBCDR) & MXC_CCM_CBCDR_DDR_HF_SEL;
reg >>= MXC_CCM_CBCDR_DDR_HF_SEL_OFFSET;
if (reg)
tclk = &ddr_hf_clk;
}
if (reg == 0) {
reg = __raw_readl(MXC_CCM_CBCMR) &
MXC_CCM_CBCMR_DDR_CLK_SEL_MASK;
reg >>= MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET;
if (reg == 0) {
tclk = &axi_a_clk;
} else if (reg == 1) {
tclk = &axi_b_clk;
} else if (reg == 2) {
tclk = &emi_slow_clk;
} else {
tclk = &ahb_clk;
}
}
clk_set_parent(&ddr_clk, tclk);
/*Setup the LPM bypass bits */
reg = __raw_readl(MXC_CCM_CLPCR);
reg |= MXC_CCM_CLPCR_BYPASS_HSC_LPM_HS
| MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS
| MXC_CCM_CLPCR_BYPASS_RTIC_LPM_HS
| MXC_CCM_CLPCR_BYPASS_SCC_LPM_HS_MX51
| MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS_MX51;
__raw_writel(reg, MXC_CCM_CLPCR);
/* Disable the handshake with HSC block as its not
* initialised right now.
*/
reg = __raw_readl(MXC_CCM_CCDR);
reg |= MXC_CCM_CCDR_HSC_HS_MASK;
__raw_writel(reg, MXC_CCM_CCDR);
/* This will propagate to all children and init all the clock rates */
propagate_rate(&osc_clk);
propagate_rate(&ckih_clk);
propagate_rate(&ckih2_clk);
propagate_rate(&ckil_clk);
propagate_rate(&pll1_sw_clk);
propagate_rate(&pll2_sw_clk);
clk_enable(&cpu_clk);
/* Set SDHC parents to be PLL2 */
clk_set_parent(&esdhc1_clk[0], &pll2_sw_clk);
clk_set_parent(&esdhc2_clk[0], &pll2_sw_clk);
/* set SDHC root clock as 166.25MHZ*/
clk_set_rate(&esdhc1_clk[0], 166250000);
clk_set_rate(&esdhc2_clk[0], 166250000);
/* Initialise the parents to be axi_b, parents are set to
* axi_a when the clocks are enabled.
*/
clk_set_parent(&vpu_clk[0], &axi_b_clk);
clk_set_parent(&vpu_clk[1], &axi_b_clk);
clk_set_parent(&gpu3d_clk, &axi_a_clk);
clk_set_parent(&gpu2d_clk, &axi_a_clk);
/* move cspi to 24MHz */
clk_set_parent(&cspi_main_clk, &lp_apm_clk);
clk_set_rate(&cspi_main_clk, 12000000);
/*move the spdif0 to spdif_xtal_ckl */
clk_set_parent(&spdif0_clk[0], &spdif_xtal_clk);
/*set the SPDIF dividers to 1 */
reg = __raw_readl(MXC_CCM_CDCDR);
reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PODF_MASK;
reg &= ~MXC_CCM_CDCDR_SPDIF0_CLK_PRED_MASK;
__raw_writel(reg, MXC_CCM_CDCDR);
/* move the spdif1 to 24MHz */
clk_set_parent(&spdif1_clk[0], &spdif_xtal_clk);
/* set the spdif1 dividers to 1 */
reg = __raw_readl(MXC_CCM_CDCDR);
reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PODF_MASK;
reg &= ~MXC_CCM_CDCDR_SPDIF1_CLK_PRED_MASK;
__raw_writel(reg, MXC_CCM_CDCDR);
/* Move SSI clocks to SSI_LP_APM clock */
clk_set_parent(&ssi_lp_apm_clk, &lp_apm_clk);
clk_set_parent(&ssi1_clk[0], &ssi_lp_apm_clk);
/* set the SSI dividers to divide by 2 */
reg = __raw_readl(MXC_CCM_CS1CDR);
reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK;
reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK;
reg |= 1 << MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET;
__raw_writel(reg, MXC_CCM_CS1CDR);
clk_set_parent(&ssi2_clk[0], &ssi_lp_apm_clk);
reg = __raw_readl(MXC_CCM_CS2CDR);
reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK;
reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK;
reg |= 1 << MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET;
__raw_writel(reg, MXC_CCM_CS2CDR);
/* Change the SSI_EXT1_CLK to be sourced from SSI1_CLK_ROOT */
clk_set_parent(&ssi_ext1_clk, &ssi1_clk[0]);
clk_set_parent(&ssi_ext2_clk, &ssi2_clk[0]);
/* move usb_phy_clk to 24MHz */
clk_set_parent(&usb_phy_clk[0], &osc_clk);
/* set usboh3_clk to pll2 */
clk_set_parent(&usboh3_clk[0], &pll2_sw_clk);
reg = __raw_readl(MXC_CCM_CSCDR1);
reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK;
reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK;
reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET;
reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET;
__raw_writel(reg, MXC_CCM_CSCDR1);
/* Set the current working point. */
cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
/* Update the cpu working point table based on the PLL1 freq
* at boot time
*/
if (pll1_main_clk.rate <= cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate)
wp_cnt = 1;
else if (pll1_main_clk.rate <= cpu_wp_tbl[1].cpu_rate &&
pll1_main_clk.rate > cpu_wp_tbl[2].cpu_rate)
wp_cnt = cpu_wp_nr - 1;
else
wp_cnt = cpu_wp_nr;
cpu_wp_tbl[0].cpu_rate = pll1_main_clk.rate;
if (wp_cnt == 1) {
cpu_wp_tbl[0] = cpu_wp_tbl[cpu_wp_nr - 1];
memset(&cpu_wp_tbl[cpu_wp_nr - 1], 0, sizeof(struct cpu_wp));
memset(&cpu_wp_tbl[cpu_wp_nr - 2], 0, sizeof(struct cpu_wp));
} else if (wp_cnt < cpu_wp_nr) {
for (i = 0; i < wp_cnt; i++)
cpu_wp_tbl[i] = cpu_wp_tbl[i+1];
memset(&cpu_wp_tbl[i], 0, sizeof(struct cpu_wp));
}
if (wp_cnt < cpu_wp_nr) {
set_num_cpu_wp(wp_cnt);
cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr);
}
for (j = 0; j < cpu_wp_nr; j++) {
if ((ddr_clk.parent == &ddr_hf_clk)) {
/* Change the CPU podf divider based on the boot up
* pll1 rate.
*/
cpu_wp_tbl[j].cpu_podf =
(pll1_main_clk.rate / cpu_wp_tbl[j].cpu_rate)
- 1;
if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) >
cpu_wp_tbl[j].cpu_rate) {
cpu_wp_tbl[j].cpu_podf++;
cpu_wp_tbl[j].cpu_rate =
pll1_main_clk.rate/
(1000 * (cpu_wp_tbl[j].cpu_podf + 1));
cpu_wp_tbl[j].cpu_rate *= 1000;
}
if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) <
cpu_wp_tbl[j].cpu_rate) {
cpu_wp_tbl[j].cpu_rate = pll1_main_clk.rate;
}
}
cpu_wp_tbl[j].pll_rate = pll1_main_clk.rate;
}
/* Set the current working point. */
for (i = 0; i < cpu_wp_nr; i++) {
if (clk_get_rate(&cpu_clk) == cpu_wp_tbl[i].cpu_rate) {
cpu_curr_wp = i;
break;
}
}
if (i > cpu_wp_nr)
BUG();
clk_set_parent(&arm_axi_clk, &axi_a_clk);
clk_set_parent(&ipu_clk[0], &axi_b_clk);
if (uart_at_24) {
/* Move UART to run from lp_apm */
clk_set_parent(&uart_main_clk, &lp_apm_clk);
/* Set the UART dividers to divide, so the UART_CLK is 24MHz. */
reg = __raw_readl(MXC_CCM_CSCDR1);
reg &= ~MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
reg &= ~MXC_CCM_CSCDR1_UART_CLK_PRED_MASK;
reg |= (0 << MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) |
(0 << MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET);
__raw_writel(reg, MXC_CCM_CSCDR1);
} else {
/* Move UART to run from PLL1 */
/* Move UART to run from PLL1 */
clk_set_parent(&uart_main_clk, &pll1_sw_clk);
/* Set the UART dividers to divide,
* so the UART_CLK is 66.5MHz.
*/
reg = __raw_readl(MXC_CCM_CSCDR1);
reg &= ~MXC_CCM_CSCDR1_UART_CLK_PODF_MASK;
reg &= ~MXC_CCM_CSCDR1_UART_CLK_PRED_MASK;
reg |= (5 << MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) |
(1 << MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET);
__raw_writel(reg, MXC_CCM_CSCDR1);
}
propagate_rate(&osc_clk);
propagate_rate(&pll1_sw_clk);
propagate_rate(&pll2_sw_clk);
clk_set_parent(&emi_slow_clk, &ahb_clk);
clk_set_rate(&emi_slow_clk, clk_round_rate(&emi_slow_clk, 130000000));
/* Change the NFC clock rate to be 1:4 ratio with emi clock. */
clk_set_rate(&emi_enfc_clk, clk_round_rate(&emi_enfc_clk,
(clk_get_rate(&emi_slow_clk))/4));
base = ioremap(GPT1_BASE_ADDR, SZ_4K);
mxc_timer_init(&gpt_clk[0], base, MXC_INT_GPT);
return 0;
}
终于贴完了,真他妈的长貌似也没多大用处,还是自己好好分析吧
void __init mxc_timer_init(struct clk *timer_clk, void __iomem *base, int irq)
{
uint32_t tctl_val;
clk_enable(timer_clk);
if (cpu_is_mx1()) {
#ifdef CONFIG_ARCH_MX1
timer_base = IO_ADDRESS(TIM1_BASE_ADDR);
irq = TIM1_INT;
#endif
} else if (cpu_is_mx2()) {
#ifdef CONFIG_ARCH_MX2
timer_base = IO_ADDRESS(GPT1_BASE_ADDR);
irq = MXC_INT_GPT1;
#endif
} else if (cpu_is_mx3()) {
#ifdef CONFIG_ARCH_MX3
timer_base = IO_ADDRESS(GPT1_BASE_ADDR);
irq = MXC_INT_GPT;
#endif
}
if (base) {
timer_base = base;
}
/*
* Initialise to a known state (all timers off, and timing reset)
*/
__raw_writel(0, timer_base + MXC_TCTL);
__raw_writel(0, timer_base + MXC_TPRER); /* see datasheet note */
if (timer_is_v2())
tctl_val = MX3_TCTL_CLK_PER | MX3_TCTL_FRR | MX3_TCTL_WAITEN | MXC_TCTL_TEN;
else
tctl_val = MX1_2_TCTL_FRR | MX1_2_TCTL_CLK_PCLK1 | MXC_TCTL_TEN;
__raw_writel(tctl_val, timer_base + MXC_TCTL);
/* init and register the timer to the framework */
/* init and register the timer to the framework */
mxc_clocksource_init(timer_clk);
mxc_clockevent_init(timer_clk);
/* Make irqs happen */
setup_irq(irq, &mxc_timer_irq); //设置系统定时的中断函数
}
static struct irqaction mxc_timer_irq = {
.name = "i.MX Timer Tick",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = mxc_timer_interrupt,
};
static irqreturn_t mxc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = &clockevent_mxc;
uint32_t tstat;
if (timer_is_v2())
tstat = __raw_readl(timer_base + MX3_TSTAT);
else
tstat = __raw_readl(timer_base + MX1_2_TSTAT);
gpt_irq_acknowledge();
evt->event_handler(evt); //这里就是系统中断,里面完成了好多东西,包括软中断的执行系统调用
return IRQ_HANDLED;
}
static int __init mxc_clockevent_init(struct clk *timer_clk)
{
unsigned int c = clk_get_rate(timer_clk);
if (timer_is_v2())
clockevent_mxc.set_next_event = mx3_set_next_event;
clockevent_mxc.mult = div_sc(c, NSEC_PER_SEC,
clockevent_mxc.shift);
clockevent_mxc.max_delta_ns =
clockevent_delta2ns(0xfffffffe, &clockevent_mxc);
clockevent_mxc.min_delta_ns =
clockevent_delta2ns(0xff, &clockevent_mxc);
clockevent_mxc.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_mxc); //向通知链中注册自己,同时设置自己的event_handler这个就是在那里定下来的里面完成了软件中断操作,系统调度等任务
return 0;
}
static struct clock_event_device clockevent_mxc = {
.name = "mxc_timer1",
.features = CLOCK_EVT_FEAT_ONESHOT,
.shift = 32,
.set_mode = mxc_set_mode,
.set_next_event = mx1_2_set_next_event,
.rating = 200,
};
最后贴一下别人总结的一点东西
clockevents_register_device()
clockevents_do_notify(CLOCK_EVT_NOTIFY_ADD, dev);
//通知注册回调的人执行当时注册的回调函数 tick_notify()
raw_notifier_call_chain(&clockevents_chain, reason, dev);
tick_notify()
tick_check_new_device()
tick_setup_device()
tick_setup_periodic()
tick_set_periodic_handler()
dev->event_handler = tick_handle_periodic;
tick_handle_periodic()
tick_periodic
do_timer /*更新jiffies */
update_wall_time
update_process_times /*计算进程耗时,, 重新计算调度时间片等等 */
run_local_timers() //执行所有的软件timer
clockevents_program_event()
->set_next_event //设置下一次事件(周期性的)