📄 clock.c
字号:
/* Can check only after xtal frequency check */ if (ptr->mpu_speed <= rate) break; } return highest_rate;}/* * omap2_convert_field_to_div() - turn field value into integer divider */static u32 omap2_clksel_to_divisor(u32 div_sel, u32 field_val){ u32 i; u32 clkout_array[] = {1, 2, 4, 8, 16}; if ((div_sel & SRC_RATE_SEL_MASK) == CM_SYSCLKOUT_SEL1) { for (i = 0; i < 5; i++) { if (field_val == i) return clkout_array[i]; } return ~0; } else return field_val;}/* * Returns the CLKSEL divider register value * REVISIT: This should be cleaned up to work nicely with void __iomem * */static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask, struct clk *clk){ int ret = ~0; u32 reg_val, div_off; u32 div_addr = 0; u32 mask = ~0; div_off = clk->rate_offset; switch ((*div_sel & SRC_RATE_SEL_MASK)) { case CM_MPU_SEL1: div_addr = (u32)&CM_CLKSEL_MPU; mask = 0x1f; break; case CM_DSP_SEL1: div_addr = (u32)&CM_CLKSEL_DSP; if (cpu_is_omap2420()) { if ((div_off == 0) || (div_off == 8)) mask = 0x1f; else if (div_off == 5) mask = 0x3; } else if (cpu_is_omap2430()) { if (div_off == 0) mask = 0x1f; else if (div_off == 5) mask = 0x3; } break; case CM_GFX_SEL1: div_addr = (u32)&CM_CLKSEL_GFX; if (div_off == 0) mask = 0x7; break; case CM_MODEM_SEL1: div_addr = (u32)&CM_CLKSEL_MDM; if (div_off == 0) mask = 0xf; break; case CM_SYSCLKOUT_SEL1: div_addr = (u32)&PRCM_CLKOUT_CTRL; if ((div_off == 3) || (div_off = 11)) mask= 0x3; break; case CM_CORE_SEL1: div_addr = (u32)&CM_CLKSEL1_CORE; switch (div_off) { case 0: /* l3 */ case 8: /* dss1 */ case 15: /* vylnc-2420 */ case 20: /* ssi */ mask = 0x1f; break; case 5: /* l4 */ mask = 0x3; break; case 13: /* dss2 */ mask = 0x1; break; case 25: /* usb */ mask = 0xf; break; } } *field_mask = mask; if (unlikely(mask == ~0)) div_addr = 0; *div_sel = div_addr; if (unlikely(div_addr == 0)) return ret; /* Isolate field */ reg_val = __raw_readl((void __iomem *)div_addr) & (mask << div_off); /* Normalize back to divider value */ reg_val >>= div_off; return reg_val;}/* * Return divider to be applied to parent clock. * Return 0 on error. */static u32 omap2_clksel_get_divisor(struct clk *clk){ int ret = 0; u32 div, div_sel, div_off, field_mask, field_val; /* isolate control register */ div_sel = (SRC_RATE_SEL_MASK & clk->flags); div_off = clk->rate_offset; field_val = omap2_get_clksel(&div_sel, &field_mask, clk); if (div_sel == 0) return ret; div_sel = (SRC_RATE_SEL_MASK & clk->flags); div = omap2_clksel_to_divisor(div_sel, field_val); return div;}/* Set the clock rate for a clock source */static int omap2_clk_set_rate(struct clk *clk, unsigned long rate){ int ret = -EINVAL; void __iomem * reg; u32 div_sel, div_off, field_mask, field_val, reg_val, validrate; u32 new_div = 0; if (!(clk->flags & CONFIG_PARTICIPANT) && (clk->flags & RATE_CKCTL)) { if (clk == &dpll_ck) return omap2_reprogram_dpll(clk, rate); /* Isolate control register */ div_sel = (SRC_RATE_SEL_MASK & clk->flags); div_off = clk->src_offset; validrate = omap2_clksel_round_rate(clk, rate, &new_div); if(validrate != rate) return(ret); field_val = omap2_get_clksel(&div_sel, &field_mask, clk); if (div_sel == 0) return ret; if(clk->flags & CM_SYSCLKOUT_SEL1){ switch(new_div){ case 16: field_val = 4; break; case 8: field_val = 3; break; case 4: field_val = 2; break; case 2: field_val = 1; break; case 1: field_val = 0; break; } } else field_val = new_div; reg = (void __iomem *)div_sel; reg_val = __raw_readl(reg); reg_val &= ~(field_mask << div_off); reg_val |= (field_val << div_off); __raw_writel(reg_val, reg); clk->rate = clk->parent->rate / field_val; if (clk->flags & DELAYED_APP) __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); ret = 0; } else if (clk->set_rate != 0) ret = clk->set_rate(clk, rate); if (unlikely(ret == 0 && (clk->flags & RATE_PROPAGATES))) propagate_rate(clk); return ret;}/* Converts encoded control register address into a full address */static u32 omap2_get_src_field(u32 *type_to_addr, u32 reg_offset, struct clk *src_clk, u32 *field_mask){ u32 val = ~0, src_reg_addr = 0, mask = 0; /* Find target control register.*/ switch ((*type_to_addr & SRC_RATE_SEL_MASK)) { case CM_CORE_SEL1: src_reg_addr = (u32)&CM_CLKSEL1_CORE; if (reg_offset == 13) { /* DSS2_fclk */ mask = 0x1; if (src_clk == &sys_ck) val = 0; if (src_clk == &func_48m_ck) val = 1; } else if (reg_offset == 8) { /* DSS1_fclk */ mask = 0x1f; if (src_clk == &sys_ck) val = 0; else if (src_clk == &core_ck) /* divided clock */ val = 0x10; /* rate needs fixing */ } else if ((reg_offset == 15) && cpu_is_omap2420()){ /*vlnyq*/ mask = 0x1F; if(src_clk == &func_96m_ck) val = 0; else if (src_clk == &core_ck) val = 0x10; } break; case CM_CORE_SEL2: src_reg_addr = (u32)&CM_CLKSEL2_CORE; mask = 0x3; if (src_clk == &func_32k_ck) val = 0x0; if (src_clk == &sys_ck) val = 0x1; if (src_clk == &alt_ck) val = 0x2; break; case CM_WKUP_SEL1: src_reg_addr = (u32)&CM_CLKSEL2_CORE; mask = 0x3; if (src_clk == &func_32k_ck) val = 0x0; if (src_clk == &sys_ck) val = 0x1; if (src_clk == &alt_ck) val = 0x2; break; case CM_PLL_SEL1: src_reg_addr = (u32)&CM_CLKSEL1_PLL; mask = 0x1; if (reg_offset == 0x3) { if (src_clk == &apll96_ck) val = 0; if (src_clk == &alt_ck) val = 1; } else if (reg_offset == 0x5) { if (src_clk == &apll54_ck) val = 0; if (src_clk == &alt_ck) val = 1; } break; case CM_PLL_SEL2: src_reg_addr = (u32)&CM_CLKSEL2_PLL; mask = 0x3; if (src_clk == &func_32k_ck) val = 0x0; if (src_clk == &dpll_ck) val = 0x2; break; case CM_SYSCLKOUT_SEL1: src_reg_addr = (u32)&PRCM_CLKOUT_CTRL; mask = 0x3; if (src_clk == &dpll_ck) val = 0; if (src_clk == &sys_ck) val = 1; if (src_clk == &func_54m_ck) val = 2; if (src_clk == &func_96m_ck) val = 3; break; } if (val == ~0) /* Catch errors in offset */ *type_to_addr = 0; else *type_to_addr = src_reg_addr; *field_mask = mask; return val;}static int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent){ void __iomem * reg; u32 src_sel, src_off, field_val, field_mask, reg_val, rate; int ret = -EINVAL; if (unlikely(clk->flags & CONFIG_PARTICIPANT)) return ret; if (clk->flags & SRC_SEL_MASK) { /* On-chip SEL collection */ src_sel = (SRC_RATE_SEL_MASK & clk->flags); src_off = clk->src_offset; if (src_sel == 0) goto set_parent_error; field_val = omap2_get_src_field(&src_sel, src_off, new_parent, &field_mask); reg = (void __iomem *)src_sel; if (clk->usecount > 0) omap2_clk_disable(clk); /* Set new source value (previous dividers if any in effect) */ reg_val = __raw_readl(reg) & ~(field_mask << src_off); reg_val |= (field_val << src_off); __raw_writel(reg_val, reg); if (clk->flags & DELAYED_APP) __raw_writel(0x1, (void __iomem *)&PRCM_CLKCFG_CTRL); if (clk->usecount > 0) omap2_clk_enable(clk); clk->parent = new_parent; /* SRC_RATE_SEL_MASK clocks follow their parents rates.*/ if ((new_parent == &core_ck) && (clk == &dss1_fck)) clk->rate = new_parent->rate / 0x10; else clk->rate = new_parent->rate; if (unlikely(clk->flags & RATE_PROPAGATES)) propagate_rate(clk); return 0; } else { clk->parent = new_parent; rate = new_parent->rate; omap2_clk_set_rate(clk, rate); ret = 0; } set_parent_error: return ret;}/* Sets basic clocks based on the specified rate */static int omap2_select_table_rate(struct clk * clk, unsigned long rate){ u32 flags, cur_rate, done_rate, bypass = 0; u8 cpu_mask = 0; struct prcm_config *prcm; unsigned long found_speed = 0; if (clk != &virt_prcm_set) return -EINVAL; /* FIXME: Change cpu_is_omap2420() to cpu_is_omap242x() */ if (cpu_is_omap2420()) cpu_mask = RATE_IN_242X; else if (cpu_is_omap2430()) cpu_mask = RATE_IN_243X; for (prcm = rate_table; prcm->mpu_speed; prcm++) { if (!(prcm->flags & cpu_mask)) continue; if (prcm->xtal_speed != sys_ck.rate) continue; if (prcm->mpu_speed <= rate) { found_speed = prcm->mpu_speed; break; } } if (!found_speed) { printk(KERN_INFO "Could not set MPU rate to %luMHz\n", rate / 1000000); return -EINVAL; } curr_prcm_set = prcm; cur_rate = omap2_get_dpll_rate(&dpll_ck); if (prcm->dpll_speed == cur_rate / 2) { omap2_reprogram_sdrc(PRCM_HALF_SPEED, 1); } else if (prcm->dpll_speed == cur_rate * 2) { omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); } else if (prcm->dpll_speed != cur_rate) { local_irq_save(flags); if (prcm->dpll_speed == prcm->xtal_speed) bypass = 1; if ((prcm->cm_clksel2_pll & 0x3) == 2) done_rate = PRCM_FULL_SPEED; else done_rate = PRCM_HALF_SPEED; /* MPU divider */ CM_CLKSEL_MPU = prcm->cm_clksel_mpu; /* dsp + iva1 div(2420), iva2.1(2430) */ CM_CLKSEL_DSP = prcm->cm_clksel_dsp; CM_CLKSEL_GFX = prcm->cm_clksel_gfx; /* Major subsystem dividers */ CM_CLKSEL1_CORE = prcm->cm_clksel1_core; if (cpu_is_omap2430()) CM_CLKSEL_MDM = prcm->cm_clksel_mdm; /* x2 to enter init_mem */ omap2_reprogram_sdrc(PRCM_FULL_SPEED, 1); omap2_set_prcm(prcm->cm_clksel1_pll, prcm->base_sdrc_rfr, bypass); omap2_init_memory_params(omap2_dll_force_needed()); omap2_reprogram_sdrc(done_rate, 0); local_irq_restore(flags); } omap2_clksel_recalc(&dpll_ck); return 0;}/*------------------------------------------------------------------------- * Omap2 clock reset and init functions *-------------------------------------------------------------------------*/static struct clk_functions omap2_clk_functions = { .clk_enable = omap2_clk_enable, .clk_disable = omap2_clk_disable, .clk_use = omap2_clk_use, .clk_unuse = omap2_clk_unuse, .clk_round_rate = omap2_clk_round_rate, .clk_set_rate = omap2_clk_set_rate, .clk_set_parent = omap2_clk_set_parent,};static void __init omap2_get_crystal_rate(struct clk *osc, struct clk *sys){ u32 div, aplls, sclk = 13000000; aplls = CM_CLKSEL1_PLL; aplls &= ((1 << 23) | (1 << 24) | (1 << 25)); aplls >>= 23; /* Isolate field, 0,2,3 */ if (aplls == 0) sclk = 19200000; else if (aplls == 2) sclk = 13000000; else if (aplls == 3) sclk = 12000000; div = PRCM_CLKSRC_CTRL; div &= ((1 << 7) | (1 << 6)); div >>= sys->rate_offset; osc->rate = sclk * div; sys->rate = sclk;}#ifdef CONFIG_OMAP_RESET_CLOCKSstatic void __init omap2_disable_unused_clocks(void){ struct clk *ck; u32 regval32; list_for_each_entry(ck, &clocks, node) { if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) || ck->enable_reg == 0) continue; regval32 = __raw_readl(ck->enable_reg); if ((regval32 & (1 << ck->enable_bit)) == 0) continue; printk(KERN_INFO "Disabling unused clock \"%s\"\n", ck->name); omap2_clk_disable(ck); }}late_initcall(omap2_disable_unused_clocks);#endif/* * Switch the MPU rate if specified on cmdline. * We cannot do this early until cmdline is parsed. */static int __init omap2_clk_arch_init(void){ if (!mpurate) return -EINVAL; if (omap2_select_table_rate(&virt_prcm_set, mpurate)) printk(KERN_ERR "Could not find matching MPU rate\n"); propagate_rate(&osc_ck); /* update main root fast */ propagate_rate(&func_32k_ck); /* update main root slow */ printk(KERN_INFO "Switched to new clocking rate (Crystal/DPLL/MPU): " "%ld.%01ld/%ld/%ld MHz\n", (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; return 0;}arch_initcall(omap2_clk_arch_init);int __init omap2_clk_init(void){ struct prcm_config *prcm; struct clk ** clkp; u32 clkrate; clk_init(&omap2_clk_functions); omap2_get_crystal_rate(&osc_ck, &sys_ck); for (clkp = onchip_clks; clkp < onchip_clks + ARRAY_SIZE(onchip_clks); clkp++) { if ((*clkp)->flags & CLOCK_IN_OMAP242X && cpu_is_omap2420()) { clk_register(*clkp); continue; } if ((*clkp)->flags & CLOCK_IN_OMAP243X && cpu_is_omap2430()) { clk_register(*clkp); continue; } } /* Check the MPU rate set by bootloader */ clkrate = omap2_get_dpll_rate(&dpll_ck); for (prcm = rate_table; prcm->mpu_speed; prcm++) { if (prcm->xtal_speed != sys_ck.rate) continue; if (prcm->dpll_speed <= clkrate) break; } curr_prcm_set = prcm; propagate_rate(&osc_ck); /* update main root fast */ propagate_rate(&func_32k_ck); /* update main root slow */ printk(KERN_INFO "Clocking rate (Crystal/DPLL/MPU): " "%ld.%01ld/%ld/%ld MHz\n", (sys_ck.rate / 1000000), (sys_ck.rate / 100000) % 10, (dpll_ck.rate / 1000000), (mpu_ck.rate / 1000000)) ; /* * Only enable those clocks we will need, let the drivers * enable other clocks as necessary */ clk_use(&sync_32k_ick); clk_use(&omapctrl_ick); if (cpu_is_omap2430()) clk_use(&sdrc_ick); return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -