18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * OMAP clkctrl clock support 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Texas Instruments, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Tero Kristo <t-kristo@ti.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 98c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 108c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * This program is distributed "as is" WITHOUT ANY WARRANTY of any 138c2ecf20Sopenharmony_ci * kind, whether express or implied; without even the implied warranty 148c2ecf20Sopenharmony_ci * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 158c2ecf20Sopenharmony_ci * GNU General Public License for more details. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_address.h> 228c2ecf20Sopenharmony_ci#include <linux/clk/ti.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/timekeeping.h> 258c2ecf20Sopenharmony_ci#include "clock.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define NO_IDLEST 0 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define OMAP4_MODULEMODE_MASK 0x3 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define MODULEMODE_HWCTRL 0x1 328c2ecf20Sopenharmony_ci#define MODULEMODE_SWCTRL 0x2 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci#define OMAP4_IDLEST_MASK (0x3 << 16) 358c2ecf20Sopenharmony_ci#define OMAP4_IDLEST_SHIFT 16 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define OMAP4_STBYST_MASK BIT(18) 388c2ecf20Sopenharmony_ci#define OMAP4_STBYST_SHIFT 18 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define CLKCTRL_IDLEST_FUNCTIONAL 0x0 418c2ecf20Sopenharmony_ci#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 428c2ecf20Sopenharmony_ci#define CLKCTRL_IDLEST_DISABLED 0x3 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* These timeouts are in us */ 458c2ecf20Sopenharmony_ci#define OMAP4_MAX_MODULE_READY_TIME 2000 468c2ecf20Sopenharmony_ci#define OMAP4_MAX_MODULE_DISABLE_TIME 5000 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic bool _early_timeout = true; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct omap_clkctrl_provider { 518c2ecf20Sopenharmony_ci void __iomem *base; 528c2ecf20Sopenharmony_ci struct list_head clocks; 538c2ecf20Sopenharmony_ci char *clkdm_name; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistruct omap_clkctrl_clk { 578c2ecf20Sopenharmony_ci struct clk_hw *clk; 588c2ecf20Sopenharmony_ci u16 reg_offset; 598c2ecf20Sopenharmony_ci int bit_offset; 608c2ecf20Sopenharmony_ci struct list_head node; 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciunion omap4_timeout { 648c2ecf20Sopenharmony_ci u32 cycles; 658c2ecf20Sopenharmony_ci ktime_t start; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic const struct omap_clkctrl_data default_clkctrl_data[] __initconst = { 698c2ecf20Sopenharmony_ci { 0 }, 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic u32 _omap4_idlest(u32 val) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci val &= OMAP4_IDLEST_MASK; 758c2ecf20Sopenharmony_ci val >>= OMAP4_IDLEST_SHIFT; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return val; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic bool _omap4_is_idle(u32 val) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci val = _omap4_idlest(val); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return val == CLKCTRL_IDLEST_DISABLED; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic bool _omap4_is_ready(u32 val) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci val = _omap4_idlest(val); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return val == CLKCTRL_IDLEST_FUNCTIONAL || 928c2ecf20Sopenharmony_ci val == CLKCTRL_IDLEST_INTERFACE_IDLE; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * There are two special cases where ktime_to_ns() can't be 998c2ecf20Sopenharmony_ci * used to track the timeouts. First one is during early boot 1008c2ecf20Sopenharmony_ci * when the timers haven't been initialized yet. The second 1018c2ecf20Sopenharmony_ci * one is during suspend-resume cycle while timekeeping is 1028c2ecf20Sopenharmony_ci * being suspended / resumed. Clocksource for the system 1038c2ecf20Sopenharmony_ci * can be from a timer that requires pm_runtime access, which 1048c2ecf20Sopenharmony_ci * will eventually bring us here with timekeeping_suspended, 1058c2ecf20Sopenharmony_ci * during both suspend entry and resume paths. This happens 1068c2ecf20Sopenharmony_ci * at least on am43xx platform. Account for flakeyness 1078c2ecf20Sopenharmony_ci * with udelay() by multiplying the timeout value by 2. 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_ci if (unlikely(_early_timeout || timekeeping_suspended)) { 1108c2ecf20Sopenharmony_ci if (time->cycles++ < timeout) { 1118c2ecf20Sopenharmony_ci udelay(1 * 2); 1128c2ecf20Sopenharmony_ci return false; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } else { 1158c2ecf20Sopenharmony_ci if (!ktime_to_ns(time->start)) { 1168c2ecf20Sopenharmony_ci time->start = ktime_get(); 1178c2ecf20Sopenharmony_ci return false; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (ktime_us_delta(ktime_get(), time->start) < timeout) { 1218c2ecf20Sopenharmony_ci cpu_relax(); 1228c2ecf20Sopenharmony_ci return false; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return true; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int __init _omap4_disable_early_timeout(void) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci _early_timeout = false; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return 0; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ciarch_initcall(_omap4_disable_early_timeout); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic int _omap4_clkctrl_clk_enable(struct clk_hw *hw) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 1408c2ecf20Sopenharmony_ci u32 val; 1418c2ecf20Sopenharmony_ci int ret; 1428c2ecf20Sopenharmony_ci union omap4_timeout timeout = { 0 }; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (clk->clkdm) { 1458c2ecf20Sopenharmony_ci ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); 1468c2ecf20Sopenharmony_ci if (ret) { 1478c2ecf20Sopenharmony_ci WARN(1, 1488c2ecf20Sopenharmony_ci "%s: could not enable %s's clockdomain %s: %d\n", 1498c2ecf20Sopenharmony_ci __func__, clk_hw_get_name(hw), 1508c2ecf20Sopenharmony_ci clk->clkdm_name, ret); 1518c2ecf20Sopenharmony_ci return ret; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (!clk->enable_bit) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci val &= ~OMAP4_MODULEMODE_MASK; 1618c2ecf20Sopenharmony_ci val |= clk->enable_bit; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (test_bit(NO_IDLEST, &clk->flags)) 1668c2ecf20Sopenharmony_ci return 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Wait until module is enabled */ 1698c2ecf20Sopenharmony_ci while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { 1708c2ecf20Sopenharmony_ci if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) { 1718c2ecf20Sopenharmony_ci pr_err("%s: failed to enable\n", clk_hw_get_name(hw)); 1728c2ecf20Sopenharmony_ci return -EBUSY; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic void _omap4_clkctrl_clk_disable(struct clk_hw *hw) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 1828c2ecf20Sopenharmony_ci u32 val; 1838c2ecf20Sopenharmony_ci union omap4_timeout timeout = { 0 }; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (!clk->enable_bit) 1868c2ecf20Sopenharmony_ci goto exit; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci val &= ~OMAP4_MODULEMODE_MASK; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (test_bit(NO_IDLEST, &clk->flags)) 1958c2ecf20Sopenharmony_ci goto exit; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* Wait until module is disabled */ 1988c2ecf20Sopenharmony_ci while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { 1998c2ecf20Sopenharmony_ci if (_omap4_is_timeout(&timeout, 2008c2ecf20Sopenharmony_ci OMAP4_MAX_MODULE_DISABLE_TIME)) { 2018c2ecf20Sopenharmony_ci pr_err("%s: failed to disable\n", clk_hw_get_name(hw)); 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ciexit: 2078c2ecf20Sopenharmony_ci if (clk->clkdm) 2088c2ecf20Sopenharmony_ci ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int _omap4_clkctrl_clk_is_enabled(struct clk_hw *hw) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 2148c2ecf20Sopenharmony_ci u32 val; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (val & clk->enable_bit) 2198c2ecf20Sopenharmony_ci return 1; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic const struct clk_ops omap4_clkctrl_clk_ops = { 2258c2ecf20Sopenharmony_ci .enable = _omap4_clkctrl_clk_enable, 2268c2ecf20Sopenharmony_ci .disable = _omap4_clkctrl_clk_disable, 2278c2ecf20Sopenharmony_ci .is_enabled = _omap4_clkctrl_clk_is_enabled, 2288c2ecf20Sopenharmony_ci .init = omap2_init_clk_clkdm, 2298c2ecf20Sopenharmony_ci}; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic struct clk_hw *_ti_omap4_clkctrl_xlate(struct of_phandle_args *clkspec, 2328c2ecf20Sopenharmony_ci void *data) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct omap_clkctrl_provider *provider = data; 2358c2ecf20Sopenharmony_ci struct omap_clkctrl_clk *entry; 2368c2ecf20Sopenharmony_ci bool found = false; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (clkspec->args_count != 2) 2398c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci pr_debug("%s: looking for %x:%x\n", __func__, 2428c2ecf20Sopenharmony_ci clkspec->args[0], clkspec->args[1]); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci list_for_each_entry(entry, &provider->clocks, node) { 2458c2ecf20Sopenharmony_ci if (entry->reg_offset == clkspec->args[0] && 2468c2ecf20Sopenharmony_ci entry->bit_offset == clkspec->args[1]) { 2478c2ecf20Sopenharmony_ci found = true; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci if (!found) 2538c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci return entry->clk; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* Get clkctrl clock base name based on clkctrl_name or dts node */ 2598c2ecf20Sopenharmony_cistatic const char * __init clkctrl_get_clock_name(struct device_node *np, 2608c2ecf20Sopenharmony_ci const char *clkctrl_name, 2618c2ecf20Sopenharmony_ci int offset, int index, 2628c2ecf20Sopenharmony_ci bool legacy_naming) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci char *clock_name; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* l4per-clkctrl:1234:0 style naming based on clkctrl_name */ 2678c2ecf20Sopenharmony_ci if (clkctrl_name && !legacy_naming) { 2688c2ecf20Sopenharmony_ci clock_name = kasprintf(GFP_KERNEL, "%s-clkctrl:%04x:%d", 2698c2ecf20Sopenharmony_ci clkctrl_name, offset, index); 2708c2ecf20Sopenharmony_ci if (!clock_name) 2718c2ecf20Sopenharmony_ci return NULL; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci strreplace(clock_name, '_', '-'); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return clock_name; 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci /* l4per:1234:0 old style naming based on clkctrl_name */ 2798c2ecf20Sopenharmony_ci if (clkctrl_name) 2808c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%s_cm:clk:%04x:%d", 2818c2ecf20Sopenharmony_ci clkctrl_name, offset, index); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci /* l4per_cm:1234:0 old style naming based on parent node name */ 2848c2ecf20Sopenharmony_ci if (legacy_naming) 2858c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%pOFn:clk:%04x:%d", 2868c2ecf20Sopenharmony_ci np->parent, offset, index); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* l4per-clkctrl:1234:0 style naming based on node name */ 2898c2ecf20Sopenharmony_ci return kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", np, offset, index); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int __init 2938c2ecf20Sopenharmony_ci_ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, 2948c2ecf20Sopenharmony_ci struct device_node *node, struct clk_hw *clk_hw, 2958c2ecf20Sopenharmony_ci u16 offset, u8 bit, const char * const *parents, 2968c2ecf20Sopenharmony_ci int num_parents, const struct clk_ops *ops, 2978c2ecf20Sopenharmony_ci const char *clkctrl_name) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct clk_init_data init = { NULL }; 3008c2ecf20Sopenharmony_ci struct clk *clk; 3018c2ecf20Sopenharmony_ci struct omap_clkctrl_clk *clkctrl_clk; 3028c2ecf20Sopenharmony_ci int ret = 0; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci init.name = clkctrl_get_clock_name(node, clkctrl_name, offset, bit, 3058c2ecf20Sopenharmony_ci ti_clk_get_features()->flags & 3068c2ecf20Sopenharmony_ci TI_CLK_CLKCTRL_COMPAT); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); 3098c2ecf20Sopenharmony_ci if (!init.name || !clkctrl_clk) { 3108c2ecf20Sopenharmony_ci ret = -ENOMEM; 3118c2ecf20Sopenharmony_ci goto cleanup; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci clk_hw->init = &init; 3158c2ecf20Sopenharmony_ci init.parent_names = parents; 3168c2ecf20Sopenharmony_ci init.num_parents = num_parents; 3178c2ecf20Sopenharmony_ci init.ops = ops; 3188c2ecf20Sopenharmony_ci init.flags = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci clk = of_ti_clk_register(node, clk_hw, init.name); 3218c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) { 3228c2ecf20Sopenharmony_ci ret = -EINVAL; 3238c2ecf20Sopenharmony_ci goto cleanup; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci clkctrl_clk->reg_offset = offset; 3278c2ecf20Sopenharmony_ci clkctrl_clk->bit_offset = bit; 3288c2ecf20Sopenharmony_ci clkctrl_clk->clk = clk_hw; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci list_add(&clkctrl_clk->node, &provider->clocks); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cicleanup: 3358c2ecf20Sopenharmony_ci kfree(init.name); 3368c2ecf20Sopenharmony_ci kfree(clkctrl_clk); 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic void __init 3418c2ecf20Sopenharmony_ci_ti_clkctrl_setup_gate(struct omap_clkctrl_provider *provider, 3428c2ecf20Sopenharmony_ci struct device_node *node, u16 offset, 3438c2ecf20Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 3448c2ecf20Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct clk_hw_omap *clk_hw; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); 3498c2ecf20Sopenharmony_ci if (!clk_hw) 3508c2ecf20Sopenharmony_ci return; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci clk_hw->enable_bit = data->bit; 3538c2ecf20Sopenharmony_ci clk_hw->enable_reg.ptr = reg; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &clk_hw->hw, offset, 3568c2ecf20Sopenharmony_ci data->bit, data->parents, 1, 3578c2ecf20Sopenharmony_ci &omap_gate_clk_ops, clkctrl_name)) 3588c2ecf20Sopenharmony_ci kfree(clk_hw); 3598c2ecf20Sopenharmony_ci} 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_cistatic void __init 3628c2ecf20Sopenharmony_ci_ti_clkctrl_setup_mux(struct omap_clkctrl_provider *provider, 3638c2ecf20Sopenharmony_ci struct device_node *node, u16 offset, 3648c2ecf20Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 3658c2ecf20Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct clk_omap_mux *mux; 3688c2ecf20Sopenharmony_ci int num_parents = 0; 3698c2ecf20Sopenharmony_ci const char * const *pname; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 3728c2ecf20Sopenharmony_ci if (!mux) 3738c2ecf20Sopenharmony_ci return; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci pname = data->parents; 3768c2ecf20Sopenharmony_ci while (*pname) { 3778c2ecf20Sopenharmony_ci num_parents++; 3788c2ecf20Sopenharmony_ci pname++; 3798c2ecf20Sopenharmony_ci } 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci mux->mask = num_parents; 3828c2ecf20Sopenharmony_ci if (!(mux->flags & CLK_MUX_INDEX_ONE)) 3838c2ecf20Sopenharmony_ci mux->mask--; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci mux->mask = (1 << fls(mux->mask)) - 1; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci mux->shift = data->bit; 3888c2ecf20Sopenharmony_ci mux->reg.ptr = reg; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &mux->hw, offset, 3918c2ecf20Sopenharmony_ci data->bit, data->parents, num_parents, 3928c2ecf20Sopenharmony_ci &ti_clk_mux_ops, clkctrl_name)) 3938c2ecf20Sopenharmony_ci kfree(mux); 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void __init 3978c2ecf20Sopenharmony_ci_ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, 3988c2ecf20Sopenharmony_ci struct device_node *node, u16 offset, 3998c2ecf20Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 4008c2ecf20Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct clk_omap_divider *div; 4038c2ecf20Sopenharmony_ci const struct omap_clkctrl_div_data *div_data = data->data; 4048c2ecf20Sopenharmony_ci u8 div_flags = 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 4078c2ecf20Sopenharmony_ci if (!div) 4088c2ecf20Sopenharmony_ci return; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci div->reg.ptr = reg; 4118c2ecf20Sopenharmony_ci div->shift = data->bit; 4128c2ecf20Sopenharmony_ci div->flags = div_data->flags; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (div->flags & CLK_DIVIDER_POWER_OF_TWO) 4158c2ecf20Sopenharmony_ci div_flags |= CLKF_INDEX_POWER_OF_TWO; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, 4188c2ecf20Sopenharmony_ci div_data->max_div, div_flags, 4198c2ecf20Sopenharmony_ci div)) { 4208c2ecf20Sopenharmony_ci pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, 4218c2ecf20Sopenharmony_ci node, offset, data->bit); 4228c2ecf20Sopenharmony_ci kfree(div); 4238c2ecf20Sopenharmony_ci return; 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &div->hw, offset, 4278c2ecf20Sopenharmony_ci data->bit, data->parents, 1, 4288c2ecf20Sopenharmony_ci &ti_clk_divider_ops, clkctrl_name)) 4298c2ecf20Sopenharmony_ci kfree(div); 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void __init 4338c2ecf20Sopenharmony_ci_ti_clkctrl_setup_subclks(struct omap_clkctrl_provider *provider, 4348c2ecf20Sopenharmony_ci struct device_node *node, 4358c2ecf20Sopenharmony_ci const struct omap_clkctrl_reg_data *data, 4368c2ecf20Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci const struct omap_clkctrl_bit_data *bits = data->bit_data; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!bits) 4418c2ecf20Sopenharmony_ci return; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci while (bits->bit) { 4448c2ecf20Sopenharmony_ci switch (bits->type) { 4458c2ecf20Sopenharmony_ci case TI_CLK_GATE: 4468c2ecf20Sopenharmony_ci _ti_clkctrl_setup_gate(provider, node, data->offset, 4478c2ecf20Sopenharmony_ci bits, reg, clkctrl_name); 4488c2ecf20Sopenharmony_ci break; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci case TI_CLK_DIVIDER: 4518c2ecf20Sopenharmony_ci _ti_clkctrl_setup_div(provider, node, data->offset, 4528c2ecf20Sopenharmony_ci bits, reg, clkctrl_name); 4538c2ecf20Sopenharmony_ci break; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci case TI_CLK_MUX: 4568c2ecf20Sopenharmony_ci _ti_clkctrl_setup_mux(provider, node, data->offset, 4578c2ecf20Sopenharmony_ci bits, reg, clkctrl_name); 4588c2ecf20Sopenharmony_ci break; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci default: 4618c2ecf20Sopenharmony_ci pr_err("%s: bad subclk type: %d\n", __func__, 4628c2ecf20Sopenharmony_ci bits->type); 4638c2ecf20Sopenharmony_ci return; 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci bits++; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void __init _clkctrl_add_provider(void *data, 4708c2ecf20Sopenharmony_ci struct device_node *np) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci of_clk_add_hw_provider(np, _ti_omap4_clkctrl_xlate, data); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci/* Get clock name based on compatible string for clkctrl */ 4768c2ecf20Sopenharmony_cistatic char * __init clkctrl_get_name(struct device_node *np) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct property *prop; 4798c2ecf20Sopenharmony_ci const int prefix_len = 11; 4808c2ecf20Sopenharmony_ci const char *compat; 4818c2ecf20Sopenharmony_ci char *name; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci of_property_for_each_string(np, "compatible", prop, compat) { 4848c2ecf20Sopenharmony_ci if (!strncmp("ti,clkctrl-", compat, prefix_len)) { 4858c2ecf20Sopenharmony_ci /* Two letter minimum name length for l3, l4 etc */ 4868c2ecf20Sopenharmony_ci if (strnlen(compat + prefix_len, 16) < 2) 4878c2ecf20Sopenharmony_ci continue; 4888c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%s", compat + prefix_len); 4898c2ecf20Sopenharmony_ci if (!name) 4908c2ecf20Sopenharmony_ci continue; 4918c2ecf20Sopenharmony_ci strreplace(name, '-', '_'); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return name; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return NULL; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic void __init _ti_omap4_clkctrl_setup(struct device_node *node) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct omap_clkctrl_provider *provider; 5038c2ecf20Sopenharmony_ci const struct omap_clkctrl_data *data = default_clkctrl_data; 5048c2ecf20Sopenharmony_ci const struct omap_clkctrl_reg_data *reg_data; 5058c2ecf20Sopenharmony_ci struct clk_init_data init = { NULL }; 5068c2ecf20Sopenharmony_ci struct clk_hw_omap *hw; 5078c2ecf20Sopenharmony_ci struct clk *clk; 5088c2ecf20Sopenharmony_ci struct omap_clkctrl_clk *clkctrl_clk = NULL; 5098c2ecf20Sopenharmony_ci const __be32 *addrp; 5108c2ecf20Sopenharmony_ci bool legacy_naming; 5118c2ecf20Sopenharmony_ci char *clkctrl_name; 5128c2ecf20Sopenharmony_ci u32 addr; 5138c2ecf20Sopenharmony_ci int ret; 5148c2ecf20Sopenharmony_ci char *c; 5158c2ecf20Sopenharmony_ci u16 soc_mask = 0; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci if (!(ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) && 5188c2ecf20Sopenharmony_ci of_node_name_eq(node, "clk")) 5198c2ecf20Sopenharmony_ci ti_clk_features.flags |= TI_CLK_CLKCTRL_COMPAT; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci addrp = of_get_address(node, 0, NULL, NULL); 5228c2ecf20Sopenharmony_ci addr = (u32)of_translate_address(node, addrp); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP4 5258c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,omap4")) 5268c2ecf20Sopenharmony_ci data = omap4_clkctrl_data; 5278c2ecf20Sopenharmony_ci#endif 5288c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_OMAP5 5298c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,omap5")) 5308c2ecf20Sopenharmony_ci data = omap5_clkctrl_data; 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_DRA7XX 5338c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dra7")) { 5348c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) 5358c2ecf20Sopenharmony_ci data = dra7_clkctrl_compat_data; 5368c2ecf20Sopenharmony_ci else 5378c2ecf20Sopenharmony_ci data = dra7_clkctrl_data; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dra72")) 5418c2ecf20Sopenharmony_ci soc_mask = CLKF_SOC_DRA72; 5428c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dra74")) 5438c2ecf20Sopenharmony_ci soc_mask = CLKF_SOC_DRA74; 5448c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dra76")) 5458c2ecf20Sopenharmony_ci soc_mask = CLKF_SOC_DRA76; 5468c2ecf20Sopenharmony_ci#endif 5478c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_AM33XX 5488c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,am33xx")) { 5498c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) 5508c2ecf20Sopenharmony_ci data = am3_clkctrl_compat_data; 5518c2ecf20Sopenharmony_ci else 5528c2ecf20Sopenharmony_ci data = am3_clkctrl_data; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci#endif 5558c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_AM43XX 5568c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,am4372")) { 5578c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) 5588c2ecf20Sopenharmony_ci data = am4_clkctrl_compat_data; 5598c2ecf20Sopenharmony_ci else 5608c2ecf20Sopenharmony_ci data = am4_clkctrl_data; 5618c2ecf20Sopenharmony_ci } 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,am438x")) { 5648c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) 5658c2ecf20Sopenharmony_ci data = am438x_clkctrl_compat_data; 5668c2ecf20Sopenharmony_ci else 5678c2ecf20Sopenharmony_ci data = am438x_clkctrl_data; 5688c2ecf20Sopenharmony_ci } 5698c2ecf20Sopenharmony_ci#endif 5708c2ecf20Sopenharmony_ci#ifdef CONFIG_SOC_TI81XX 5718c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dm814")) 5728c2ecf20Sopenharmony_ci data = dm814_clkctrl_data; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (of_machine_is_compatible("ti,dm816")) 5758c2ecf20Sopenharmony_ci data = dm816_clkctrl_data; 5768c2ecf20Sopenharmony_ci#endif 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DEVICE_TYPE_GP) 5798c2ecf20Sopenharmony_ci soc_mask |= CLKF_SOC_NONSEC; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci while (data->addr) { 5828c2ecf20Sopenharmony_ci if (addr == data->addr) 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci data++; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci if (!data->addr) { 5898c2ecf20Sopenharmony_ci pr_err("%pOF not found from clkctrl data.\n", node); 5908c2ecf20Sopenharmony_ci return; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci provider = kzalloc(sizeof(*provider), GFP_KERNEL); 5948c2ecf20Sopenharmony_ci if (!provider) 5958c2ecf20Sopenharmony_ci return; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci provider->base = of_iomap(node, 0); 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci legacy_naming = ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT; 6008c2ecf20Sopenharmony_ci clkctrl_name = clkctrl_get_name(node); 6018c2ecf20Sopenharmony_ci if (clkctrl_name) { 6028c2ecf20Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, 6038c2ecf20Sopenharmony_ci "%s_clkdm", clkctrl_name); 6048c2ecf20Sopenharmony_ci if (!provider->clkdm_name) { 6058c2ecf20Sopenharmony_ci kfree(provider); 6068c2ecf20Sopenharmony_ci return; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci goto clkdm_found; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* 6128c2ecf20Sopenharmony_ci * The code below can be removed when all clkctrl nodes use domain 6138c2ecf20Sopenharmony_ci * specific compatible proprerty and standard clock node naming 6148c2ecf20Sopenharmony_ci */ 6158c2ecf20Sopenharmony_ci if (legacy_naming) { 6168c2ecf20Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFnxxx", node->parent); 6178c2ecf20Sopenharmony_ci if (!provider->clkdm_name) { 6188c2ecf20Sopenharmony_ci kfree(provider); 6198c2ecf20Sopenharmony_ci return; 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* 6238c2ecf20Sopenharmony_ci * Create default clkdm name, replace _cm from end of parent 6248c2ecf20Sopenharmony_ci * node name with _clkdm 6258c2ecf20Sopenharmony_ci */ 6268c2ecf20Sopenharmony_ci provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; 6278c2ecf20Sopenharmony_ci } else { 6288c2ecf20Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); 6298c2ecf20Sopenharmony_ci if (!provider->clkdm_name) { 6308c2ecf20Sopenharmony_ci kfree(provider); 6318c2ecf20Sopenharmony_ci return; 6328c2ecf20Sopenharmony_ci } 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci /* 6358c2ecf20Sopenharmony_ci * Create default clkdm name, replace _clkctrl from end of 6368c2ecf20Sopenharmony_ci * node name with _clkdm 6378c2ecf20Sopenharmony_ci */ 6388c2ecf20Sopenharmony_ci provider->clkdm_name[strlen(provider->clkdm_name) - 7] = 0; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci strcat(provider->clkdm_name, "clkdm"); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci /* Replace any dash from the clkdm name with underscore */ 6448c2ecf20Sopenharmony_ci c = provider->clkdm_name; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci while (*c) { 6478c2ecf20Sopenharmony_ci if (*c == '-') 6488c2ecf20Sopenharmony_ci *c = '_'; 6498c2ecf20Sopenharmony_ci c++; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ciclkdm_found: 6528c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&provider->clocks); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci /* Generate clocks */ 6558c2ecf20Sopenharmony_ci reg_data = data->regs; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci while (reg_data->parent) { 6588c2ecf20Sopenharmony_ci if ((reg_data->flags & CLKF_SOC_MASK) && 6598c2ecf20Sopenharmony_ci (reg_data->flags & soc_mask) == 0) { 6608c2ecf20Sopenharmony_ci reg_data++; 6618c2ecf20Sopenharmony_ci continue; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci hw = kzalloc(sizeof(*hw), GFP_KERNEL); 6658c2ecf20Sopenharmony_ci if (!hw) 6668c2ecf20Sopenharmony_ci return; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci hw->enable_reg.ptr = provider->base + reg_data->offset; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci _ti_clkctrl_setup_subclks(provider, node, reg_data, 6718c2ecf20Sopenharmony_ci hw->enable_reg.ptr, clkctrl_name); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (reg_data->flags & CLKF_SW_SUP) 6748c2ecf20Sopenharmony_ci hw->enable_bit = MODULEMODE_SWCTRL; 6758c2ecf20Sopenharmony_ci if (reg_data->flags & CLKF_HW_SUP) 6768c2ecf20Sopenharmony_ci hw->enable_bit = MODULEMODE_HWCTRL; 6778c2ecf20Sopenharmony_ci if (reg_data->flags & CLKF_NO_IDLEST) 6788c2ecf20Sopenharmony_ci set_bit(NO_IDLEST, &hw->flags); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (reg_data->clkdm_name) 6818c2ecf20Sopenharmony_ci hw->clkdm_name = reg_data->clkdm_name; 6828c2ecf20Sopenharmony_ci else 6838c2ecf20Sopenharmony_ci hw->clkdm_name = provider->clkdm_name; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci init.parent_names = ®_data->parent; 6868c2ecf20Sopenharmony_ci init.num_parents = 1; 6878c2ecf20Sopenharmony_ci init.flags = 0; 6888c2ecf20Sopenharmony_ci if (reg_data->flags & CLKF_SET_RATE_PARENT) 6898c2ecf20Sopenharmony_ci init.flags |= CLK_SET_RATE_PARENT; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci init.name = clkctrl_get_clock_name(node, clkctrl_name, 6928c2ecf20Sopenharmony_ci reg_data->offset, 0, 6938c2ecf20Sopenharmony_ci legacy_naming); 6948c2ecf20Sopenharmony_ci if (!init.name) 6958c2ecf20Sopenharmony_ci goto cleanup; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); 6988c2ecf20Sopenharmony_ci if (!clkctrl_clk) 6998c2ecf20Sopenharmony_ci goto cleanup; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci init.ops = &omap4_clkctrl_clk_ops; 7028c2ecf20Sopenharmony_ci hw->hw.init = &init; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci clk = of_ti_clk_register_omap_hw(node, &hw->hw, init.name); 7058c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) 7068c2ecf20Sopenharmony_ci goto cleanup; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci clkctrl_clk->reg_offset = reg_data->offset; 7098c2ecf20Sopenharmony_ci clkctrl_clk->clk = &hw->hw; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci list_add(&clkctrl_clk->node, &provider->clocks); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci reg_data++; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ret = of_clk_add_hw_provider(node, _ti_omap4_clkctrl_xlate, provider); 7178c2ecf20Sopenharmony_ci if (ret == -EPROBE_DEFER) 7188c2ecf20Sopenharmony_ci ti_clk_retry_init(node, provider, _clkctrl_add_provider); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci kfree(clkctrl_name); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cicleanup: 7258c2ecf20Sopenharmony_ci kfree(hw); 7268c2ecf20Sopenharmony_ci kfree(init.name); 7278c2ecf20Sopenharmony_ci kfree(clkctrl_name); 7288c2ecf20Sopenharmony_ci kfree(clkctrl_clk); 7298c2ecf20Sopenharmony_ci} 7308c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", 7318c2ecf20Sopenharmony_ci _ti_omap4_clkctrl_setup); 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci/** 7348c2ecf20Sopenharmony_ci * ti_clk_is_in_standby - Check if clkctrl clock is in standby or not 7358c2ecf20Sopenharmony_ci * @clk: clock to check standby status for 7368c2ecf20Sopenharmony_ci * 7378c2ecf20Sopenharmony_ci * Finds whether the provided clock is in standby mode or not. Returns 7388c2ecf20Sopenharmony_ci * true if the provided clock is a clkctrl type clock and it is in standby, 7398c2ecf20Sopenharmony_ci * false otherwise. 7408c2ecf20Sopenharmony_ci */ 7418c2ecf20Sopenharmony_cibool ti_clk_is_in_standby(struct clk *clk) 7428c2ecf20Sopenharmony_ci{ 7438c2ecf20Sopenharmony_ci struct clk_hw *hw; 7448c2ecf20Sopenharmony_ci struct clk_hw_omap *hwclk; 7458c2ecf20Sopenharmony_ci u32 val; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci hw = __clk_get_hw(clk); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci if (!omap2_clk_is_hw_omap(hw)) 7508c2ecf20Sopenharmony_ci return false; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci hwclk = to_clk_hw_omap(hw); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&hwclk->enable_reg); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (val & OMAP4_STBYST_MASK) 7578c2ecf20Sopenharmony_ci return true; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci return false; 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(ti_clk_is_in_standby); 762