clocks.c

来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 703 行 · 第 1/2 页

C
703
字号
/* * Clock interface for OMAP * * Copyright (C) 2001 RidgeRun, Inc * Written by Gordon McNutt <gmcnutt@ridgerun.com> * Updated 2004 for Linux 2.6 by Tony Lindgren <tony@atomide.com> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/types.h>#include <linux/spinlock.h>#include <asm/errno.h>#include <asm/io.h>#include <asm/arch/clocks.h>#include <asm/arch/board.h>/* Input clock in MHz */static unsigned int source_clock = 12;/* * We use one spinlock for all clock registers for now. We may want to * change this to be clock register specific later on. Before we can do * that, we need to map out the shared clock registers. */static spinlock_t clock_lock = SPIN_LOCK_UNLOCKED;typedef struct {	char		*name;	__u8		flags;	ck_t		parent;	unsigned long	rate_reg;	/* Clock rate register */	unsigned long	enbl_reg;	/* Enable register */	unsigned long	idle_reg;	/* Idle register */	unsigned long	slct_reg;	/* Select register */	__s8		rate_shift;	/* Clock rate bit shift */	__s8		enbl_shift;	/* Clock enable bit shift */	__s8		idle_shift;	/* Clock idle bit shift */	__s8		slct_shift;	/* Clock select bit shift */} ck_info_t;#define CK_NAME(ck)		ck_info_table[ck].name#define CK_FLAGS(ck)		ck_info_table[ck].flags#define CK_PARENT(ck)		ck_info_table[ck].parent#define CK_RATE_REG(ck)		ck_info_table[ck].rate_reg#define CK_ENABLE_REG(ck)	ck_info_table[ck].enbl_reg#define CK_IDLE_REG(ck)		ck_info_table[ck].idle_reg#define CK_SELECT_REG(ck)	ck_info_table[ck].slct_reg#define CK_RATE_SHIFT(ck)	ck_info_table[ck].rate_shift#define CK_ENABLE_SHIFT(ck)	ck_info_table[ck].enbl_shift#define CK_IDLE_SHIFT(ck)	ck_info_table[ck].idle_shift#define CK_SELECT_SHIFT(ck)	ck_info_table[ck].slct_shift#define CK_CAN_CHANGE_RATE(cl)	(CK_FLAGS(ck) & CK_RATEF)#define CK_CAN_DISABLE(cl)	(CK_FLAGS(ck) & CK_ENABLEF)#define CK_CAN_IDLE(cl)		(CK_FLAGS(ck) & CK_IDLEF)#define CK_CAN_SWITCH(cl)	(CK_FLAGS(ck) & CK_SELECTF)static ck_info_t ck_info_table[] = {	{		.name		= "clkin",		.flags		= 0,		.parent		= OMAP_CLKIN,	}, {		.name		= "ck_gen1",		.flags		= CK_RATEF | CK_IDLEF,		.rate_reg	= DPLL_CTL,		.idle_reg	= ARM_IDLECT1,		.idle_shift	= IDLDPLL_ARM,		.parent		= OMAP_CLKIN,	}, {		.name		= "ck_gen2",		.flags		= 0,		.parent		= OMAP_CK_GEN1,	}, {		.name		= "ck_gen3",		.flags		= 0,		.parent		= OMAP_CK_GEN1,	}, {		.name		= "tc_ck",		.flags		= CK_RATEF | CK_IDLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.idle_reg	= ARM_IDLECT1,		.rate_shift	= TCDIV,		.idle_shift	= IDLIF_ARM	}, {		.name		= "arm_ck",		.flags		= CK_IDLEF | CK_RATEF,		.parent		= OMAP_CK_GEN1,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[ARMDIV(5:4)] */		.idle_reg	= ARM_IDLECT1,		.rate_shift	= ARMDIV,		.idle_shift	= SETARM_IDLE,	}, {		.name		= "mpuper_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN1,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[PERDIV(1:0)] */		.enbl_reg	= ARM_IDLECT2,		.idle_reg	= ARM_IDLECT1,		.rate_shift	= PERDIV,		.enbl_shift	= EN_PERCK,		.idle_shift	= IDLPER_ARM	}, {		.name		= "arm_gpio_ck",		.flags		= CK_ENABLEF,		.parent		= OMAP_CK_GEN1,		.enbl_reg	= ARM_IDLECT2,		.enbl_shift	= EN_GPIOCK	}, {		.name		= "mpuxor_ck",		.flags		= CK_ENABLEF | CK_IDLEF,		.parent		= OMAP_CLKIN,		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.idle_shift	= IDLXORP_ARM,		.enbl_shift	= EN_XORPCK	}, {		.name		= "mputim_ck",		.flags		= CK_IDLEF | CK_ENABLEF | CK_SELECTF,		.parent		= OMAP_CLKIN,		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.slct_reg	= ARM_CKCTL,		.idle_shift	= IDLTIM_ARM,		.enbl_shift	= EN_TIMCK,		.slct_shift	= ARM_TIMXO	}, {		.name		= "mpuwd_ck",		.flags		= CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CLKIN,		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.idle_shift	= IDLWDT_ARM,		.enbl_shift	= EN_WDTCK,	}, {		.name		= "dsp_ck",		.flags		= CK_RATEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN2,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[DSPDIV(7:6)] */		.enbl_reg	= ARM_CKCTL,		.rate_shift	= DSPDIV,		.enbl_shift	= EN_DSPCK,	}, {		.name		= "dspmmu_ck",		.flags		= CK_RATEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN2,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[DSPMMUDIV(11:10)] */		.enbl_reg	= ARM_CKCTL,		.rate_shift	= DSPMMUDIV,		.enbl_shift	= EN_DSPCK,	}, {		.name		= "dma_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= TCDIV,		.idle_shift	= IDLIF_ARM,		.enbl_shift	= DMACK_REQ	}, {		.name		= "api_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= TCDIV,		.idle_shift	= IDLAPI_ARM,		.enbl_shift	= EN_APICK,	}, {		.name		= "hsab_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= TCDIV,		.idle_shift	= IDLHSAB_ARM,		.enbl_shift	= EN_HSABCK,	}, {		.name		= "lbfree_ck",		.flags		= CK_RATEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= TCDIV,		.enbl_shift	= EN_LBFREECK,	}, {		.name		= "lb_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[TCDIV(9:8)] */		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= TCDIV,		.idle_shift	= IDLLB_ARM,		.enbl_shift	= EN_LBCK,	}, {		.name		= "lcd_ck",		.flags		= CK_RATEF | CK_IDLEF | CK_ENABLEF,		.parent		= OMAP_CK_GEN3,		.rate_reg	= ARM_CKCTL,	/* ARM_CKCTL[LCDDIV(3:2)] */		.idle_reg	= ARM_IDLECT1,		.enbl_reg	= ARM_IDLECT2,		.rate_shift	= LCDDIV,		.idle_shift	= IDLLCD_ARM,		.enbl_shift	= EN_LCDCK,	},};/*****************************************************************************/#define CK_IN_RANGE(ck)		(!((ck < OMAP_CK_MIN) || (ck > OMAP_CK_MAX)))int ck_auto_unclock = 1;int ck_debug = 0;#define CK_MAX_PLL_FREQ		OMAP_CK_MAX_RATEstatic __u8 ck_valid_table[CK_MAX_PLL_FREQ / 8 + 1];static __u8 ck_lookup_table[CK_MAX_PLL_FREQ];intck_set_input(ck_t ck, ck_t input){	int ret = 0, shift;	unsigned short reg;	unsigned long flags;	if (!CK_IN_RANGE(ck) || !CK_CAN_SWITCH(ck)) {		ret = -EINVAL;		goto exit;	}	reg = omap_readw(CK_SELECT_REG(ck));	shift = CK_SELECT_SHIFT(ck);	spin_lock_irqsave(&clock_lock, flags);	if (input == OMAP_CLKIN) {		reg &= ~(1 << shift);		omap_writew(reg, CK_SELECT_REG(ck));		goto exit;	} else if (input == CK_PARENT(ck)) {		reg |= (1 << shift);		omap_writew(reg, CK_SELECT_REG(ck));		goto exit;	}	ret = -EINVAL; exit:	spin_unlock_irqrestore(&clock_lock, flags);	return ret;}intck_get_input(ck_t ck, ck_t * input){	int ret = -EINVAL;	unsigned long flags;	if (!CK_IN_RANGE(ck))		goto exit;	ret = 0;	spin_lock_irqsave(&clock_lock, flags);	if (CK_CAN_SWITCH(ck)) {		int shift;		unsigned short reg;		reg = omap_readw(CK_SELECT_REG(ck));		shift = CK_SELECT_SHIFT(ck);		if (reg & (1 << shift)) {			*input = CK_PARENT(ck);			goto exit;		}	}	*input = OMAP_CLKIN; exit:	spin_unlock_irqrestore(&clock_lock, flags);	return ret;}static int__ck_set_pll_rate(ck_t ck, int rate){	unsigned short pll;	unsigned long flags;	if ((rate < 0) || (rate > CK_MAX_PLL_FREQ))		return -EINVAL;	/* Scan downward for the closest matching frequency */	while (rate && !test_bit(rate, (unsigned long *)&ck_valid_table))		rate--;	if (!rate) {		printk(KERN_ERR "%s: couldn't find a matching rate\n",			__FUNCTION__);		return -EINVAL;	}	spin_lock_irqsave(&clock_lock, flags);	pll = omap_readw(CK_RATE_REG(ck));	/* Clear the rate bits */	pll &= ~(0x1f << 5);	/* Set the rate bits */	pll |= (ck_lookup_table[rate - 1] << 5);	omap_writew(pll, CK_RATE_REG(ck));	spin_unlock_irqrestore(&clock_lock, flags);	return 0;}static int__ck_set_clkm_rate(ck_t ck, int rate){	int shift, prate, div, ret;	unsigned short reg;	unsigned long flags;	spin_lock_irqsave(&clock_lock, flags);	/*

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?