18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Clock and PLL control for C64x+ devices 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010, 2011 Texas Instruments. 68c2ecf20Sopenharmony_ci * Contributed by: Mark Salter <msalter@redhat.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copied heavily from arm/mach-davinci/clock.c, so: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2006-2007 Texas Instruments. 118c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Deep Root Systems, LLC 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 168c2ecf20Sopenharmony_ci#include <linux/clk.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/err.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <asm/clock.h> 218c2ecf20Sopenharmony_ci#include <asm/soc.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic LIST_HEAD(clocks); 248c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(clocks_mutex); 258c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(clockfw_lock); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic void __clk_enable(struct clk *clk) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci if (clk->parent) 308c2ecf20Sopenharmony_ci __clk_enable(clk->parent); 318c2ecf20Sopenharmony_ci clk->usecount++; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void __clk_disable(struct clk *clk) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci if (WARN_ON(clk->usecount == 0)) 378c2ecf20Sopenharmony_ci return; 388c2ecf20Sopenharmony_ci --clk->usecount; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci if (clk->parent) 418c2ecf20Sopenharmony_ci __clk_disable(clk->parent); 428c2ecf20Sopenharmony_ci} 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ciint clk_enable(struct clk *clk) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci unsigned long flags; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 498c2ecf20Sopenharmony_ci return -EINVAL; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 528c2ecf20Sopenharmony_ci __clk_enable(clk); 538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_enable); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_civoid clk_disable(struct clk *clk) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci unsigned long flags; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 648c2ecf20Sopenharmony_ci return; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 678c2ecf20Sopenharmony_ci __clk_disable(clk); 688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_disable); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ciunsigned long clk_get_rate(struct clk *clk) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 758c2ecf20Sopenharmony_ci return -EINVAL; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return clk->rate; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_get_rate); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cilong clk_round_rate(struct clk *clk, unsigned long rate) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 848c2ecf20Sopenharmony_ci return -EINVAL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (clk->round_rate) 878c2ecf20Sopenharmony_ci return clk->round_rate(clk, rate); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return clk->rate; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_round_rate); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* Propagate rate to children */ 948c2ecf20Sopenharmony_cistatic void propagate_rate(struct clk *root) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct clk *clk; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci list_for_each_entry(clk, &root->children, childnode) { 998c2ecf20Sopenharmony_ci if (clk->recalc) 1008c2ecf20Sopenharmony_ci clk->rate = clk->recalc(clk); 1018c2ecf20Sopenharmony_ci propagate_rate(clk); 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciint clk_set_rate(struct clk *clk, unsigned long rate) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci unsigned long flags; 1088c2ecf20Sopenharmony_ci int ret = -EINVAL; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 1118c2ecf20Sopenharmony_ci return ret; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (clk->set_rate) 1148c2ecf20Sopenharmony_ci ret = clk->set_rate(clk, rate); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 1178c2ecf20Sopenharmony_ci if (ret == 0) { 1188c2ecf20Sopenharmony_ci if (clk->recalc) 1198c2ecf20Sopenharmony_ci clk->rate = clk->recalc(clk); 1208c2ecf20Sopenharmony_ci propagate_rate(clk); 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return ret; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_set_rate); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint clk_set_parent(struct clk *clk, struct clk *parent) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci unsigned long flags; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 1338c2ecf20Sopenharmony_ci return -EINVAL; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Cannot change parent on enabled clock */ 1368c2ecf20Sopenharmony_ci if (WARN_ON(clk->usecount)) 1378c2ecf20Sopenharmony_ci return -EINVAL; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 1408c2ecf20Sopenharmony_ci clk->parent = parent; 1418c2ecf20Sopenharmony_ci list_del_init(&clk->childnode); 1428c2ecf20Sopenharmony_ci list_add(&clk->childnode, &clk->parent->children); 1438c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci spin_lock_irqsave(&clockfw_lock, flags); 1468c2ecf20Sopenharmony_ci if (clk->recalc) 1478c2ecf20Sopenharmony_ci clk->rate = clk->recalc(clk); 1488c2ecf20Sopenharmony_ci propagate_rate(clk); 1498c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&clockfw_lock, flags); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci return 0; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_set_parent); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ciint clk_register(struct clk *clk) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 1588c2ecf20Sopenharmony_ci return -EINVAL; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (WARN(clk->parent && !clk->parent->rate, 1618c2ecf20Sopenharmony_ci "CLK: %s parent %s has no rate!\n", 1628c2ecf20Sopenharmony_ci clk->name, clk->parent->name)) 1638c2ecf20Sopenharmony_ci return -EINVAL; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 1668c2ecf20Sopenharmony_ci list_add_tail(&clk->node, &clocks); 1678c2ecf20Sopenharmony_ci if (clk->parent) 1688c2ecf20Sopenharmony_ci list_add_tail(&clk->childnode, &clk->parent->children); 1698c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* If rate is already set, use it */ 1728c2ecf20Sopenharmony_ci if (clk->rate) 1738c2ecf20Sopenharmony_ci return 0; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci /* Else, see if there is a way to calculate it */ 1768c2ecf20Sopenharmony_ci if (clk->recalc) 1778c2ecf20Sopenharmony_ci clk->rate = clk->recalc(clk); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* Otherwise, default to parent rate */ 1808c2ecf20Sopenharmony_ci else if (clk->parent) 1818c2ecf20Sopenharmony_ci clk->rate = clk->parent->rate; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci return 0; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_register); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_civoid clk_unregister(struct clk *clk) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci if (clk == NULL || IS_ERR(clk)) 1908c2ecf20Sopenharmony_ci return; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 1938c2ecf20Sopenharmony_ci list_del(&clk->node); 1948c2ecf20Sopenharmony_ci list_del(&clk->childnode); 1958c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(clk_unregister); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic u32 pll_read(struct pll_data *pll, int reg) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci return soc_readl(pll->base + reg); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic unsigned long clk_sysclk_recalc(struct clk *clk) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci u32 v, plldiv = 0; 2088c2ecf20Sopenharmony_ci struct pll_data *pll; 2098c2ecf20Sopenharmony_ci unsigned long rate = clk->rate; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (WARN_ON(!clk->parent)) 2128c2ecf20Sopenharmony_ci return rate; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci rate = clk->parent->rate; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci /* the parent must be a PLL */ 2178c2ecf20Sopenharmony_ci if (WARN_ON(!clk->parent->pll_data)) 2188c2ecf20Sopenharmony_ci return rate; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci pll = clk->parent->pll_data; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci /* If pre-PLL, source clock is before the multiplier and divider(s) */ 2238c2ecf20Sopenharmony_ci if (clk->flags & PRE_PLL) 2248c2ecf20Sopenharmony_ci rate = pll->input_rate; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (!clk->div) { 2278c2ecf20Sopenharmony_ci pr_debug("%s: (no divider) rate = %lu KHz\n", 2288c2ecf20Sopenharmony_ci clk->name, rate / 1000); 2298c2ecf20Sopenharmony_ci return rate; 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (clk->flags & FIXED_DIV_PLL) { 2338c2ecf20Sopenharmony_ci rate /= clk->div; 2348c2ecf20Sopenharmony_ci pr_debug("%s: (fixed divide by %d) rate = %lu KHz\n", 2358c2ecf20Sopenharmony_ci clk->name, clk->div, rate / 1000); 2368c2ecf20Sopenharmony_ci return rate; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci v = pll_read(pll, clk->div); 2408c2ecf20Sopenharmony_ci if (v & PLLDIV_EN) 2418c2ecf20Sopenharmony_ci plldiv = (v & PLLDIV_RATIO_MASK) + 1; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci if (plldiv == 0) 2448c2ecf20Sopenharmony_ci plldiv = 1; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci rate /= plldiv; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci pr_debug("%s: (divide by %d) rate = %lu KHz\n", 2498c2ecf20Sopenharmony_ci clk->name, plldiv, rate / 1000); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return rate; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic unsigned long clk_leafclk_recalc(struct clk *clk) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci if (WARN_ON(!clk->parent)) 2578c2ecf20Sopenharmony_ci return clk->rate; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci pr_debug("%s: (parent %s) rate = %lu KHz\n", 2608c2ecf20Sopenharmony_ci clk->name, clk->parent->name, clk->parent->rate / 1000); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci return clk->parent->rate; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic unsigned long clk_pllclk_recalc(struct clk *clk) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci u32 ctrl, mult = 0, prediv = 0, postdiv = 0; 2688c2ecf20Sopenharmony_ci u8 bypass; 2698c2ecf20Sopenharmony_ci struct pll_data *pll = clk->pll_data; 2708c2ecf20Sopenharmony_ci unsigned long rate = clk->rate; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (clk->flags & FIXED_RATE_PLL) 2738c2ecf20Sopenharmony_ci return rate; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci ctrl = pll_read(pll, PLLCTL); 2768c2ecf20Sopenharmony_ci rate = pll->input_rate = clk->parent->rate; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (ctrl & PLLCTL_PLLEN) 2798c2ecf20Sopenharmony_ci bypass = 0; 2808c2ecf20Sopenharmony_ci else 2818c2ecf20Sopenharmony_ci bypass = 1; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (pll->flags & PLL_HAS_MUL) { 2848c2ecf20Sopenharmony_ci mult = pll_read(pll, PLLM); 2858c2ecf20Sopenharmony_ci mult = (mult & PLLM_PLLM_MASK) + 1; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci if (pll->flags & PLL_HAS_PRE) { 2888c2ecf20Sopenharmony_ci prediv = pll_read(pll, PLLPRE); 2898c2ecf20Sopenharmony_ci if (prediv & PLLDIV_EN) 2908c2ecf20Sopenharmony_ci prediv = (prediv & PLLDIV_RATIO_MASK) + 1; 2918c2ecf20Sopenharmony_ci else 2928c2ecf20Sopenharmony_ci prediv = 0; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci if (pll->flags & PLL_HAS_POST) { 2958c2ecf20Sopenharmony_ci postdiv = pll_read(pll, PLLPOST); 2968c2ecf20Sopenharmony_ci if (postdiv & PLLDIV_EN) 2978c2ecf20Sopenharmony_ci postdiv = (postdiv & PLLDIV_RATIO_MASK) + 1; 2988c2ecf20Sopenharmony_ci else 2998c2ecf20Sopenharmony_ci postdiv = 1; 3008c2ecf20Sopenharmony_ci } 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (!bypass) { 3038c2ecf20Sopenharmony_ci if (prediv) 3048c2ecf20Sopenharmony_ci rate /= prediv; 3058c2ecf20Sopenharmony_ci if (mult) 3068c2ecf20Sopenharmony_ci rate *= mult; 3078c2ecf20Sopenharmony_ci if (postdiv) 3088c2ecf20Sopenharmony_ci rate /= postdiv; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci pr_debug("PLL%d: input = %luMHz, pre[%d] mul[%d] post[%d] " 3118c2ecf20Sopenharmony_ci "--> %luMHz output.\n", 3128c2ecf20Sopenharmony_ci pll->num, clk->parent->rate / 1000000, 3138c2ecf20Sopenharmony_ci prediv, mult, postdiv, rate / 1000000); 3148c2ecf20Sopenharmony_ci } else 3158c2ecf20Sopenharmony_ci pr_debug("PLL%d: input = %luMHz, bypass mode.\n", 3168c2ecf20Sopenharmony_ci pll->num, clk->parent->rate / 1000000); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci return rate; 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void __init __init_clk(struct clk *clk) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clk->node); 3258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clk->children); 3268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&clk->childnode); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci if (!clk->recalc) { 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci /* Check if clock is a PLL */ 3318c2ecf20Sopenharmony_ci if (clk->pll_data) 3328c2ecf20Sopenharmony_ci clk->recalc = clk_pllclk_recalc; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci /* Else, if it is a PLL-derived clock */ 3358c2ecf20Sopenharmony_ci else if (clk->flags & CLK_PLL) 3368c2ecf20Sopenharmony_ci clk->recalc = clk_sysclk_recalc; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Otherwise, it is a leaf clock (PSC clock) */ 3398c2ecf20Sopenharmony_ci else if (clk->parent) 3408c2ecf20Sopenharmony_ci clk->recalc = clk_leafclk_recalc; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_civoid __init c6x_clks_init(struct clk_lookup *clocks) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct clk_lookup *c; 3478c2ecf20Sopenharmony_ci struct clk *clk; 3488c2ecf20Sopenharmony_ci size_t num_clocks = 0; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci for (c = clocks; c->clk; c++) { 3518c2ecf20Sopenharmony_ci clk = c->clk; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci __init_clk(clk); 3548c2ecf20Sopenharmony_ci clk_register(clk); 3558c2ecf20Sopenharmony_ci num_clocks++; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Turn on clocks that Linux doesn't otherwise manage */ 3588c2ecf20Sopenharmony_ci if (clk->flags & ALWAYS_ENABLED) 3598c2ecf20Sopenharmony_ci clk_enable(clk); 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci clkdev_add_table(clocks, num_clocks); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 3688c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci#define CLKNAME_MAX 10 /* longest clock name */ 3718c2ecf20Sopenharmony_ci#define NEST_DELTA 2 3728c2ecf20Sopenharmony_ci#define NEST_MAX 4 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic void 3758c2ecf20Sopenharmony_cidump_clock(struct seq_file *s, unsigned nest, struct clk *parent) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci char *state; 3788c2ecf20Sopenharmony_ci char buf[CLKNAME_MAX + NEST_DELTA * NEST_MAX]; 3798c2ecf20Sopenharmony_ci struct clk *clk; 3808c2ecf20Sopenharmony_ci unsigned i; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (parent->flags & CLK_PLL) 3838c2ecf20Sopenharmony_ci state = "pll"; 3848c2ecf20Sopenharmony_ci else 3858c2ecf20Sopenharmony_ci state = ""; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* <nest spaces> name <pad to end> */ 3888c2ecf20Sopenharmony_ci memset(buf, ' ', sizeof(buf) - 1); 3898c2ecf20Sopenharmony_ci buf[sizeof(buf) - 1] = 0; 3908c2ecf20Sopenharmony_ci i = strlen(parent->name); 3918c2ecf20Sopenharmony_ci memcpy(buf + nest, parent->name, 3928c2ecf20Sopenharmony_ci min(i, (unsigned)(sizeof(buf) - 1 - nest))); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci seq_printf(s, "%s users=%2d %-3s %9ld Hz\n", 3958c2ecf20Sopenharmony_ci buf, parent->usecount, state, clk_get_rate(parent)); 3968c2ecf20Sopenharmony_ci /* REVISIT show device associations too */ 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* cost is now small, but not linear... */ 3998c2ecf20Sopenharmony_ci list_for_each_entry(clk, &parent->children, childnode) { 4008c2ecf20Sopenharmony_ci dump_clock(s, nest + NEST_DELTA, clk); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int c6x_ck_show(struct seq_file *m, void *v) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct clk *clk; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * Show clock tree; We trust nonzero usecounts equate to PSC enables... 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci mutex_lock(&clocks_mutex); 4128c2ecf20Sopenharmony_ci list_for_each_entry(clk, &clocks, node) 4138c2ecf20Sopenharmony_ci if (!clk->parent) 4148c2ecf20Sopenharmony_ci dump_clock(m, 0, clk); 4158c2ecf20Sopenharmony_ci mutex_unlock(&clocks_mutex); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return 0; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int c6x_ck_open(struct inode *inode, struct file *file) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci return single_open(file, c6x_ck_show, NULL); 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistatic const struct file_operations c6x_ck_operations = { 4268c2ecf20Sopenharmony_ci .open = c6x_ck_open, 4278c2ecf20Sopenharmony_ci .read = seq_read, 4288c2ecf20Sopenharmony_ci .llseek = seq_lseek, 4298c2ecf20Sopenharmony_ci .release = single_release, 4308c2ecf20Sopenharmony_ci}; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic int __init c6x_clk_debugfs_init(void) 4338c2ecf20Sopenharmony_ci{ 4348c2ecf20Sopenharmony_ci debugfs_create_file("c6x_clocks", S_IFREG | S_IRUGO, NULL, NULL, 4358c2ecf20Sopenharmony_ci &c6x_ck_operations); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_cidevice_initcall(c6x_clk_debugfs_init); 4408c2ecf20Sopenharmony_ci#endif /* CONFIG_DEBUG_FS */ 441