162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/mach-omap1/clock.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation 662306a36Sopenharmony_ci * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Modified to use omap shared clock framework by 962306a36Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/export.h> 1362306a36Sopenharmony_ci#include <linux/list.h> 1462306a36Sopenharmony_ci#include <linux/errno.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/clk.h> 1862306a36Sopenharmony_ci#include <linux/clkdev.h> 1962306a36Sopenharmony_ci#include <linux/clk-provider.h> 2062306a36Sopenharmony_ci#include <linux/soc/ti/omap1-io.h> 2162306a36Sopenharmony_ci#include <linux/spinlock.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <asm/mach-types.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "hardware.h" 2662306a36Sopenharmony_ci#include "soc.h" 2762306a36Sopenharmony_ci#include "iomap.h" 2862306a36Sopenharmony_ci#include "clock.h" 2962306a36Sopenharmony_ci#include "opp.h" 3062306a36Sopenharmony_ci#include "sram.h" 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci__u32 arm_idlect1_mask; 3362306a36Sopenharmony_ci/* provide direct internal access (not via clk API) to some clocks */ 3462306a36Sopenharmony_cistruct omap1_clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* protect registeres shared among clk_enable/disable() and clk_set_rate() operations */ 3762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(arm_ckctl_lock); 3862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(arm_idlect2_lock); 3962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mod_conf_ctrl_0_lock); 4062306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mod_conf_ctrl_1_lock); 4162306a36Sopenharmony_cistatic DEFINE_SPINLOCK(swd_clk_div_ctrl_sel_lock); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* 4462306a36Sopenharmony_ci * Omap1 specific clock functions 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ciunsigned long omap1_uart_recalc(struct omap1_clk *clk, unsigned long p_rate) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci unsigned int val = __raw_readl(clk->enable_reg); 5062306a36Sopenharmony_ci return val & 1 << clk->enable_bit ? 48000000 : 12000000; 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ciunsigned long omap1_sossi_recalc(struct omap1_clk *clk, unsigned long p_rate) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u32 div = omap_readl(MOD_CONF_CTRL_1); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci div = (div >> 17) & 0x7; 5862306a36Sopenharmony_ci div++; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return p_rate / div; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void omap1_clk_allow_idle(struct omap1_clk *clk) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!(clk->flags & CLOCK_IDLE_CONTROL)) 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count)) 7162306a36Sopenharmony_ci arm_idlect1_mask |= 1 << iclk->idlect_shift; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void omap1_clk_deny_idle(struct omap1_clk *clk) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!(clk->flags & CLOCK_IDLE_CONTROL)) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (iclk->no_idle_count++ == 0) 8262306a36Sopenharmony_ci arm_idlect1_mask &= ~(1 << iclk->idlect_shift); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic __u16 verify_ckctl_value(__u16 newval) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci /* This function checks for following limitations set 8862306a36Sopenharmony_ci * by the hardware (all conditions must be true): 8962306a36Sopenharmony_ci * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 9062306a36Sopenharmony_ci * ARM_CK >= TC_CK 9162306a36Sopenharmony_ci * DSP_CK >= TC_CK 9262306a36Sopenharmony_ci * DSPMMU_CK >= TC_CK 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * In addition following rules are enforced: 9562306a36Sopenharmony_ci * LCD_CK <= TC_CK 9662306a36Sopenharmony_ci * ARMPER_CK <= TC_CK 9762306a36Sopenharmony_ci * 9862306a36Sopenharmony_ci * However, maximum frequencies are not checked for! 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci __u8 per_exp; 10162306a36Sopenharmony_ci __u8 lcd_exp; 10262306a36Sopenharmony_ci __u8 arm_exp; 10362306a36Sopenharmony_ci __u8 dsp_exp; 10462306a36Sopenharmony_ci __u8 tc_exp; 10562306a36Sopenharmony_ci __u8 dspmmu_exp; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; 10862306a36Sopenharmony_ci lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; 10962306a36Sopenharmony_ci arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; 11062306a36Sopenharmony_ci dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; 11162306a36Sopenharmony_ci tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; 11262306a36Sopenharmony_ci dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (dspmmu_exp < dsp_exp) 11562306a36Sopenharmony_ci dspmmu_exp = dsp_exp; 11662306a36Sopenharmony_ci if (dspmmu_exp > dsp_exp+1) 11762306a36Sopenharmony_ci dspmmu_exp = dsp_exp+1; 11862306a36Sopenharmony_ci if (tc_exp < arm_exp) 11962306a36Sopenharmony_ci tc_exp = arm_exp; 12062306a36Sopenharmony_ci if (tc_exp < dspmmu_exp) 12162306a36Sopenharmony_ci tc_exp = dspmmu_exp; 12262306a36Sopenharmony_ci if (tc_exp > lcd_exp) 12362306a36Sopenharmony_ci lcd_exp = tc_exp; 12462306a36Sopenharmony_ci if (tc_exp > per_exp) 12562306a36Sopenharmony_ci per_exp = tc_exp; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci newval &= 0xf000; 12862306a36Sopenharmony_ci newval |= per_exp << CKCTL_PERDIV_OFFSET; 12962306a36Sopenharmony_ci newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; 13062306a36Sopenharmony_ci newval |= arm_exp << CKCTL_ARMDIV_OFFSET; 13162306a36Sopenharmony_ci newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; 13262306a36Sopenharmony_ci newval |= tc_exp << CKCTL_TCDIV_OFFSET; 13362306a36Sopenharmony_ci newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return newval; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int calc_dsor_exp(unsigned long rate, unsigned long realrate) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci /* Note: If target frequency is too low, this function will return 4, 14162306a36Sopenharmony_ci * which is invalid value. Caller must check for this value and act 14262306a36Sopenharmony_ci * accordingly. 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * Note: This function does not check for following limitations set 14562306a36Sopenharmony_ci * by the hardware (all conditions must be true): 14662306a36Sopenharmony_ci * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 14762306a36Sopenharmony_ci * ARM_CK >= TC_CK 14862306a36Sopenharmony_ci * DSP_CK >= TC_CK 14962306a36Sopenharmony_ci * DSPMMU_CK >= TC_CK 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci unsigned dsor_exp; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (unlikely(realrate == 0)) 15462306a36Sopenharmony_ci return -EIO; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci for (dsor_exp=0; dsor_exp<4; dsor_exp++) { 15762306a36Sopenharmony_ci if (realrate <= rate) 15862306a36Sopenharmony_ci break; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci realrate /= 2; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return dsor_exp; 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ciunsigned long omap1_ckctl_recalc(struct omap1_clk *clk, unsigned long p_rate) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci /* Calculate divisor encoded as 2-bit exponent */ 16962306a36Sopenharmony_ci int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* update locally maintained rate, required by arm_ck for omap1_show_rates() */ 17262306a36Sopenharmony_ci clk->rate = p_rate / dsor; 17362306a36Sopenharmony_ci return clk->rate; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int omap1_clk_is_enabled(struct clk_hw *hw) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 17962306a36Sopenharmony_ci bool api_ck_was_enabled = true; 18062306a36Sopenharmony_ci __u32 regval32; 18162306a36Sopenharmony_ci int ret; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!clk->ops) /* no gate -- always enabled */ 18462306a36Sopenharmony_ci return 1; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci if (clk->ops == &clkops_dspck) { 18762306a36Sopenharmony_ci api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); 18862306a36Sopenharmony_ci if (!api_ck_was_enabled) 18962306a36Sopenharmony_ci if (api_ck_p->ops->enable(api_ck_p) < 0) 19062306a36Sopenharmony_ci return 0; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) 19462306a36Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 19562306a36Sopenharmony_ci else 19662306a36Sopenharmony_ci regval32 = __raw_readw(clk->enable_reg); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci ret = regval32 & (1 << clk->enable_bit); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (!api_ck_was_enabled) 20162306a36Sopenharmony_ci api_ck_p->ops->disable(api_ck_p); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return ret; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ciunsigned long omap1_ckctl_recalc_dsp_domain(struct omap1_clk *clk, unsigned long p_rate) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci bool api_ck_was_enabled; 21062306a36Sopenharmony_ci int dsor; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* Calculate divisor encoded as 2-bit exponent 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * The clock control bits are in DSP domain, 21562306a36Sopenharmony_ci * so api_ck is needed for access. 21662306a36Sopenharmony_ci * Note that DSP_CKCTL virt addr = phys addr, so 21762306a36Sopenharmony_ci * we must use __raw_readw() instead of omap_readw(). 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); 22062306a36Sopenharmony_ci if (!api_ck_was_enabled) 22162306a36Sopenharmony_ci api_ck_p->ops->enable(api_ck_p); 22262306a36Sopenharmony_ci dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset)); 22362306a36Sopenharmony_ci if (!api_ck_was_enabled) 22462306a36Sopenharmony_ci api_ck_p->ops->disable(api_ck_p); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return p_rate / dsor; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* MPU virtual clock functions */ 23062306a36Sopenharmony_ciint omap1_select_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci /* Find the highest supported frequency <= rate and switch to it */ 23362306a36Sopenharmony_ci struct mpu_rate * ptr; 23462306a36Sopenharmony_ci unsigned long ref_rate; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ref_rate = ck_ref_p->rate; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci for (ptr = omap1_rate_table; ptr->rate; ptr++) { 23962306a36Sopenharmony_ci if (!(ptr->flags & cpu_mask)) 24062306a36Sopenharmony_ci continue; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (ptr->xtal != ref_rate) 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Can check only after xtal frequency check */ 24662306a36Sopenharmony_ci if (ptr->rate <= rate) 24762306a36Sopenharmony_ci break; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (!ptr->rate) 25162306a36Sopenharmony_ci return -EINVAL; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * In most cases we should not need to reprogram DPLL. 25562306a36Sopenharmony_ci * Reprogramming the DPLL is tricky, it must be done from SRAM. 25662306a36Sopenharmony_ci */ 25762306a36Sopenharmony_ci omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* XXX Do we need to recalculate the tree below DPLL1 at this point? */ 26062306a36Sopenharmony_ci ck_dpll1_p->rate = ptr->pll_rate; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint omap1_clk_set_rate_dsp_domain(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci int dsor_exp; 26862306a36Sopenharmony_ci u16 regval; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci dsor_exp = calc_dsor_exp(rate, p_rate); 27162306a36Sopenharmony_ci if (dsor_exp > 3) 27262306a36Sopenharmony_ci dsor_exp = -EINVAL; 27362306a36Sopenharmony_ci if (dsor_exp < 0) 27462306a36Sopenharmony_ci return dsor_exp; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci regval = __raw_readw(DSP_CKCTL); 27762306a36Sopenharmony_ci regval &= ~(3 << clk->rate_offset); 27862306a36Sopenharmony_ci regval |= dsor_exp << clk->rate_offset; 27962306a36Sopenharmony_ci __raw_writew(regval, DSP_CKCTL); 28062306a36Sopenharmony_ci clk->rate = p_rate / (1 << dsor_exp); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return 0; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cilong omap1_clk_round_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, 28662306a36Sopenharmony_ci unsigned long *p_rate) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci int dsor_exp = calc_dsor_exp(rate, *p_rate); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (dsor_exp < 0) 29162306a36Sopenharmony_ci return dsor_exp; 29262306a36Sopenharmony_ci if (dsor_exp > 3) 29362306a36Sopenharmony_ci dsor_exp = 3; 29462306a36Sopenharmony_ci return *p_rate / (1 << dsor_exp); 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint omap1_clk_set_rate_ckctl_arm(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci unsigned long flags; 30062306a36Sopenharmony_ci int dsor_exp; 30162306a36Sopenharmony_ci u16 regval; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci dsor_exp = calc_dsor_exp(rate, p_rate); 30462306a36Sopenharmony_ci if (dsor_exp > 3) 30562306a36Sopenharmony_ci dsor_exp = -EINVAL; 30662306a36Sopenharmony_ci if (dsor_exp < 0) 30762306a36Sopenharmony_ci return dsor_exp; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* protect ARM_CKCTL register from concurrent access via clk_enable/disable() */ 31062306a36Sopenharmony_ci spin_lock_irqsave(&arm_ckctl_lock, flags); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci regval = omap_readw(ARM_CKCTL); 31362306a36Sopenharmony_ci regval &= ~(3 << clk->rate_offset); 31462306a36Sopenharmony_ci regval |= dsor_exp << clk->rate_offset; 31562306a36Sopenharmony_ci regval = verify_ckctl_value(regval); 31662306a36Sopenharmony_ci omap_writew(regval, ARM_CKCTL); 31762306a36Sopenharmony_ci clk->rate = p_rate / (1 << dsor_exp); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci spin_unlock_irqrestore(&arm_ckctl_lock, flags); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cilong omap1_round_to_table_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) 32562306a36Sopenharmony_ci{ 32662306a36Sopenharmony_ci /* Find the highest supported frequency <= rate */ 32762306a36Sopenharmony_ci struct mpu_rate * ptr; 32862306a36Sopenharmony_ci long highest_rate; 32962306a36Sopenharmony_ci unsigned long ref_rate; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci ref_rate = ck_ref_p->rate; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci highest_rate = -EINVAL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci for (ptr = omap1_rate_table; ptr->rate; ptr++) { 33662306a36Sopenharmony_ci if (!(ptr->flags & cpu_mask)) 33762306a36Sopenharmony_ci continue; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (ptr->xtal != ref_rate) 34062306a36Sopenharmony_ci continue; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci highest_rate = ptr->rate; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Can check only after xtal frequency check */ 34562306a36Sopenharmony_ci if (ptr->rate <= rate) 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return highest_rate; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic unsigned calc_ext_dsor(unsigned long rate) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci unsigned dsor; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci /* MCLK and BCLK divisor selection is not linear: 35762306a36Sopenharmony_ci * freq = 96MHz / dsor 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * RATIO_SEL range: dsor <-> RATIO_SEL 36062306a36Sopenharmony_ci * 0..6: (RATIO_SEL+2) <-> (dsor-2) 36162306a36Sopenharmony_ci * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) 36262306a36Sopenharmony_ci * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 36362306a36Sopenharmony_ci * can not be used. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci for (dsor = 2; dsor < 96; ++dsor) { 36662306a36Sopenharmony_ci if ((dsor & 1) && dsor > 8) 36762306a36Sopenharmony_ci continue; 36862306a36Sopenharmony_ci if (rate >= 96000000 / dsor) 36962306a36Sopenharmony_ci break; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci return dsor; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* XXX Only needed on 1510 */ 37562306a36Sopenharmony_cilong omap1_round_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return rate > 24000000 ? 48000000 : 12000000; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciint omap1_set_uart_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci unsigned long flags; 38362306a36Sopenharmony_ci unsigned int val; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (rate == 12000000) 38662306a36Sopenharmony_ci val = 0; 38762306a36Sopenharmony_ci else if (rate == 48000000) 38862306a36Sopenharmony_ci val = 1 << clk->enable_bit; 38962306a36Sopenharmony_ci else 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* protect MOD_CONF_CTRL_0 register from concurrent access via clk_enable/disable() */ 39362306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci val |= __raw_readl(clk->enable_reg) & ~(1 << clk->enable_bit); 39662306a36Sopenharmony_ci __raw_writel(val, clk->enable_reg); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci clk->rate = rate; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* External clock (MCLK & BCLK) functions */ 40662306a36Sopenharmony_ciint omap1_set_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci unsigned long flags; 40962306a36Sopenharmony_ci unsigned dsor; 41062306a36Sopenharmony_ci __u16 ratio_bits; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci dsor = calc_ext_dsor(rate); 41362306a36Sopenharmony_ci clk->rate = 96000000 / dsor; 41462306a36Sopenharmony_ci if (dsor > 8) 41562306a36Sopenharmony_ci ratio_bits = ((dsor - 8) / 2 + 6) << 2; 41662306a36Sopenharmony_ci else 41762306a36Sopenharmony_ci ratio_bits = (dsor - 2) << 2; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* protect SWD_CLK_DIV_CTRL_SEL register from concurrent access via clk_enable/disable() */ 42062306a36Sopenharmony_ci spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ratio_bits |= __raw_readw(clk->enable_reg) & ~0xfd; 42362306a36Sopenharmony_ci __raw_writew(ratio_bits, clk->enable_reg); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int calc_div_sossi(unsigned long rate, unsigned long p_rate) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int div; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Round towards slower frequency */ 43562306a36Sopenharmony_ci div = (p_rate + rate - 1) / rate; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return --div; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cilong omap1_round_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci int div; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci div = calc_div_sossi(rate, *p_rate); 44562306a36Sopenharmony_ci if (div < 0) 44662306a36Sopenharmony_ci div = 0; 44762306a36Sopenharmony_ci else if (div > 7) 44862306a36Sopenharmony_ci div = 7; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return *p_rate / (div + 1); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciint omap1_set_sossi_rate(struct omap1_clk *clk, unsigned long rate, unsigned long p_rate) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci unsigned long flags; 45662306a36Sopenharmony_ci u32 l; 45762306a36Sopenharmony_ci int div; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci div = calc_div_sossi(rate, p_rate); 46062306a36Sopenharmony_ci if (div < 0 || div > 7) 46162306a36Sopenharmony_ci return -EINVAL; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* protect MOD_CONF_CTRL_1 register from concurrent access via clk_enable/disable() */ 46462306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci l = omap_readl(MOD_CONF_CTRL_1); 46762306a36Sopenharmony_ci l &= ~(7 << 17); 46862306a36Sopenharmony_ci l |= div << 17; 46962306a36Sopenharmony_ci omap_writel(l, MOD_CONF_CTRL_1); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci clk->rate = p_rate / (div + 1); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cilong omap1_round_ext_clk_rate(struct omap1_clk *clk, unsigned long rate, unsigned long *p_rate) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci return 96000000 / calc_ext_dsor(rate); 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ciint omap1_init_ext_clk(struct omap1_clk *clk) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci unsigned dsor; 48662306a36Sopenharmony_ci __u16 ratio_bits; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci /* Determine current rate and ensure clock is based on 96MHz APLL */ 48962306a36Sopenharmony_ci ratio_bits = __raw_readw(clk->enable_reg) & ~1; 49062306a36Sopenharmony_ci __raw_writew(ratio_bits, clk->enable_reg); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci ratio_bits = (ratio_bits & 0xfc) >> 2; 49362306a36Sopenharmony_ci if (ratio_bits > 6) 49462306a36Sopenharmony_ci dsor = (ratio_bits - 6) * 2 + 8; 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci dsor = ratio_bits + 2; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci clk-> rate = 96000000 / dsor; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int omap1_clk_enable(struct clk_hw *hw) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw)); 50662306a36Sopenharmony_ci int ret = 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (parent && clk->flags & CLOCK_NO_IDLE_PARENT) 50962306a36Sopenharmony_ci omap1_clk_deny_idle(parent); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (clk->ops && !(WARN_ON(!clk->ops->enable))) 51262306a36Sopenharmony_ci ret = clk->ops->enable(clk); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void omap1_clk_disable(struct clk_hw *hw) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw), *parent = to_omap1_clk(clk_hw_get_parent(hw)); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (clk->ops && !(WARN_ON(!clk->ops->disable))) 52262306a36Sopenharmony_ci clk->ops->disable(clk); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (likely(parent) && clk->flags & CLOCK_NO_IDLE_PARENT) 52562306a36Sopenharmony_ci omap1_clk_allow_idle(parent); 52662306a36Sopenharmony_ci} 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_cistatic int omap1_clk_enable_generic(struct omap1_clk *clk) 52962306a36Sopenharmony_ci{ 53062306a36Sopenharmony_ci unsigned long flags; 53162306a36Sopenharmony_ci __u16 regval16; 53262306a36Sopenharmony_ci __u32 regval32; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (unlikely(clk->enable_reg == NULL)) { 53562306a36Sopenharmony_ci printk(KERN_ERR "clock.c: Enable for %s without enable code\n", 53662306a36Sopenharmony_ci clk_hw_get_name(&clk->hw)); 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* protect clk->enable_reg from concurrent access via clk_set_rate() */ 54162306a36Sopenharmony_ci if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) 54262306a36Sopenharmony_ci spin_lock_irqsave(&arm_ckctl_lock, flags); 54362306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) 54462306a36Sopenharmony_ci spin_lock_irqsave(&arm_idlect2_lock, flags); 54562306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) 54662306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); 54762306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) 54862306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); 54962306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) 55062306a36Sopenharmony_ci spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) { 55362306a36Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 55462306a36Sopenharmony_ci regval32 |= (1 << clk->enable_bit); 55562306a36Sopenharmony_ci __raw_writel(regval32, clk->enable_reg); 55662306a36Sopenharmony_ci } else { 55762306a36Sopenharmony_ci regval16 = __raw_readw(clk->enable_reg); 55862306a36Sopenharmony_ci regval16 |= (1 << clk->enable_bit); 55962306a36Sopenharmony_ci __raw_writew(regval16, clk->enable_reg); 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) 56362306a36Sopenharmony_ci spin_unlock_irqrestore(&arm_ckctl_lock, flags); 56462306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) 56562306a36Sopenharmony_ci spin_unlock_irqrestore(&arm_idlect2_lock, flags); 56662306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) 56762306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); 56862306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) 56962306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); 57062306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) 57162306a36Sopenharmony_ci spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return 0; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic void omap1_clk_disable_generic(struct omap1_clk *clk) 57762306a36Sopenharmony_ci{ 57862306a36Sopenharmony_ci unsigned long flags; 57962306a36Sopenharmony_ci __u16 regval16; 58062306a36Sopenharmony_ci __u32 regval32; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci if (clk->enable_reg == NULL) 58362306a36Sopenharmony_ci return; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci /* protect clk->enable_reg from concurrent access via clk_set_rate() */ 58662306a36Sopenharmony_ci if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) 58762306a36Sopenharmony_ci spin_lock_irqsave(&arm_ckctl_lock, flags); 58862306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) 58962306a36Sopenharmony_ci spin_lock_irqsave(&arm_idlect2_lock, flags); 59062306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) 59162306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_0_lock, flags); 59262306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) 59362306a36Sopenharmony_ci spin_lock_irqsave(&mod_conf_ctrl_1_lock, flags); 59462306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) 59562306a36Sopenharmony_ci spin_lock_irqsave(&swd_clk_div_ctrl_sel_lock, flags); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) { 59862306a36Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 59962306a36Sopenharmony_ci regval32 &= ~(1 << clk->enable_bit); 60062306a36Sopenharmony_ci __raw_writel(regval32, clk->enable_reg); 60162306a36Sopenharmony_ci } else { 60262306a36Sopenharmony_ci regval16 = __raw_readw(clk->enable_reg); 60362306a36Sopenharmony_ci regval16 &= ~(1 << clk->enable_bit); 60462306a36Sopenharmony_ci __raw_writew(regval16, clk->enable_reg); 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_CKCTL)) 60862306a36Sopenharmony_ci spin_unlock_irqrestore(&arm_ckctl_lock, flags); 60962306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(ARM_IDLECT2)) 61062306a36Sopenharmony_ci spin_unlock_irqrestore(&arm_idlect2_lock, flags); 61162306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_0)) 61262306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_0_lock, flags); 61362306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(MOD_CONF_CTRL_1)) 61462306a36Sopenharmony_ci spin_unlock_irqrestore(&mod_conf_ctrl_1_lock, flags); 61562306a36Sopenharmony_ci else if (clk->enable_reg == OMAP1_IO_ADDRESS(SWD_CLK_DIV_CTRL_SEL)) 61662306a36Sopenharmony_ci spin_unlock_irqrestore(&swd_clk_div_ctrl_sel_lock, flags); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciconst struct clkops clkops_generic = { 62062306a36Sopenharmony_ci .enable = omap1_clk_enable_generic, 62162306a36Sopenharmony_ci .disable = omap1_clk_disable_generic, 62262306a36Sopenharmony_ci}; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic int omap1_clk_enable_dsp_domain(struct omap1_clk *clk) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci bool api_ck_was_enabled; 62762306a36Sopenharmony_ci int retval = 0; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); 63062306a36Sopenharmony_ci if (!api_ck_was_enabled) 63162306a36Sopenharmony_ci retval = api_ck_p->ops->enable(api_ck_p); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (!retval) { 63462306a36Sopenharmony_ci retval = omap1_clk_enable_generic(clk); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (!api_ck_was_enabled) 63762306a36Sopenharmony_ci api_ck_p->ops->disable(api_ck_p); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci return retval; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void omap1_clk_disable_dsp_domain(struct omap1_clk *clk) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci bool api_ck_was_enabled; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci api_ck_was_enabled = omap1_clk_is_enabled(&api_ck_p->hw); 64862306a36Sopenharmony_ci if (!api_ck_was_enabled) 64962306a36Sopenharmony_ci if (api_ck_p->ops->enable(api_ck_p) < 0) 65062306a36Sopenharmony_ci return; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci omap1_clk_disable_generic(clk); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!api_ck_was_enabled) 65562306a36Sopenharmony_ci api_ck_p->ops->disable(api_ck_p); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ciconst struct clkops clkops_dspck = { 65962306a36Sopenharmony_ci .enable = omap1_clk_enable_dsp_domain, 66062306a36Sopenharmony_ci .disable = omap1_clk_disable_dsp_domain, 66162306a36Sopenharmony_ci}; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 66462306a36Sopenharmony_cistatic int omap1_clk_enable_uart_functional_16xx(struct omap1_clk *clk) 66562306a36Sopenharmony_ci{ 66662306a36Sopenharmony_ci int ret; 66762306a36Sopenharmony_ci struct uart_clk *uclk; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci ret = omap1_clk_enable_generic(clk); 67062306a36Sopenharmony_ci if (ret == 0) { 67162306a36Sopenharmony_ci /* Set smart idle acknowledgement mode */ 67262306a36Sopenharmony_ci uclk = (struct uart_clk *)clk; 67362306a36Sopenharmony_ci omap_writeb((omap_readb(uclk->sysc_addr) & ~0x10) | 8, 67462306a36Sopenharmony_ci uclk->sysc_addr); 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci return ret; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 68162306a36Sopenharmony_cistatic void omap1_clk_disable_uart_functional_16xx(struct omap1_clk *clk) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct uart_clk *uclk; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci /* Set force idle acknowledgement mode */ 68662306a36Sopenharmony_ci uclk = (struct uart_clk *)clk; 68762306a36Sopenharmony_ci omap_writeb((omap_readb(uclk->sysc_addr) & ~0x18), uclk->sysc_addr); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci omap1_clk_disable_generic(clk); 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 69362306a36Sopenharmony_ciconst struct clkops clkops_uart_16xx = { 69462306a36Sopenharmony_ci .enable = omap1_clk_enable_uart_functional_16xx, 69562306a36Sopenharmony_ci .disable = omap1_clk_disable_uart_functional_16xx, 69662306a36Sopenharmony_ci}; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic unsigned long omap1_clk_recalc_rate(struct clk_hw *hw, unsigned long p_rate) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (clk->recalc) 70362306a36Sopenharmony_ci return clk->recalc(clk, p_rate); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci return clk->rate; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic long omap1_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *p_rate) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (clk->round_rate != NULL) 71362306a36Sopenharmony_ci return clk->round_rate(clk, rate, p_rate); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci return omap1_clk_recalc_rate(hw, *p_rate); 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic int omap1_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 72162306a36Sopenharmony_ci int ret = -EINVAL; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (clk->set_rate) 72462306a36Sopenharmony_ci ret = clk->set_rate(clk, rate, p_rate); 72562306a36Sopenharmony_ci return ret; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * Omap1 clock reset and init functions 73062306a36Sopenharmony_ci */ 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic int omap1_clk_init_op(struct clk_hw *hw) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (clk->init) 73762306a36Sopenharmony_ci return clk->init(clk); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci return 0; 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci#ifdef CONFIG_OMAP_RESET_CLOCKS 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic void omap1_clk_disable_unused(struct clk_hw *hw) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct omap1_clk *clk = to_omap1_clk(hw); 74762306a36Sopenharmony_ci const char *name = clk_hw_get_name(hw); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Clocks in the DSP domain need api_ck. Just assume bootloader 75062306a36Sopenharmony_ci * has not enabled any DSP clocks */ 75162306a36Sopenharmony_ci if (clk->enable_reg == DSP_IDLECT2) { 75262306a36Sopenharmony_ci pr_info("Skipping reset check for DSP domain clock \"%s\"\n", name); 75362306a36Sopenharmony_ci return; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci pr_info("Disabling unused clock \"%s\"... ", name); 75762306a36Sopenharmony_ci omap1_clk_disable(hw); 75862306a36Sopenharmony_ci printk(" done\n"); 75962306a36Sopenharmony_ci} 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci#endif 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ciconst struct clk_ops omap1_clk_gate_ops = { 76462306a36Sopenharmony_ci .enable = omap1_clk_enable, 76562306a36Sopenharmony_ci .disable = omap1_clk_disable, 76662306a36Sopenharmony_ci .is_enabled = omap1_clk_is_enabled, 76762306a36Sopenharmony_ci#ifdef CONFIG_OMAP_RESET_CLOCKS 76862306a36Sopenharmony_ci .disable_unused = omap1_clk_disable_unused, 76962306a36Sopenharmony_ci#endif 77062306a36Sopenharmony_ci}; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ciconst struct clk_ops omap1_clk_rate_ops = { 77362306a36Sopenharmony_ci .recalc_rate = omap1_clk_recalc_rate, 77462306a36Sopenharmony_ci .round_rate = omap1_clk_round_rate, 77562306a36Sopenharmony_ci .set_rate = omap1_clk_set_rate, 77662306a36Sopenharmony_ci .init = omap1_clk_init_op, 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ciconst struct clk_ops omap1_clk_full_ops = { 78062306a36Sopenharmony_ci .enable = omap1_clk_enable, 78162306a36Sopenharmony_ci .disable = omap1_clk_disable, 78262306a36Sopenharmony_ci .is_enabled = omap1_clk_is_enabled, 78362306a36Sopenharmony_ci#ifdef CONFIG_OMAP_RESET_CLOCKS 78462306a36Sopenharmony_ci .disable_unused = omap1_clk_disable_unused, 78562306a36Sopenharmony_ci#endif 78662306a36Sopenharmony_ci .recalc_rate = omap1_clk_recalc_rate, 78762306a36Sopenharmony_ci .round_rate = omap1_clk_round_rate, 78862306a36Sopenharmony_ci .set_rate = omap1_clk_set_rate, 78962306a36Sopenharmony_ci .init = omap1_clk_init_op, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci/* 79362306a36Sopenharmony_ci * OMAP specific clock functions shared between omap1 and omap2 79462306a36Sopenharmony_ci */ 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci/* Used for clocks that always have same value as the parent clock */ 79762306a36Sopenharmony_ciunsigned long followparent_recalc(struct omap1_clk *clk, unsigned long p_rate) 79862306a36Sopenharmony_ci{ 79962306a36Sopenharmony_ci return p_rate; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* 80362306a36Sopenharmony_ci * Used for clocks that have the same value as the parent clock, 80462306a36Sopenharmony_ci * divided by some factor 80562306a36Sopenharmony_ci */ 80662306a36Sopenharmony_ciunsigned long omap_fixed_divisor_recalc(struct omap1_clk *clk, unsigned long p_rate) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci WARN_ON(!clk->fixed_div); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci return p_rate / clk->fixed_div; 81162306a36Sopenharmony_ci} 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci/* Propagate rate to children */ 81462306a36Sopenharmony_civoid propagate_rate(struct omap1_clk *tclk) 81562306a36Sopenharmony_ci{ 81662306a36Sopenharmony_ci struct clk *clkp; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* depend on CCF ability to recalculate new rates across whole clock subtree */ 81962306a36Sopenharmony_ci if (WARN_ON(!(clk_hw_get_flags(&tclk->hw) & CLK_GET_RATE_NOCACHE))) 82062306a36Sopenharmony_ci return; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci clkp = clk_get_sys(NULL, clk_hw_get_name(&tclk->hw)); 82362306a36Sopenharmony_ci if (WARN_ON(!clkp)) 82462306a36Sopenharmony_ci return; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci clk_get_rate(clkp); 82762306a36Sopenharmony_ci clk_put(clkp); 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ciconst struct clk_ops omap1_clk_null_ops = { 83162306a36Sopenharmony_ci}; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci/* 83462306a36Sopenharmony_ci * Dummy clock 83562306a36Sopenharmony_ci * 83662306a36Sopenharmony_ci * Used for clock aliases that are needed on some OMAPs, but not others 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_cistruct omap1_clk dummy_ck __refdata = { 83962306a36Sopenharmony_ci .hw.init = CLK_HW_INIT_NO_PARENT("dummy", &omap1_clk_null_ops, 0), 84062306a36Sopenharmony_ci}; 841