/* * linux/arch/arm/mach-omap1/clock.c * * Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation * Written by Tuukka Tikkanen * * Modified to use omap shared clock framework by * Tony Lindgren * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include "soc.h" #include "iomap.h" #include "clock.h" #include "opp.h" #include "sram.h" __u32 arm_idlect1_mask; struct clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; static LIST_HEAD(clocks); static DEFINE_MUTEX(clocks_mutex); static DEFINE_SPINLOCK(clockfw_lock); /* * Omap1 specific clock functions */ unsigned long omap1_uart_recalc(struct clk *clk) { unsigned int val = __raw_readl(clk->enable_reg); return val & clk->enable_bit ? 48000000 : 12000000; } unsigned long omap1_sossi_recalc(struct clk *clk) { u32 div = omap_readl(MOD_CONF_CTRL_1); div = (div >> 17) & 0x7; div++; return clk->parent->rate / div; } static void omap1_clk_allow_idle(struct clk *clk) { struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; if (!(clk->flags & CLOCK_IDLE_CONTROL)) return; if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count)) arm_idlect1_mask |= 1 << iclk->idlect_shift; } static void omap1_clk_deny_idle(struct clk *clk) { struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; if (!(clk->flags & CLOCK_IDLE_CONTROL)) return; if (iclk->no_idle_count++ == 0) arm_idlect1_mask &= ~(1 << iclk->idlect_shift); } static __u16 verify_ckctl_value(__u16 newval) { /* This function checks for following limitations set * by the hardware (all conditions must be true): * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 * ARM_CK >= TC_CK * DSP_CK >= TC_CK * DSPMMU_CK >= TC_CK * * In addition following rules are enforced: * LCD_CK <= TC_CK * ARMPER_CK <= TC_CK * * However, maximum frequencies are not checked for! */ __u8 per_exp; __u8 lcd_exp; __u8 arm_exp; __u8 dsp_exp; __u8 tc_exp; __u8 dspmmu_exp; per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; if (dspmmu_exp < dsp_exp) dspmmu_exp = dsp_exp; if (dspmmu_exp > dsp_exp+1) dspmmu_exp = dsp_exp+1; if (tc_exp < arm_exp) tc_exp = arm_exp; if (tc_exp < dspmmu_exp) tc_exp = dspmmu_exp; if (tc_exp > lcd_exp) lcd_exp = tc_exp; if (tc_exp > per_exp) per_exp = tc_exp; newval &= 0xf000; newval |= per_exp << CKCTL_PERDIV_OFFSET; newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; newval |= arm_exp << CKCTL_ARMDIV_OFFSET; newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; newval |= tc_exp << CKCTL_TCDIV_OFFSET; newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; return newval;