clocks.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 724 行 · 第 1/2 页
C
724 行
int shift, prate, div, ret; unsigned short reg; unsigned long flags; spin_lock_irqsave(&clock_lock, flags); /* * We can only set this clock's value to a fraction of its * parent's value. The interface says I'll round down when necessary. * So first let's get the parent's current rate. */ prate = ck_get_rate(CK_PARENT(ck)); /* * Let's just start with the highest fraction and keep searching * down through available rates until we find one less than or equal * to the desired rate. */ for (div = 0; div < 4; div++) { if (prate <= rate) break; prate = prate / 2; } /* * Oops. Looks like the caller wants a rate lower than we can support. */ if (div == 5) { printk(KERN_ERR "%s: %d is too low\n", __FUNCTION__, rate); ret = -EINVAL; goto exit; } /* * One more detail: if this clock supports more than one parent, then * we're going to automatically switch over to the parent which runs * through the divisor. For omap this is not ambiguous because for all * such clocks one choice is always OMAP_CLKIN (which doesn't run * through the divisor) and the other is whatever I encoded as * CK_PARENT. Note that I wait until we get this far because I don't * want to switch the input until we're sure this is going to work. */ if (CK_CAN_SWITCH(ck)) if ((ret = ck_set_input(ck, CK_PARENT(ck))) < 0) { BUG(); goto exit; } /* * At last, we can set the divisor. Clear the old rate bits and * set the new ones. */ reg = omap_readw(CK_RATE_REG(ck)); shift = CK_RATE_SHIFT(ck); reg &= ~(3 << shift); reg |= (div << shift); omap_writew(reg, CK_RATE_REG(ck)); /* And return the new (actual, after rounding down) rate. */ ret = prate; exit: spin_unlock_irqrestore(&clock_lock, flags); return ret;}intck_set_rate(ck_t ck, int rate){ int ret = -EINVAL; if (!CK_IN_RANGE(ck) || !CK_CAN_CHANGE_RATE(ck)) goto exit; switch (ck) { default: ret = __ck_set_clkm_rate(ck, rate); break; case OMAP_CK_GEN1: ret = __ck_set_pll_rate(ck, rate); break; }; exit: return ret;}static int__ck_get_pll_rate(ck_t ck){ int m, d; unsigned short pll = omap_readw(CK_RATE_REG(ck)); m = (pll & (0x1f << 7)) >> 7; m = m ? m : 1; d = (pll & (3 << 5)) >> 5; d++; return ((source_clock * m) / d);}static int__ck_get_clkm_rate(ck_t ck){ static int bits2div[] = { 1, 2, 4, 8 }; int in, bits, reg, shift; reg = omap_readw(CK_RATE_REG(ck)); shift = CK_RATE_SHIFT(ck); in = ck_get_rate(CK_PARENT(ck)); bits = (reg & (3 << shift)) >> shift; return (in / bits2div[bits]);}intck_get_rate(ck_t ck){ int ret = 0; ck_t parent; if (!CK_IN_RANGE(ck)) { ret = -EINVAL; goto exit; } switch (ck) { case OMAP_CK_GEN1: ret = __ck_get_pll_rate(ck); break; case OMAP_CLKIN: ret = source_clock; break; case OMAP_MPUXOR_CK: case OMAP_CK_GEN2: case OMAP_CK_GEN3: case OMAP_ARM_GPIO_CK: ret = ck_get_rate(CK_PARENT(ck)); break; case OMAP_ARM_CK: case OMAP_MPUPER_CK: case OMAP_DSP_CK: case OMAP_DSPMMU_CK: case OMAP_LCD_CK: case OMAP_TC_CK: case OMAP_DMA_CK: case OMAP_API_CK: case OMAP_HSAB_CK: case OMAP_LBFREE_CK: case OMAP_LB_CK: ret = __ck_get_clkm_rate(ck); break; case OMAP_MPUTIM_CK: ck_get_input(ck, &parent); ret = ck_get_rate(parent); break; case OMAP_MPUWD_CK: /* Note that this evaluates to zero if source_clock is 12MHz. */ ret = source_clock / 14; break; default: ret = -EINVAL; break; } exit: return ret;}intck_enable(ck_t ck){ unsigned short reg; int ret = -EINVAL, shift; unsigned long flags; if (!CK_IN_RANGE(ck)) goto exit; if (ck_debug) printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck)); ret = 0; if (!CK_CAN_DISABLE(ck)) /* Then it must be on... */ goto exit; spin_lock_irqsave(&clock_lock, flags); if ((CK_USECOUNT(ck) < 0) || (CK_USECOUNT(ck)++ == 0)) { reg = omap_readw(CK_ENABLE_REG(ck)); shift = CK_ENABLE_SHIFT(ck); reg |= (1 << shift); omap_writew(reg, CK_ENABLE_REG(ck)); } spin_unlock_irqrestore(&clock_lock, flags); exit: return ret;}intck_disable(ck_t ck){ unsigned short reg; int ret = -EINVAL, shift; unsigned long flags; if (!CK_IN_RANGE(ck)) goto exit; if (ck_debug) printk(KERN_DEBUG "%s: %s\n", __FUNCTION__, CK_NAME(ck)); if (!CK_CAN_DISABLE(ck)) goto exit; ret = 0; if (ck == OMAP_CLKIN) return -EINVAL; spin_lock_irqsave(&clock_lock, flags); if ((CK_USECOUNT(ck) < 0) || (--CK_USECOUNT(ck) == 0)) { reg = omap_readw(CK_ENABLE_REG(ck)); shift = CK_ENABLE_SHIFT(ck); reg &= ~(1 << shift); omap_writew(reg, CK_ENABLE_REG(ck)); } spin_unlock_irqrestore(&clock_lock, flags); exit: return ret;}int ck_valid_rate(int rate){ return test_bit(rate, (unsigned long *)&ck_valid_table);}static void__ck_make_lookup_table(void){ __u8 m, d; memset(ck_valid_table, 0, sizeof (ck_valid_table)); for (m = 1; m < 32; m++) for (d = 1; d < 5; d++) { int rate = ((source_clock * m) / (d)); if (rate > CK_MAX_PLL_FREQ) continue; if (test_bit(rate, (unsigned long *)&ck_valid_table)) continue; set_bit(rate, (unsigned long *)&ck_valid_table); ck_lookup_table[rate - 1] = (m << 2) | (d - 1); }}int __initinit_ck(void){ const struct omap_clock_config *info; int crystal_type = 0; /* Default 12 MHz */ __ck_make_lookup_table(); info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); if (info != NULL) { if (!cpu_is_omap1510()) crystal_type = info->system_clock_type; } /* We want to be in syncronous scalable mode */ omap_writew(0x1000, ARM_SYSST);#if defined(CONFIG_OMAP_ARM_30MHZ) omap_writew(0x1555, ARM_CKCTL); omap_writew(0x2290, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_60MHZ) omap_writew(0x1005, ARM_CKCTL); omap_writew(0x2290, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_96MHZ) omap_writew(0x1005, ARM_CKCTL); omap_writew(0x2410, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_120MHZ) omap_writew(0x110a, ARM_CKCTL); omap_writew(0x2510, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_168MHZ) omap_writew(0x110f, ARM_CKCTL); omap_writew(0x2710, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_182MHZ) && defined(CONFIG_ARCH_OMAP730) omap_writew(0x250E, ARM_CKCTL); omap_writew(0x2710, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_192MHZ) && defined(CONFIG_ARCH_OMAP16XX) omap_writew(0x150f, ARM_CKCTL); if (crystal_type == 2) { source_clock = 13; /* MHz */ omap_writew(0x2510, DPLL_CTL); } else omap_writew(0x2810, DPLL_CTL);#elif defined(CONFIG_OMAP_ARM_195MHZ) && defined(CONFIG_ARCH_OMAP730) omap_writew(0x250E, ARM_CKCTL); omap_writew(0x2790, DPLL_CTL);#endif#ifdef CONFIG_MACH_OMAP_PERSEUS2 /* Select slicer output as OMAP input clock */ omap_writew(omap_readw(OMAP730_PCC_UPLD_CTRL) & ~0x1, OMAP730_PCC_UPLD_CTRL);#endif /* Turn off some other junk the bootloader might have turned on */ /* Turn off DSP, ARM_INTHCK, ARM_TIMXO */ omap_writew(omap_readw(ARM_CKCTL) & 0x0fff, ARM_CKCTL); /* Put DSP/MPUI into reset until needed */ omap_writew(0, ARM_RSTCT1); omap_writew(1, ARM_RSTCT2); omap_writew(0x400, ARM_IDLECT1); /* * According to OMAP5910 Erratum SYS_DMA_1, bit DMACK_REQ (bit 8) * of the ARM_IDLECT2 register must be set to zero. The power-on * default value of this bit is one. */ omap_writew(0x0000, ARM_IDLECT2); /* Turn LCD clock off also */ /* * Only enable those clocks we will need, let the drivers * enable other clocks as necessary */ ck_enable(OMAP_MPUPER_CK); ck_enable(OMAP_ARM_GPIO_CK); ck_enable(OMAP_MPUXOR_CK); //ck_set_rate(OMAP_MPUTIM_CK, OMAP_CLKIN); ck_enable(OMAP_MPUTIM_CK); start_mputimer1(0xffffffff); return 0;}EXPORT_SYMBOL(ck_get_rate);EXPORT_SYMBOL(ck_set_rate);EXPORT_SYMBOL(ck_enable);EXPORT_SYMBOL(ck_disable);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?