18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/mach-omap1/clock.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2005, 2009-2010 Nokia Corporation 68c2ecf20Sopenharmony_ci * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Modified to use omap shared clock framework by 98c2ecf20Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/export.h> 138c2ecf20Sopenharmony_ci#include <linux/list.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/clk.h> 188c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/mach-types.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <mach/hardware.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "soc.h" 258c2ecf20Sopenharmony_ci#include "iomap.h" 268c2ecf20Sopenharmony_ci#include "clock.h" 278c2ecf20Sopenharmony_ci#include "opp.h" 288c2ecf20Sopenharmony_ci#include "sram.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci__u32 arm_idlect1_mask; 318c2ecf20Sopenharmony_cistruct clk *api_ck_p, *ck_dpll1_p, *ck_ref_p; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic LIST_HEAD(clocks); 348c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(clocks_mutex); 358c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(clockfw_lock); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * Omap1 specific clock functions 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ciunsigned long omap1_uart_recalc(struct clk *clk) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci unsigned int val = __raw_readl(clk->enable_reg); 448c2ecf20Sopenharmony_ci return val & 1 << clk->enable_bit ? 48000000 : 12000000; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ciunsigned long omap1_sossi_recalc(struct clk *clk) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci u32 div = omap_readl(MOD_CONF_CTRL_1); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci div = (div >> 17) & 0x7; 528c2ecf20Sopenharmony_ci div++; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci return clk->parent->rate / div; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic void omap1_clk_allow_idle(struct clk *clk) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (!(clk->flags & CLOCK_IDLE_CONTROL)) 628c2ecf20Sopenharmony_ci return; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (iclk->no_idle_count > 0 && !(--iclk->no_idle_count)) 658c2ecf20Sopenharmony_ci arm_idlect1_mask |= 1 << iclk->idlect_shift; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void omap1_clk_deny_idle(struct clk *clk) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct arm_idlect1_clk * iclk = (struct arm_idlect1_clk *)clk; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (!(clk->flags & CLOCK_IDLE_CONTROL)) 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (iclk->no_idle_count++ == 0) 768c2ecf20Sopenharmony_ci arm_idlect1_mask &= ~(1 << iclk->idlect_shift); 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic __u16 verify_ckctl_value(__u16 newval) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci /* This function checks for following limitations set 828c2ecf20Sopenharmony_ci * by the hardware (all conditions must be true): 838c2ecf20Sopenharmony_ci * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 848c2ecf20Sopenharmony_ci * ARM_CK >= TC_CK 858c2ecf20Sopenharmony_ci * DSP_CK >= TC_CK 868c2ecf20Sopenharmony_ci * DSPMMU_CK >= TC_CK 878c2ecf20Sopenharmony_ci * 888c2ecf20Sopenharmony_ci * In addition following rules are enforced: 898c2ecf20Sopenharmony_ci * LCD_CK <= TC_CK 908c2ecf20Sopenharmony_ci * ARMPER_CK <= TC_CK 918c2ecf20Sopenharmony_ci * 928c2ecf20Sopenharmony_ci * However, maximum frequencies are not checked for! 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci __u8 per_exp; 958c2ecf20Sopenharmony_ci __u8 lcd_exp; 968c2ecf20Sopenharmony_ci __u8 arm_exp; 978c2ecf20Sopenharmony_ci __u8 dsp_exp; 988c2ecf20Sopenharmony_ci __u8 tc_exp; 998c2ecf20Sopenharmony_ci __u8 dspmmu_exp; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci per_exp = (newval >> CKCTL_PERDIV_OFFSET) & 3; 1028c2ecf20Sopenharmony_ci lcd_exp = (newval >> CKCTL_LCDDIV_OFFSET) & 3; 1038c2ecf20Sopenharmony_ci arm_exp = (newval >> CKCTL_ARMDIV_OFFSET) & 3; 1048c2ecf20Sopenharmony_ci dsp_exp = (newval >> CKCTL_DSPDIV_OFFSET) & 3; 1058c2ecf20Sopenharmony_ci tc_exp = (newval >> CKCTL_TCDIV_OFFSET) & 3; 1068c2ecf20Sopenharmony_ci dspmmu_exp = (newval >> CKCTL_DSPMMUDIV_OFFSET) & 3; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (dspmmu_exp < dsp_exp) 1098c2ecf20Sopenharmony_ci dspmmu_exp = dsp_exp; 1108c2ecf20Sopenharmony_ci if (dspmmu_exp > dsp_exp+1) 1118c2ecf20Sopenharmony_ci dspmmu_exp = dsp_exp+1; 1128c2ecf20Sopenharmony_ci if (tc_exp < arm_exp) 1138c2ecf20Sopenharmony_ci tc_exp = arm_exp; 1148c2ecf20Sopenharmony_ci if (tc_exp < dspmmu_exp) 1158c2ecf20Sopenharmony_ci tc_exp = dspmmu_exp; 1168c2ecf20Sopenharmony_ci if (tc_exp > lcd_exp) 1178c2ecf20Sopenharmony_ci lcd_exp = tc_exp; 1188c2ecf20Sopenharmony_ci if (tc_exp > per_exp) 1198c2ecf20Sopenharmony_ci per_exp = tc_exp; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci newval &= 0xf000; 1228c2ecf20Sopenharmony_ci newval |= per_exp << CKCTL_PERDIV_OFFSET; 1238c2ecf20Sopenharmony_ci newval |= lcd_exp << CKCTL_LCDDIV_OFFSET; 1248c2ecf20Sopenharmony_ci newval |= arm_exp << CKCTL_ARMDIV_OFFSET; 1258c2ecf20Sopenharmony_ci newval |= dsp_exp << CKCTL_DSPDIV_OFFSET; 1268c2ecf20Sopenharmony_ci newval |= tc_exp << CKCTL_TCDIV_OFFSET; 1278c2ecf20Sopenharmony_ci newval |= dspmmu_exp << CKCTL_DSPMMUDIV_OFFSET; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return newval; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int calc_dsor_exp(struct clk *clk, unsigned long rate) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci /* Note: If target frequency is too low, this function will return 4, 1358c2ecf20Sopenharmony_ci * which is invalid value. Caller must check for this value and act 1368c2ecf20Sopenharmony_ci * accordingly. 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * Note: This function does not check for following limitations set 1398c2ecf20Sopenharmony_ci * by the hardware (all conditions must be true): 1408c2ecf20Sopenharmony_ci * DSPMMU_CK == DSP_CK or DSPMMU_CK == DSP_CK/2 1418c2ecf20Sopenharmony_ci * ARM_CK >= TC_CK 1428c2ecf20Sopenharmony_ci * DSP_CK >= TC_CK 1438c2ecf20Sopenharmony_ci * DSPMMU_CK >= TC_CK 1448c2ecf20Sopenharmony_ci */ 1458c2ecf20Sopenharmony_ci unsigned long realrate; 1468c2ecf20Sopenharmony_ci struct clk * parent; 1478c2ecf20Sopenharmony_ci unsigned dsor_exp; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci parent = clk->parent; 1508c2ecf20Sopenharmony_ci if (unlikely(parent == NULL)) 1518c2ecf20Sopenharmony_ci return -EIO; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci realrate = parent->rate; 1548c2ecf20Sopenharmony_ci for (dsor_exp=0; dsor_exp<4; dsor_exp++) { 1558c2ecf20Sopenharmony_ci if (realrate <= rate) 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci realrate /= 2; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return dsor_exp; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ciunsigned long omap1_ckctl_recalc(struct clk *clk) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci /* Calculate divisor encoded as 2-bit exponent */ 1678c2ecf20Sopenharmony_ci int dsor = 1 << (3 & (omap_readw(ARM_CKCTL) >> clk->rate_offset)); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return clk->parent->rate / dsor; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ciunsigned long omap1_ckctl_recalc_dsp_domain(struct clk *clk) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci int dsor; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* Calculate divisor encoded as 2-bit exponent 1778c2ecf20Sopenharmony_ci * 1788c2ecf20Sopenharmony_ci * The clock control bits are in DSP domain, 1798c2ecf20Sopenharmony_ci * so api_ck is needed for access. 1808c2ecf20Sopenharmony_ci * Note that DSP_CKCTL virt addr = phys addr, so 1818c2ecf20Sopenharmony_ci * we must use __raw_readw() instead of omap_readw(). 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci omap1_clk_enable(api_ck_p); 1848c2ecf20Sopenharmony_ci dsor = 1 << (3 & (__raw_readw(DSP_CKCTL) >> clk->rate_offset)); 1858c2ecf20Sopenharmony_ci omap1_clk_disable(api_ck_p); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return clk->parent->rate / dsor; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* MPU virtual clock functions */ 1918c2ecf20Sopenharmony_ciint omap1_select_table_rate(struct clk *clk, unsigned long rate) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci /* Find the highest supported frequency <= rate and switch to it */ 1948c2ecf20Sopenharmony_ci struct mpu_rate * ptr; 1958c2ecf20Sopenharmony_ci unsigned long ref_rate; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci ref_rate = ck_ref_p->rate; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci for (ptr = omap1_rate_table; ptr->rate; ptr++) { 2008c2ecf20Sopenharmony_ci if (!(ptr->flags & cpu_mask)) 2018c2ecf20Sopenharmony_ci continue; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (ptr->xtal != ref_rate) 2048c2ecf20Sopenharmony_ci continue; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Can check only after xtal frequency check */ 2078c2ecf20Sopenharmony_ci if (ptr->rate <= rate) 2088c2ecf20Sopenharmony_ci break; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (!ptr->rate) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* 2158c2ecf20Sopenharmony_ci * In most cases we should not need to reprogram DPLL. 2168c2ecf20Sopenharmony_ci * Reprogramming the DPLL is tricky, it must be done from SRAM. 2178c2ecf20Sopenharmony_ci */ 2188c2ecf20Sopenharmony_ci omap_sram_reprogram_clock(ptr->dpllctl_val, ptr->ckctl_val); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* XXX Do we need to recalculate the tree below DPLL1 at this point? */ 2218c2ecf20Sopenharmony_ci ck_dpll1_p->rate = ptr->pll_rate; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciint omap1_clk_set_rate_dsp_domain(struct clk *clk, unsigned long rate) 2278c2ecf20Sopenharmony_ci{ 2288c2ecf20Sopenharmony_ci int dsor_exp; 2298c2ecf20Sopenharmony_ci u16 regval; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci dsor_exp = calc_dsor_exp(clk, rate); 2328c2ecf20Sopenharmony_ci if (dsor_exp > 3) 2338c2ecf20Sopenharmony_ci dsor_exp = -EINVAL; 2348c2ecf20Sopenharmony_ci if (dsor_exp < 0) 2358c2ecf20Sopenharmony_ci return dsor_exp; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci regval = __raw_readw(DSP_CKCTL); 2388c2ecf20Sopenharmony_ci regval &= ~(3 << clk->rate_offset); 2398c2ecf20Sopenharmony_ci regval |= dsor_exp << clk->rate_offset; 2408c2ecf20Sopenharmony_ci __raw_writew(regval, DSP_CKCTL); 2418c2ecf20Sopenharmony_ci clk->rate = clk->parent->rate / (1 << dsor_exp); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cilong omap1_clk_round_rate_ckctl_arm(struct clk *clk, unsigned long rate) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci int dsor_exp = calc_dsor_exp(clk, rate); 2498c2ecf20Sopenharmony_ci if (dsor_exp < 0) 2508c2ecf20Sopenharmony_ci return dsor_exp; 2518c2ecf20Sopenharmony_ci if (dsor_exp > 3) 2528c2ecf20Sopenharmony_ci dsor_exp = 3; 2538c2ecf20Sopenharmony_ci return clk->parent->rate / (1 << dsor_exp); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciint omap1_clk_set_rate_ckctl_arm(struct clk *clk, unsigned long rate) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int dsor_exp; 2598c2ecf20Sopenharmony_ci u16 regval; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci dsor_exp = calc_dsor_exp(clk, rate); 2628c2ecf20Sopenharmony_ci if (dsor_exp > 3) 2638c2ecf20Sopenharmony_ci dsor_exp = -EINVAL; 2648c2ecf20Sopenharmony_ci if (dsor_exp < 0) 2658c2ecf20Sopenharmony_ci return dsor_exp; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci regval = omap_readw(ARM_CKCTL); 2688c2ecf20Sopenharmony_ci regval &= ~(3 << clk->rate_offset); 2698c2ecf20Sopenharmony_ci regval |= dsor_exp << clk->rate_offset; 2708c2ecf20Sopenharmony_ci regval = verify_ckctl_value(regval); 2718c2ecf20Sopenharmony_ci omap_writew(regval, ARM_CKCTL); 2728c2ecf20Sopenharmony_ci clk->rate = clk->parent->rate / (1 << dsor_exp); 2738c2ecf20Sopenharmony_ci return 0; 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cilong omap1_round_to_table_rate(struct clk *clk, unsigned long rate) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci /* Find the highest supported frequency <= rate */ 2798c2ecf20Sopenharmony_ci struct mpu_rate * ptr; 2808c2ecf20Sopenharmony_ci long highest_rate; 2818c2ecf20Sopenharmony_ci unsigned long ref_rate; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci ref_rate = ck_ref_p->rate; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci highest_rate = -EINVAL; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci for (ptr = omap1_rate_table; ptr->rate; ptr++) { 2888c2ecf20Sopenharmony_ci if (!(ptr->flags & cpu_mask)) 2898c2ecf20Sopenharmony_ci continue; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (ptr->xtal != ref_rate) 2928c2ecf20Sopenharmony_ci continue; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci highest_rate = ptr->rate; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Can check only after xtal frequency check */ 2978c2ecf20Sopenharmony_ci if (ptr->rate <= rate) 2988c2ecf20Sopenharmony_ci break; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return highest_rate; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic unsigned calc_ext_dsor(unsigned long rate) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci unsigned dsor; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* MCLK and BCLK divisor selection is not linear: 3098c2ecf20Sopenharmony_ci * freq = 96MHz / dsor 3108c2ecf20Sopenharmony_ci * 3118c2ecf20Sopenharmony_ci * RATIO_SEL range: dsor <-> RATIO_SEL 3128c2ecf20Sopenharmony_ci * 0..6: (RATIO_SEL+2) <-> (dsor-2) 3138c2ecf20Sopenharmony_ci * 6..48: (8+(RATIO_SEL-6)*2) <-> ((dsor-8)/2+6) 3148c2ecf20Sopenharmony_ci * Minimum dsor is 2 and maximum is 96. Odd divisors starting from 9 3158c2ecf20Sopenharmony_ci * can not be used. 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci for (dsor = 2; dsor < 96; ++dsor) { 3188c2ecf20Sopenharmony_ci if ((dsor & 1) && dsor > 8) 3198c2ecf20Sopenharmony_ci continue; 3208c2ecf20Sopenharmony_ci if (rate >= 96000000 / dsor) 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci return dsor; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci/* XXX Only needed on 1510 */ 3278c2ecf20Sopenharmony_ciint omap1_set_uart_rate(struct clk *clk, unsigned long rate) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci unsigned int val; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci val = __raw_readl(clk->enable_reg); 3328c2ecf20Sopenharmony_ci if (rate == 12000000) 3338c2ecf20Sopenharmony_ci val &= ~(1 << clk->enable_bit); 3348c2ecf20Sopenharmony_ci else if (rate == 48000000) 3358c2ecf20Sopenharmony_ci val |= (1 << clk->enable_bit); 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci return -EINVAL; 3388c2ecf20Sopenharmony_ci __raw_writel(val, clk->enable_reg); 3398c2ecf20Sopenharmony_ci clk->rate = rate; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci/* External clock (MCLK & BCLK) functions */ 3458c2ecf20Sopenharmony_ciint omap1_set_ext_clk_rate(struct clk *clk, unsigned long rate) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci unsigned dsor; 3488c2ecf20Sopenharmony_ci __u16 ratio_bits; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dsor = calc_ext_dsor(rate); 3518c2ecf20Sopenharmony_ci clk->rate = 96000000 / dsor; 3528c2ecf20Sopenharmony_ci if (dsor > 8) 3538c2ecf20Sopenharmony_ci ratio_bits = ((dsor - 8) / 2 + 6) << 2; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci ratio_bits = (dsor - 2) << 2; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci ratio_bits |= __raw_readw(clk->enable_reg) & ~0xfd; 3588c2ecf20Sopenharmony_ci __raw_writew(ratio_bits, clk->enable_reg); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return 0; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ciint omap1_set_sossi_rate(struct clk *clk, unsigned long rate) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci u32 l; 3668c2ecf20Sopenharmony_ci int div; 3678c2ecf20Sopenharmony_ci unsigned long p_rate; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci p_rate = clk->parent->rate; 3708c2ecf20Sopenharmony_ci /* Round towards slower frequency */ 3718c2ecf20Sopenharmony_ci div = (p_rate + rate - 1) / rate; 3728c2ecf20Sopenharmony_ci div--; 3738c2ecf20Sopenharmony_ci if (div < 0 || div > 7) 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci l = omap_readl(MOD_CONF_CTRL_1); 3778c2ecf20Sopenharmony_ci l &= ~(7 << 17); 3788c2ecf20Sopenharmony_ci l |= div << 17; 3798c2ecf20Sopenharmony_ci omap_writel(l, MOD_CONF_CTRL_1); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci clk->rate = p_rate / (div + 1); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return 0; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cilong omap1_round_ext_clk_rate(struct clk *clk, unsigned long rate) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci return 96000000 / calc_ext_dsor(rate); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_civoid omap1_init_ext_clk(struct clk *clk) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci unsigned dsor; 3948c2ecf20Sopenharmony_ci __u16 ratio_bits; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Determine current rate and ensure clock is based on 96MHz APLL */ 3978c2ecf20Sopenharmony_ci ratio_bits = __raw_readw(clk->enable_reg) & ~1; 3988c2ecf20Sopenharmony_ci __raw_writew(ratio_bits, clk->enable_reg); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ratio_bits = (ratio_bits & 0xfc) >> 2; 4018c2ecf20Sopenharmony_ci if (ratio_bits > 6) 4028c2ecf20Sopenharmony_ci dsor = (ratio_bits - 6) * 2 + 8; 4038c2ecf20Sopenharmony_ci else 4048c2ecf20Sopenharmony_ci dsor = ratio_bits + 2; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci clk-> rate = 96000000 / dsor; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ciint omap1_clk_enable(struct clk *clk) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci int ret = 0; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (clk->usecount++ == 0) { 4148c2ecf20Sopenharmony_ci if (clk->parent) { 4158c2ecf20Sopenharmony_ci ret = omap1_clk_enable(clk->parent); 4168c2ecf20Sopenharmony_ci if (ret) 4178c2ecf20Sopenharmony_ci goto err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci if (clk->flags & CLOCK_NO_IDLE_PARENT) 4208c2ecf20Sopenharmony_ci omap1_clk_deny_idle(clk->parent); 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = clk->ops->enable(clk); 4248c2ecf20Sopenharmony_ci if (ret) { 4258c2ecf20Sopenharmony_ci if (clk->parent) 4268c2ecf20Sopenharmony_ci omap1_clk_disable(clk->parent); 4278c2ecf20Sopenharmony_ci goto err; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci return ret; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cierr: 4338c2ecf20Sopenharmony_ci clk->usecount--; 4348c2ecf20Sopenharmony_ci return ret; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_civoid omap1_clk_disable(struct clk *clk) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci if (clk->usecount > 0 && !(--clk->usecount)) { 4408c2ecf20Sopenharmony_ci clk->ops->disable(clk); 4418c2ecf20Sopenharmony_ci if (likely(clk->parent)) { 4428c2ecf20Sopenharmony_ci omap1_clk_disable(clk->parent); 4438c2ecf20Sopenharmony_ci if (clk->flags & CLOCK_NO_IDLE_PARENT) 4448c2ecf20Sopenharmony_ci omap1_clk_allow_idle(clk->parent); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int omap1_clk_enable_generic(struct clk *clk) 4508c2ecf20Sopenharmony_ci{ 4518c2ecf20Sopenharmony_ci __u16 regval16; 4528c2ecf20Sopenharmony_ci __u32 regval32; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci if (unlikely(clk->enable_reg == NULL)) { 4558c2ecf20Sopenharmony_ci printk(KERN_ERR "clock.c: Enable for %s without enable code\n", 4568c2ecf20Sopenharmony_ci clk->name); 4578c2ecf20Sopenharmony_ci return -EINVAL; 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) { 4618c2ecf20Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 4628c2ecf20Sopenharmony_ci regval32 |= (1 << clk->enable_bit); 4638c2ecf20Sopenharmony_ci __raw_writel(regval32, clk->enable_reg); 4648c2ecf20Sopenharmony_ci } else { 4658c2ecf20Sopenharmony_ci regval16 = __raw_readw(clk->enable_reg); 4668c2ecf20Sopenharmony_ci regval16 |= (1 << clk->enable_bit); 4678c2ecf20Sopenharmony_ci __raw_writew(regval16, clk->enable_reg); 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci return 0; 4718c2ecf20Sopenharmony_ci} 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic void omap1_clk_disable_generic(struct clk *clk) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci __u16 regval16; 4768c2ecf20Sopenharmony_ci __u32 regval32; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (clk->enable_reg == NULL) 4798c2ecf20Sopenharmony_ci return; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) { 4828c2ecf20Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 4838c2ecf20Sopenharmony_ci regval32 &= ~(1 << clk->enable_bit); 4848c2ecf20Sopenharmony_ci __raw_writel(regval32, clk->enable_reg); 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci regval16 = __raw_readw(clk->enable_reg); 4878c2ecf20Sopenharmony_ci regval16 &= ~(1 << clk->enable_bit); 4888c2ecf20Sopenharmony_ci __raw_writew(regval16, clk->enable_reg); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci} 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciconst struct clkops clkops_generic = { 4938c2ecf20Sopenharmony_ci .enable = omap1_clk_enable_generic, 4948c2ecf20Sopenharmony_ci .disable = omap1_clk_disable_generic, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic int omap1_clk_enable_dsp_domain(struct clk *clk) 4988c2ecf20Sopenharmony_ci{ 4998c2ecf20Sopenharmony_ci int retval; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci retval = omap1_clk_enable(api_ck_p); 5028c2ecf20Sopenharmony_ci if (!retval) { 5038c2ecf20Sopenharmony_ci retval = omap1_clk_enable_generic(clk); 5048c2ecf20Sopenharmony_ci omap1_clk_disable(api_ck_p); 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return retval; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void omap1_clk_disable_dsp_domain(struct clk *clk) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci if (omap1_clk_enable(api_ck_p) == 0) { 5138c2ecf20Sopenharmony_ci omap1_clk_disable_generic(clk); 5148c2ecf20Sopenharmony_ci omap1_clk_disable(api_ck_p); 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ciconst struct clkops clkops_dspck = { 5198c2ecf20Sopenharmony_ci .enable = omap1_clk_enable_dsp_domain, 5208c2ecf20Sopenharmony_ci .disable = omap1_clk_disable_dsp_domain, 5218c2ecf20Sopenharmony_ci}; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 5248c2ecf20Sopenharmony_cistatic int omap1_clk_enable_uart_functional_16xx(struct clk *clk) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci int ret; 5278c2ecf20Sopenharmony_ci struct uart_clk *uclk; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci ret = omap1_clk_enable_generic(clk); 5308c2ecf20Sopenharmony_ci if (ret == 0) { 5318c2ecf20Sopenharmony_ci /* Set smart idle acknowledgement mode */ 5328c2ecf20Sopenharmony_ci uclk = (struct uart_clk *)clk; 5338c2ecf20Sopenharmony_ci omap_writeb((omap_readb(uclk->sysc_addr) & ~0x10) | 8, 5348c2ecf20Sopenharmony_ci uclk->sysc_addr); 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return ret; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 5418c2ecf20Sopenharmony_cistatic void omap1_clk_disable_uart_functional_16xx(struct clk *clk) 5428c2ecf20Sopenharmony_ci{ 5438c2ecf20Sopenharmony_ci struct uart_clk *uclk; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci /* Set force idle acknowledgement mode */ 5468c2ecf20Sopenharmony_ci uclk = (struct uart_clk *)clk; 5478c2ecf20Sopenharmony_ci omap_writeb((omap_readb(uclk->sysc_addr) & ~0x18), uclk->sysc_addr); 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci omap1_clk_disable_generic(clk); 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* XXX SYSC register handling does not belong in the clock framework */ 5538c2ecf20Sopenharmony_ciconst struct clkops clkops_uart_16xx = { 5548c2ecf20Sopenharmony_ci .enable = omap1_clk_enable_uart_functional_16xx, 5558c2ecf20Sopenharmony_ci .disable = omap1_clk_disable_uart_functional_16xx, 5568c2ecf20Sopenharmony_ci}; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_cilong omap1_clk_round_rate(struct clk *clk, unsigned long rate) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci if (clk->round_rate != NULL) 5618c2ecf20Sopenharmony_ci return clk->round_rate(clk, rate); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci return clk->rate; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ciint omap1_clk_set_rate(struct clk *clk, unsigned long rate) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci int ret = -EINVAL; 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (clk->set_rate) 5718c2ecf20Sopenharmony_ci ret = clk->set_rate(clk, rate); 5728c2ecf20Sopenharmony_ci return ret; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci/* 5768c2ecf20Sopenharmony_ci * Omap1 clock reset and init functions 5778c2ecf20Sopenharmony_ci */ 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_RESET_CLOCKS 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_civoid omap1_clk_disable_unused(struct clk *clk) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci __u32 regval32; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci /* Clocks in the DSP domain need api_ck. Just assume bootloader 5868c2ecf20Sopenharmony_ci * has not enabled any DSP clocks */ 5878c2ecf20Sopenharmony_ci if (clk->enable_reg == DSP_IDLECT2) { 5888c2ecf20Sopenharmony_ci pr_info("Skipping reset check for DSP domain clock \"%s\"\n", 5898c2ecf20Sopenharmony_ci clk->name); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Is the clock already disabled? */ 5948c2ecf20Sopenharmony_ci if (clk->flags & ENABLE_REG_32BIT) 5958c2ecf20Sopenharmony_ci regval32 = __raw_readl(clk->enable_reg); 5968c2ecf20Sopenharmony_ci else 5978c2ecf20Sopenharmony_ci regval32 = __raw_readw(clk->enable_reg); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if ((regval32 & (1 << clk->enable_bit)) == 0) 6008c2ecf20Sopenharmony_ci return; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci printk(KERN_INFO "Disabling unused clock \"%s\"... ", clk->name); 6038c2ecf20Sopenharmony_ci clk->ops->disable(clk); 6048c2ecf20Sopenharmony_ci printk(" done\n"); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci#endif 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ciint clk_enable(struct clk *clk) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci unsigned long flags; 6138c2ecf20Sopenharmony_ci int ret; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 6168c2ecf20Sopenharmony_ci return -EINVAL; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 6198c2ecf20Sopenharmony_ci ret = omap1_clk_enable(clk); 6208c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_enable); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_civoid clk_disable(struct clk *clk) 6278c2ecf20Sopenharmony_ci{ 6288c2ecf20Sopenharmony_ci unsigned long flags; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 6318c2ecf20Sopenharmony_ci return; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 6348c2ecf20Sopenharmony_ci if (clk->usecount == 0) { 6358c2ecf20Sopenharmony_ci pr_err("Trying disable clock %s with 0 usecount\n", 6368c2ecf20Sopenharmony_ci clk->name); 6378c2ecf20Sopenharmony_ci WARN_ON(1); 6388c2ecf20Sopenharmony_ci goto out; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci omap1_clk_disable(clk); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ciout: 6448c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_disable); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ciunsigned long clk_get_rate(struct clk *clk) 6498c2ecf20Sopenharmony_ci{ 6508c2ecf20Sopenharmony_ci unsigned long flags; 6518c2ecf20Sopenharmony_ci unsigned long ret; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 6578c2ecf20Sopenharmony_ci ret = clk->rate; 6588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci return ret; 6618c2ecf20Sopenharmony_ci} 6628c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_get_rate); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci/* 6658c2ecf20Sopenharmony_ci * Optional clock functions defined in include/linux/clk.h 6668c2ecf20Sopenharmony_ci */ 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cilong clk_round_rate(struct clk *clk, unsigned long rate) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci unsigned long flags; 6718c2ecf20Sopenharmony_ci long ret; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 6748c2ecf20Sopenharmony_ci return 0; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 6778c2ecf20Sopenharmony_ci ret = omap1_clk_round_rate(clk, rate); 6788c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return ret; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_round_rate); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ciint clk_set_rate(struct clk *clk, unsigned long rate) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci unsigned long flags; 6878c2ecf20Sopenharmony_ci int ret = -EINVAL; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 6908c2ecf20Sopenharmony_ci return ret; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 6938c2ecf20Sopenharmony_ci ret = omap1_clk_set_rate(clk, rate); 6948c2ecf20Sopenharmony_ci if (ret == 0) 6958c2ecf20Sopenharmony_ci propagate_rate(clk); 6968c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci return ret; 6998c2ecf20Sopenharmony_ci} 7008c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_set_rate); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ciint clk_set_parent(struct clk *clk, struct clk *parent) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci WARN_ONCE(1, "clk_set_parent() not implemented for OMAP1\n"); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return -EINVAL; 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_set_parent); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistruct clk *clk_get_parent(struct clk *clk) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci return clk->parent; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_get_parent); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci/* 7178c2ecf20Sopenharmony_ci * OMAP specific clock functions shared between omap1 and omap2 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci/* Used for clocks that always have same value as the parent clock */ 7218c2ecf20Sopenharmony_ciunsigned long followparent_recalc(struct clk *clk) 7228c2ecf20Sopenharmony_ci{ 7238c2ecf20Sopenharmony_ci return clk->parent->rate; 7248c2ecf20Sopenharmony_ci} 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci/* 7278c2ecf20Sopenharmony_ci * Used for clocks that have the same value as the parent clock, 7288c2ecf20Sopenharmony_ci * divided by some factor 7298c2ecf20Sopenharmony_ci */ 7308c2ecf20Sopenharmony_ciunsigned long omap_fixed_divisor_recalc(struct clk *clk) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci WARN_ON(!clk->fixed_div); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci return clk->parent->rate / clk->fixed_div; 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_civoid clk_reparent(struct clk *child, struct clk *parent) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci list_del_init(&child->sibling); 7408c2ecf20Sopenharmony_ci if (parent) 7418c2ecf20Sopenharmony_ci list_add(&child->sibling, &parent->children); 7428c2ecf20Sopenharmony_ci child->parent = parent; 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* now do the debugfs renaming to reattach the child 7458c2ecf20Sopenharmony_ci to the proper parent */ 7468c2ecf20Sopenharmony_ci} 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci/* Propagate rate to children */ 7498c2ecf20Sopenharmony_civoid propagate_rate(struct clk *tclk) 7508c2ecf20Sopenharmony_ci{ 7518c2ecf20Sopenharmony_ci struct clk *clkp; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci list_for_each_entry(clkp, &tclk->children, sibling) { 7548c2ecf20Sopenharmony_ci if (clkp->recalc) 7558c2ecf20Sopenharmony_ci clkp->rate = clkp->recalc(clkp); 7568c2ecf20Sopenharmony_ci propagate_rate(clkp); 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci} 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_cistatic LIST_HEAD(root_clks); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci/** 7638c2ecf20Sopenharmony_ci * recalculate_root_clocks - recalculate and propagate all root clocks 7648c2ecf20Sopenharmony_ci * 7658c2ecf20Sopenharmony_ci * Recalculates all root clocks (clocks with no parent), which if the 7668c2ecf20Sopenharmony_ci * clock's .recalc is set correctly, should also propagate their rates. 7678c2ecf20Sopenharmony_ci * Called at init. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_civoid recalculate_root_clocks(void) 7708c2ecf20Sopenharmony_ci{ 7718c2ecf20Sopenharmony_ci struct clk *clkp; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci list_for_each_entry(clkp, &root_clks, sibling) { 7748c2ecf20Sopenharmony_ci if (clkp->recalc) 7758c2ecf20Sopenharmony_ci clkp->rate = clkp->recalc(clkp); 7768c2ecf20Sopenharmony_ci propagate_rate(clkp); 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci/** 7818c2ecf20Sopenharmony_ci * clk_preinit - initialize any fields in the struct clk before clk init 7828c2ecf20Sopenharmony_ci * @clk: struct clk * to initialize 7838c2ecf20Sopenharmony_ci * 7848c2ecf20Sopenharmony_ci * Initialize any struct clk fields needed before normal clk initialization 7858c2ecf20Sopenharmony_ci * can run. No return value. 7868c2ecf20Sopenharmony_ci */ 7878c2ecf20Sopenharmony_civoid clk_preinit(struct clk *clk) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clk->children); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ciint clk_register(struct clk *clk) 7938c2ecf20Sopenharmony_ci{ 7948c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 7958c2ecf20Sopenharmony_ci return -EINVAL; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci /* 7988c2ecf20Sopenharmony_ci * trap out already registered clocks 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci if (clk->node.next || clk->node.prev) 8018c2ecf20Sopenharmony_ci return 0; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 8048c2ecf20Sopenharmony_ci if (clk->parent) 8058c2ecf20Sopenharmony_ci list_add(&clk->sibling, &clk->parent->children); 8068c2ecf20Sopenharmony_ci else 8078c2ecf20Sopenharmony_ci list_add(&clk->sibling, &root_clks); 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci list_add(&clk->node, &clocks); 8108c2ecf20Sopenharmony_ci if (clk->init) 8118c2ecf20Sopenharmony_ci clk->init(clk); 8128c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_register); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_civoid clk_unregister(struct clk *clk) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 8218c2ecf20Sopenharmony_ci return; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 8248c2ecf20Sopenharmony_ci list_del(&clk->sibling); 8258c2ecf20Sopenharmony_ci list_del(&clk->node); 8268c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 8278c2ecf20Sopenharmony_ci} 8288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_unregister); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_civoid clk_enable_init_clocks(void) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct clk *clkp; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci list_for_each_entry(clkp, &clocks, node) 8358c2ecf20Sopenharmony_ci if (clkp->flags & ENABLE_ON_INIT) 8368c2ecf20Sopenharmony_ci clk_enable(clkp); 8378c2ecf20Sopenharmony_ci} 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci/** 8408c2ecf20Sopenharmony_ci * omap_clk_get_by_name - locate OMAP struct clk by its name 8418c2ecf20Sopenharmony_ci * @name: name of the struct clk to locate 8428c2ecf20Sopenharmony_ci * 8438c2ecf20Sopenharmony_ci * Locate an OMAP struct clk by its name. Assumes that struct clk 8448c2ecf20Sopenharmony_ci * names are unique. Returns NULL if not found or a pointer to the 8458c2ecf20Sopenharmony_ci * struct clk if found. 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_cistruct clk *omap_clk_get_by_name(const char *name) 8488c2ecf20Sopenharmony_ci{ 8498c2ecf20Sopenharmony_ci struct clk *c; 8508c2ecf20Sopenharmony_ci struct clk *ret = NULL; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci list_for_each_entry(c, &clocks, node) { 8558c2ecf20Sopenharmony_ci if (!strcmp(c->name, name)) { 8568c2ecf20Sopenharmony_ci ret = c; 8578c2ecf20Sopenharmony_ci break; 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ci return ret; 8648c2ecf20Sopenharmony_ci} 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ciint omap_clk_enable_autoidle_all(void) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci struct clk *c; 8698c2ecf20Sopenharmony_ci unsigned long flags; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci list_for_each_entry(c, &clocks, node) 8748c2ecf20Sopenharmony_ci if (c->ops->allow_idle) 8758c2ecf20Sopenharmony_ci c->ops->allow_idle(c); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci return 0; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ciint omap_clk_disable_autoidle_all(void) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct clk *c; 8858c2ecf20Sopenharmony_ci unsigned long flags; 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci list_for_each_entry(c, &clocks, node) 8908c2ecf20Sopenharmony_ci if (c->ops->deny_idle) 8918c2ecf20Sopenharmony_ci c->ops->deny_idle(c); 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci/* 8998c2ecf20Sopenharmony_ci * Low level helpers 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic int clkll_enable_null(struct clk *clk) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_cistatic void clkll_disable_null(struct clk *clk) 9078c2ecf20Sopenharmony_ci{ 9088c2ecf20Sopenharmony_ci} 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ciconst struct clkops clkops_null = { 9118c2ecf20Sopenharmony_ci .enable = clkll_enable_null, 9128c2ecf20Sopenharmony_ci .disable = clkll_disable_null, 9138c2ecf20Sopenharmony_ci}; 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/* 9168c2ecf20Sopenharmony_ci * Dummy clock 9178c2ecf20Sopenharmony_ci * 9188c2ecf20Sopenharmony_ci * Used for clock aliases that are needed on some OMAPs, but not others 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_cistruct clk dummy_ck = { 9218c2ecf20Sopenharmony_ci .name = "dummy", 9228c2ecf20Sopenharmony_ci .ops = &clkops_null, 9238c2ecf20Sopenharmony_ci}; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci/* 9268c2ecf20Sopenharmony_ci * 9278c2ecf20Sopenharmony_ci */ 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci#ifdef CONFIG_OMAP_RESET_CLOCKS 9308c2ecf20Sopenharmony_ci/* 9318c2ecf20Sopenharmony_ci * Disable any unused clocks left on by the bootloader 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_cistatic int __init clk_disable_unused(void) 9348c2ecf20Sopenharmony_ci{ 9358c2ecf20Sopenharmony_ci struct clk *ck; 9368c2ecf20Sopenharmony_ci unsigned long flags; 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci pr_info("clock: disabling unused clocks to save power\n"); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 9418c2ecf20Sopenharmony_ci list_for_each_entry(ck, &clocks, node) { 9428c2ecf20Sopenharmony_ci if (ck->ops == &clkops_null) 9438c2ecf20Sopenharmony_ci continue; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci if (ck->usecount > 0 || !ck->enable_reg) 9468c2ecf20Sopenharmony_ci continue; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci omap1_clk_disable_unused(ck); 9498c2ecf20Sopenharmony_ci } 9508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_cilate_initcall(clk_disable_unused); 9558c2ecf20Sopenharmony_cilate_initcall(omap_clk_enable_autoidle_all); 9568c2ecf20Sopenharmony_ci#endif 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) 9598c2ecf20Sopenharmony_ci/* 9608c2ecf20Sopenharmony_ci * debugfs support to trace clock tree hierarchy and attributes 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 9648c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_cistatic struct dentry *clk_debugfs_root; 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_cistatic int debug_clock_show(struct seq_file *s, void *unused) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct clk *c; 9718c2ecf20Sopenharmony_ci struct clk *pa; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 9748c2ecf20Sopenharmony_ci seq_printf(s, "%-30s %-30s %-10s %s\n", 9758c2ecf20Sopenharmony_ci "clock-name", "parent-name", "rate", "use-count"); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci list_for_each_entry(c, &clocks, node) { 9788c2ecf20Sopenharmony_ci pa = c->parent; 9798c2ecf20Sopenharmony_ci seq_printf(s, "%-30s %-30s %-10lu %d\n", 9808c2ecf20Sopenharmony_ci c->name, pa ? pa->name : "none", c->rate, 9818c2ecf20Sopenharmony_ci c->usecount); 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci return 0; 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(debug_clock); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic void clk_debugfs_register_one(struct clk *c) 9918c2ecf20Sopenharmony_ci{ 9928c2ecf20Sopenharmony_ci struct dentry *d; 9938c2ecf20Sopenharmony_ci struct clk *pa = c->parent; 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci d = debugfs_create_dir(c->name, pa ? pa->dent : clk_debugfs_root); 9968c2ecf20Sopenharmony_ci c->dent = d; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci debugfs_create_u8("usecount", S_IRUGO, c->dent, &c->usecount); 9998c2ecf20Sopenharmony_ci debugfs_create_ulong("rate", S_IRUGO, c->dent, &c->rate); 10008c2ecf20Sopenharmony_ci debugfs_create_x8("flags", S_IRUGO, c->dent, &c->flags); 10018c2ecf20Sopenharmony_ci} 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_cistatic void clk_debugfs_register(struct clk *c) 10048c2ecf20Sopenharmony_ci{ 10058c2ecf20Sopenharmony_ci struct clk *pa = c->parent; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (pa && !pa->dent) 10088c2ecf20Sopenharmony_ci clk_debugfs_register(pa); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci if (!c->dent) 10118c2ecf20Sopenharmony_ci clk_debugfs_register_one(c); 10128c2ecf20Sopenharmony_ci} 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_cistatic int __init clk_debugfs_init(void) 10158c2ecf20Sopenharmony_ci{ 10168c2ecf20Sopenharmony_ci struct clk *c; 10178c2ecf20Sopenharmony_ci struct dentry *d; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci d = debugfs_create_dir("clock", NULL); 10208c2ecf20Sopenharmony_ci clk_debugfs_root = d; 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci list_for_each_entry(c, &clocks, node) 10238c2ecf20Sopenharmony_ci clk_debugfs_register(c); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci debugfs_create_file("summary", S_IRUGO, d, NULL, &debug_clock_fops); 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci return 0; 10288c2ecf20Sopenharmony_ci} 10298c2ecf20Sopenharmony_cilate_initcall(clk_debugfs_init); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */ 1032