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 + -
显示快捷键?