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