| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 | /* *  linux/arch/arm/mach-omap1/clock.c * *  Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> * *  Modified to use omap shared clock framework 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 version 2 as * published by the Free Software Foundation. */#include <linux/kernel.h>#include <linux/export.h>#include <linux/list.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/io.h>#include <linux/clk.h>#include <linux/clkdev.h>#include <asm/mach-types.h>#include <mach/hardware.h>#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;}static int calc_dsor_exp(struct clk *clk, unsigned long rate){	/* Note: If target frequency is too low, this function will return 4,	 * which is invalid value. Caller must check for this value and act	 * accordingly.	 *	 * Note: This function does not check 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	 */	unsigned long realrate;	struct clk * parent;	unsigned  dsor_exp;	parent = clk->parent;	if (unlikely(parent == NULL))		return -EIO;	realrate = parent->rate;	for (dsor_exp=0; dsor_exp<4; dsor_exp++) {		if (realrate <= rate)			break;		realrate /= 2;	}	return dsor_exp;}unsigned long omap1_ckctl_recalc(struct clk *clk){	/* Calculate divisor encoded as 2-bit exponent */	int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset));	return clk->parent->rate / dsor;}unsigned long omap1_ckctl_recalc_dsp_domain(struct clk *clk){	int dsor;	/* Calculate divisor encoded as 2-bit exponent	 *	 * The clock control bits are in DSP domain,	 * so api_ck is needed for access.	 * Note that DSP_CKCTL virt addr = phys addr, so	 * we must use __raw_readw() instead of omap_readw().	 */	omap1_clk_enable(api_ck_p);	dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset));	omap1_clk_disable(api_ck_p);	return clk->parent->rate / dsor;}/* MPU virtual clock functions */int omap1_select_table_rate(struct clk *clk, unsigned long rate){	/* Find the highest supported frequency <= rate and switch to it */	struct mpu_rate * ptr;	unsigned long ref_rate;	ref_rate = ck_ref_p->rate;	for (ptr = omap1_rate_table; ptr->rate; ptr++) {		if (!(ptr->flags & cpu_mask))			continue;		if (ptr->xtal != ref_rate)			continue;		/* Can check only after xtal frequency check */		if (ptr->rate <= rate)			break;	}	if (!ptr->rate)		return -EINVAL;	/*	 * In most cases we should not need to reprogram DPLL.	 * Reprogramming the DPLL is tricky, it must be done from SRAM.	 */	omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val);	/* XXX Do we need to recalculate the tree below DPLL1 at this point? */	ck_dpll1_p->rate = ptr->pll_rate;	return 0;}int omap1_clk_set_rate_dsp_domain(struct clk *clk, unsigned long rate){	int dsor_exp;	u16 regval;	dsor_exp = calc_dsor_exp(clk, rate);	if (dsor_exp > 3)		dsor_exp = -EINVAL;	if (dsor_exp < 0)		return dsor_exp;	regval = __raw_readw(DSP_CKCTL);	regval &= ~(3 << clk->rate_offset);	regval |= dsor_exp << clk->rate_offset;	__raw_writew(regval, DSP_CKCTL);	clk->rate = clk->parent->rate / (1 << dsor_exp);	return 0;}long omap1_clk_round_rate_ckctl_arm(struct clk *clk, unsigned long rate){	int dsor_exp = calc_dsor_exp(clk, rate);	if (dsor_exp < 0)		return dsor_exp;	if (dsor_exp > 3)		dsor_exp = 3;	return clk->parent->rate / (1 << dsor_exp);}int omap1_clk_set_rate_ckctl_arm(struct clk *clk, unsigned long rate){	int dsor_exp;	u16 regval;	dsor_exp = calc_dsor_exp(clk, rate);	if (dsor_exp > 3)		dsor_exp = -EINVAL;	if (dsor_exp < 0)		return dsor_exp;	regval = omap_readw(ARM_CKCTL);	regval &= ~(3 << clk->rate_offset);	regval |= dsor_exp << clk->rate_offset;	regval = verify_ckctl_value(regval);	omap_writew(regval, ARM_CKCTL);	clk->rate = clk->parent->rate / (1 << dsor_exp);	return 0;}long omap1_round_to_table_rate(struct clk *clk, unsigned long rate){	/* Find the highest supported frequency <= rate */	struct mpu_rate * ptr;	long highest_rate;	unsigned long ref_rate;	ref_rate = ck_ref_p->rate;	highest_rate = -EINVAL;	for (ptr = omap1_rate_table; ptr->rate; ptr++) {		if (!(ptr->flags & cpu_mask))			continue;		if (ptr->xtal != ref_rate)			continue;		highest_rate = ptr->rate;		/* Can check only after xtal frequency check */		if (ptr->rate <= rate)			break;	}	return highest_rate;}static unsigned calc_ext_dsor(unsigned long rate){	unsigned dsor;
 |