162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP clkctrl clock support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2017 Texas Instruments, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Tero Kristo <t-kristo@ti.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk-provider.h> 1162306a36Sopenharmony_ci#include <linux/slab.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/clk/ti.h> 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/string_helpers.h> 1762306a36Sopenharmony_ci#include <linux/timekeeping.h> 1862306a36Sopenharmony_ci#include "clock.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define NO_IDLEST 0 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define OMAP4_MODULEMODE_MASK 0x3 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MODULEMODE_HWCTRL 0x1 2562306a36Sopenharmony_ci#define MODULEMODE_SWCTRL 0x2 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define OMAP4_IDLEST_MASK (0x3 << 16) 2862306a36Sopenharmony_ci#define OMAP4_IDLEST_SHIFT 16 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define OMAP4_STBYST_MASK BIT(18) 3162306a36Sopenharmony_ci#define OMAP4_STBYST_SHIFT 18 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define CLKCTRL_IDLEST_FUNCTIONAL 0x0 3462306a36Sopenharmony_ci#define CLKCTRL_IDLEST_INTERFACE_IDLE 0x2 3562306a36Sopenharmony_ci#define CLKCTRL_IDLEST_DISABLED 0x3 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* These timeouts are in us */ 3862306a36Sopenharmony_ci#define OMAP4_MAX_MODULE_READY_TIME 2000 3962306a36Sopenharmony_ci#define OMAP4_MAX_MODULE_DISABLE_TIME 5000 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic bool _early_timeout = true; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistruct omap_clkctrl_provider { 4462306a36Sopenharmony_ci void __iomem *base; 4562306a36Sopenharmony_ci struct list_head clocks; 4662306a36Sopenharmony_ci char *clkdm_name; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct omap_clkctrl_clk { 5062306a36Sopenharmony_ci struct clk_hw *clk; 5162306a36Sopenharmony_ci u16 reg_offset; 5262306a36Sopenharmony_ci int bit_offset; 5362306a36Sopenharmony_ci struct list_head node; 5462306a36Sopenharmony_ci}; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciunion omap4_timeout { 5762306a36Sopenharmony_ci u32 cycles; 5862306a36Sopenharmony_ci ktime_t start; 5962306a36Sopenharmony_ci}; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cistatic const struct omap_clkctrl_data default_clkctrl_data[] __initconst = { 6262306a36Sopenharmony_ci { 0 }, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic u32 _omap4_idlest(u32 val) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci val &= OMAP4_IDLEST_MASK; 6862306a36Sopenharmony_ci val >>= OMAP4_IDLEST_SHIFT; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return val; 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic bool _omap4_is_idle(u32 val) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci val = _omap4_idlest(val); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return val == CLKCTRL_IDLEST_DISABLED; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistatic bool _omap4_is_ready(u32 val) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci val = _omap4_idlest(val); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return val == CLKCTRL_IDLEST_FUNCTIONAL || 8562306a36Sopenharmony_ci val == CLKCTRL_IDLEST_INTERFACE_IDLE; 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic bool _omap4_is_timeout(union omap4_timeout *time, u32 timeout) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci /* 9162306a36Sopenharmony_ci * There are two special cases where ktime_to_ns() can't be 9262306a36Sopenharmony_ci * used to track the timeouts. First one is during early boot 9362306a36Sopenharmony_ci * when the timers haven't been initialized yet. The second 9462306a36Sopenharmony_ci * one is during suspend-resume cycle while timekeeping is 9562306a36Sopenharmony_ci * being suspended / resumed. Clocksource for the system 9662306a36Sopenharmony_ci * can be from a timer that requires pm_runtime access, which 9762306a36Sopenharmony_ci * will eventually bring us here with timekeeping_suspended, 9862306a36Sopenharmony_ci * during both suspend entry and resume paths. This happens 9962306a36Sopenharmony_ci * at least on am43xx platform. Account for flakeyness 10062306a36Sopenharmony_ci * with udelay() by multiplying the timeout value by 2. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci if (unlikely(_early_timeout || timekeeping_suspended)) { 10362306a36Sopenharmony_ci if (time->cycles++ < timeout) { 10462306a36Sopenharmony_ci udelay(1 * 2); 10562306a36Sopenharmony_ci return false; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci } else { 10862306a36Sopenharmony_ci if (!ktime_to_ns(time->start)) { 10962306a36Sopenharmony_ci time->start = ktime_get(); 11062306a36Sopenharmony_ci return false; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (ktime_us_delta(ktime_get(), time->start) < timeout) { 11462306a36Sopenharmony_ci cpu_relax(); 11562306a36Sopenharmony_ci return false; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return true; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int __init _omap4_disable_early_timeout(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci _early_timeout = false; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ciarch_initcall(_omap4_disable_early_timeout); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic int _omap4_clkctrl_clk_enable(struct clk_hw *hw) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 13362306a36Sopenharmony_ci u32 val; 13462306a36Sopenharmony_ci int ret; 13562306a36Sopenharmony_ci union omap4_timeout timeout = { 0 }; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (clk->clkdm) { 13862306a36Sopenharmony_ci ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); 13962306a36Sopenharmony_ci if (ret) { 14062306a36Sopenharmony_ci WARN(1, 14162306a36Sopenharmony_ci "%s: could not enable %s's clockdomain %s: %d\n", 14262306a36Sopenharmony_ci __func__, clk_hw_get_name(hw), 14362306a36Sopenharmony_ci clk->clkdm_name, ret); 14462306a36Sopenharmony_ci return ret; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (!clk->enable_bit) 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci val &= ~OMAP4_MODULEMODE_MASK; 15462306a36Sopenharmony_ci val |= clk->enable_bit; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (test_bit(NO_IDLEST, &clk->flags)) 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci /* Wait until module is enabled */ 16262306a36Sopenharmony_ci while (!_omap4_is_ready(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { 16362306a36Sopenharmony_ci if (_omap4_is_timeout(&timeout, OMAP4_MAX_MODULE_READY_TIME)) { 16462306a36Sopenharmony_ci pr_err("%s: failed to enable\n", clk_hw_get_name(hw)); 16562306a36Sopenharmony_ci return -EBUSY; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci return 0; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void _omap4_clkctrl_clk_disable(struct clk_hw *hw) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 17562306a36Sopenharmony_ci u32 val; 17662306a36Sopenharmony_ci union omap4_timeout timeout = { 0 }; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (!clk->enable_bit) 17962306a36Sopenharmony_ci goto exit; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci val &= ~OMAP4_MODULEMODE_MASK; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci ti_clk_ll_ops->clk_writel(val, &clk->enable_reg); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci if (test_bit(NO_IDLEST, &clk->flags)) 18862306a36Sopenharmony_ci goto exit; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Wait until module is disabled */ 19162306a36Sopenharmony_ci while (!_omap4_is_idle(ti_clk_ll_ops->clk_readl(&clk->enable_reg))) { 19262306a36Sopenharmony_ci if (_omap4_is_timeout(&timeout, 19362306a36Sopenharmony_ci OMAP4_MAX_MODULE_DISABLE_TIME)) { 19462306a36Sopenharmony_ci pr_err("%s: failed to disable\n", clk_hw_get_name(hw)); 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ciexit: 20062306a36Sopenharmony_ci if (clk->clkdm) 20162306a36Sopenharmony_ci ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int _omap4_clkctrl_clk_is_enabled(struct clk_hw *hw) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct clk_hw_omap *clk = to_clk_hw_omap(hw); 20762306a36Sopenharmony_ci u32 val; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&clk->enable_reg); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (val & clk->enable_bit) 21262306a36Sopenharmony_ci return 1; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct clk_ops omap4_clkctrl_clk_ops = { 21862306a36Sopenharmony_ci .enable = _omap4_clkctrl_clk_enable, 21962306a36Sopenharmony_ci .disable = _omap4_clkctrl_clk_disable, 22062306a36Sopenharmony_ci .is_enabled = _omap4_clkctrl_clk_is_enabled, 22162306a36Sopenharmony_ci .init = omap2_init_clk_clkdm, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic struct clk_hw *_ti_omap4_clkctrl_xlate(struct of_phandle_args *clkspec, 22562306a36Sopenharmony_ci void *data) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct omap_clkctrl_provider *provider = data; 22862306a36Sopenharmony_ci struct omap_clkctrl_clk *entry = NULL, *iter; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (clkspec->args_count != 2) 23162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci pr_debug("%s: looking for %x:%x\n", __func__, 23462306a36Sopenharmony_ci clkspec->args[0], clkspec->args[1]); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci list_for_each_entry(iter, &provider->clocks, node) { 23762306a36Sopenharmony_ci if (iter->reg_offset == clkspec->args[0] && 23862306a36Sopenharmony_ci iter->bit_offset == clkspec->args[1]) { 23962306a36Sopenharmony_ci entry = iter; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (!entry) 24562306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci return entry->clk; 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci/* Get clkctrl clock base name based on clkctrl_name or dts node */ 25162306a36Sopenharmony_cistatic const char * __init clkctrl_get_clock_name(struct device_node *np, 25262306a36Sopenharmony_ci const char *clkctrl_name, 25362306a36Sopenharmony_ci int offset, int index, 25462306a36Sopenharmony_ci bool legacy_naming) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci char *clock_name; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* l4per-clkctrl:1234:0 style naming based on clkctrl_name */ 25962306a36Sopenharmony_ci if (clkctrl_name && !legacy_naming) { 26062306a36Sopenharmony_ci clock_name = kasprintf(GFP_KERNEL, "%s-clkctrl:%04x:%d", 26162306a36Sopenharmony_ci clkctrl_name, offset, index); 26262306a36Sopenharmony_ci if (!clock_name) 26362306a36Sopenharmony_ci return NULL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci strreplace(clock_name, '_', '-'); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return clock_name; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* l4per:1234:0 old style naming based on clkctrl_name */ 27162306a36Sopenharmony_ci if (clkctrl_name) 27262306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "%s_cm:clk:%04x:%d", 27362306a36Sopenharmony_ci clkctrl_name, offset, index); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* l4per_cm:1234:0 old style naming based on parent node name */ 27662306a36Sopenharmony_ci if (legacy_naming) 27762306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "%pOFn:clk:%04x:%d", 27862306a36Sopenharmony_ci np->parent, offset, index); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* l4per-clkctrl:1234:0 style naming based on node name */ 28162306a36Sopenharmony_ci return kasprintf(GFP_KERNEL, "%pOFn:%04x:%d", np, offset, index); 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int __init 28562306a36Sopenharmony_ci_ti_clkctrl_clk_register(struct omap_clkctrl_provider *provider, 28662306a36Sopenharmony_ci struct device_node *node, struct clk_hw *clk_hw, 28762306a36Sopenharmony_ci u16 offset, u8 bit, const char * const *parents, 28862306a36Sopenharmony_ci int num_parents, const struct clk_ops *ops, 28962306a36Sopenharmony_ci const char *clkctrl_name) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct clk_init_data init = { NULL }; 29262306a36Sopenharmony_ci struct clk *clk; 29362306a36Sopenharmony_ci struct omap_clkctrl_clk *clkctrl_clk; 29462306a36Sopenharmony_ci int ret = 0; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci init.name = clkctrl_get_clock_name(node, clkctrl_name, offset, bit, 29762306a36Sopenharmony_ci ti_clk_get_features()->flags & 29862306a36Sopenharmony_ci TI_CLK_CLKCTRL_COMPAT); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); 30162306a36Sopenharmony_ci if (!init.name || !clkctrl_clk) { 30262306a36Sopenharmony_ci ret = -ENOMEM; 30362306a36Sopenharmony_ci goto cleanup; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci clk_hw->init = &init; 30762306a36Sopenharmony_ci init.parent_names = parents; 30862306a36Sopenharmony_ci init.num_parents = num_parents; 30962306a36Sopenharmony_ci init.ops = ops; 31062306a36Sopenharmony_ci init.flags = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci clk = of_ti_clk_register(node, clk_hw, init.name); 31362306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) { 31462306a36Sopenharmony_ci ret = -EINVAL; 31562306a36Sopenharmony_ci goto cleanup; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci clkctrl_clk->reg_offset = offset; 31962306a36Sopenharmony_ci clkctrl_clk->bit_offset = bit; 32062306a36Sopenharmony_ci clkctrl_clk->clk = clk_hw; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci list_add(&clkctrl_clk->node, &provider->clocks); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci return 0; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cicleanup: 32762306a36Sopenharmony_ci kfree(init.name); 32862306a36Sopenharmony_ci kfree(clkctrl_clk); 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void __init 33362306a36Sopenharmony_ci_ti_clkctrl_setup_gate(struct omap_clkctrl_provider *provider, 33462306a36Sopenharmony_ci struct device_node *node, u16 offset, 33562306a36Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 33662306a36Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct clk_hw_omap *clk_hw; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL); 34162306a36Sopenharmony_ci if (!clk_hw) 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci clk_hw->enable_bit = data->bit; 34562306a36Sopenharmony_ci clk_hw->enable_reg.ptr = reg; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &clk_hw->hw, offset, 34862306a36Sopenharmony_ci data->bit, data->parents, 1, 34962306a36Sopenharmony_ci &omap_gate_clk_ops, clkctrl_name)) 35062306a36Sopenharmony_ci kfree(clk_hw); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void __init 35462306a36Sopenharmony_ci_ti_clkctrl_setup_mux(struct omap_clkctrl_provider *provider, 35562306a36Sopenharmony_ci struct device_node *node, u16 offset, 35662306a36Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 35762306a36Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct clk_omap_mux *mux; 36062306a36Sopenharmony_ci int num_parents = 0; 36162306a36Sopenharmony_ci const char * const *pname; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci mux = kzalloc(sizeof(*mux), GFP_KERNEL); 36462306a36Sopenharmony_ci if (!mux) 36562306a36Sopenharmony_ci return; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci pname = data->parents; 36862306a36Sopenharmony_ci while (*pname) { 36962306a36Sopenharmony_ci num_parents++; 37062306a36Sopenharmony_ci pname++; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci mux->mask = num_parents; 37462306a36Sopenharmony_ci if (!(mux->flags & CLK_MUX_INDEX_ONE)) 37562306a36Sopenharmony_ci mux->mask--; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci mux->mask = (1 << fls(mux->mask)) - 1; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci mux->shift = data->bit; 38062306a36Sopenharmony_ci mux->reg.ptr = reg; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &mux->hw, offset, 38362306a36Sopenharmony_ci data->bit, data->parents, num_parents, 38462306a36Sopenharmony_ci &ti_clk_mux_ops, clkctrl_name)) 38562306a36Sopenharmony_ci kfree(mux); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic void __init 38962306a36Sopenharmony_ci_ti_clkctrl_setup_div(struct omap_clkctrl_provider *provider, 39062306a36Sopenharmony_ci struct device_node *node, u16 offset, 39162306a36Sopenharmony_ci const struct omap_clkctrl_bit_data *data, 39262306a36Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct clk_omap_divider *div; 39562306a36Sopenharmony_ci const struct omap_clkctrl_div_data *div_data = data->data; 39662306a36Sopenharmony_ci u8 div_flags = 0; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci div = kzalloc(sizeof(*div), GFP_KERNEL); 39962306a36Sopenharmony_ci if (!div) 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci div->reg.ptr = reg; 40362306a36Sopenharmony_ci div->shift = data->bit; 40462306a36Sopenharmony_ci div->flags = div_data->flags; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci if (div->flags & CLK_DIVIDER_POWER_OF_TWO) 40762306a36Sopenharmony_ci div_flags |= CLKF_INDEX_POWER_OF_TWO; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (ti_clk_parse_divider_data((int *)div_data->dividers, 0, 41062306a36Sopenharmony_ci div_data->max_div, div_flags, 41162306a36Sopenharmony_ci div)) { 41262306a36Sopenharmony_ci pr_err("%s: Data parsing for %pOF:%04x:%d failed\n", __func__, 41362306a36Sopenharmony_ci node, offset, data->bit); 41462306a36Sopenharmony_ci kfree(div); 41562306a36Sopenharmony_ci return; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (_ti_clkctrl_clk_register(provider, node, &div->hw, offset, 41962306a36Sopenharmony_ci data->bit, data->parents, 1, 42062306a36Sopenharmony_ci &ti_clk_divider_ops, clkctrl_name)) 42162306a36Sopenharmony_ci kfree(div); 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void __init 42562306a36Sopenharmony_ci_ti_clkctrl_setup_subclks(struct omap_clkctrl_provider *provider, 42662306a36Sopenharmony_ci struct device_node *node, 42762306a36Sopenharmony_ci const struct omap_clkctrl_reg_data *data, 42862306a36Sopenharmony_ci void __iomem *reg, const char *clkctrl_name) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci const struct omap_clkctrl_bit_data *bits = data->bit_data; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!bits) 43362306a36Sopenharmony_ci return; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci while (bits->bit) { 43662306a36Sopenharmony_ci switch (bits->type) { 43762306a36Sopenharmony_ci case TI_CLK_GATE: 43862306a36Sopenharmony_ci _ti_clkctrl_setup_gate(provider, node, data->offset, 43962306a36Sopenharmony_ci bits, reg, clkctrl_name); 44062306a36Sopenharmony_ci break; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci case TI_CLK_DIVIDER: 44362306a36Sopenharmony_ci _ti_clkctrl_setup_div(provider, node, data->offset, 44462306a36Sopenharmony_ci bits, reg, clkctrl_name); 44562306a36Sopenharmony_ci break; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci case TI_CLK_MUX: 44862306a36Sopenharmony_ci _ti_clkctrl_setup_mux(provider, node, data->offset, 44962306a36Sopenharmony_ci bits, reg, clkctrl_name); 45062306a36Sopenharmony_ci break; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci pr_err("%s: bad subclk type: %d\n", __func__, 45462306a36Sopenharmony_ci bits->type); 45562306a36Sopenharmony_ci return; 45662306a36Sopenharmony_ci } 45762306a36Sopenharmony_ci bits++; 45862306a36Sopenharmony_ci } 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic void __init _clkctrl_add_provider(void *data, 46262306a36Sopenharmony_ci struct device_node *np) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci of_clk_add_hw_provider(np, _ti_omap4_clkctrl_xlate, data); 46562306a36Sopenharmony_ci} 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* 46862306a36Sopenharmony_ci * Get clock name based on "clock-output-names" property or the 46962306a36Sopenharmony_ci * compatible property for clkctrl. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_cistatic const char * __init clkctrl_get_name(struct device_node *np) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct property *prop; 47462306a36Sopenharmony_ci const int prefix_len = 11; 47562306a36Sopenharmony_ci const char *compat; 47662306a36Sopenharmony_ci const char *output; 47762306a36Sopenharmony_ci const char *end; 47862306a36Sopenharmony_ci char *name; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (!of_property_read_string_index(np, "clock-output-names", 0, 48162306a36Sopenharmony_ci &output)) { 48262306a36Sopenharmony_ci int len; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci len = strlen(output); 48562306a36Sopenharmony_ci end = strstr(output, "_clkctrl"); 48662306a36Sopenharmony_ci if (end) 48762306a36Sopenharmony_ci len -= strlen(end); 48862306a36Sopenharmony_ci name = kstrndup(output, len, GFP_KERNEL); 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci return name; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci of_property_for_each_string(np, "compatible", prop, compat) { 49462306a36Sopenharmony_ci if (!strncmp("ti,clkctrl-", compat, prefix_len)) { 49562306a36Sopenharmony_ci end = compat + prefix_len; 49662306a36Sopenharmony_ci /* Two letter minimum name length for l3, l4 etc */ 49762306a36Sopenharmony_ci if (strnlen(end, 16) < 2) 49862306a36Sopenharmony_ci continue; 49962306a36Sopenharmony_ci name = kstrdup_and_replace(end, '-', '_', GFP_KERNEL); 50062306a36Sopenharmony_ci if (!name) 50162306a36Sopenharmony_ci continue; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return name; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci return NULL; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void __init _ti_omap4_clkctrl_setup(struct device_node *node) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct omap_clkctrl_provider *provider; 51362306a36Sopenharmony_ci const struct omap_clkctrl_data *data = default_clkctrl_data; 51462306a36Sopenharmony_ci const struct omap_clkctrl_reg_data *reg_data; 51562306a36Sopenharmony_ci struct clk_init_data init = { NULL }; 51662306a36Sopenharmony_ci struct clk_hw_omap *hw; 51762306a36Sopenharmony_ci struct clk *clk; 51862306a36Sopenharmony_ci struct omap_clkctrl_clk *clkctrl_clk = NULL; 51962306a36Sopenharmony_ci bool legacy_naming; 52062306a36Sopenharmony_ci const char *clkctrl_name; 52162306a36Sopenharmony_ci u32 addr; 52262306a36Sopenharmony_ci int ret; 52362306a36Sopenharmony_ci char *c; 52462306a36Sopenharmony_ci u16 soc_mask = 0; 52562306a36Sopenharmony_ci struct resource res; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci of_address_to_resource(node, 0, &res); 52862306a36Sopenharmony_ci addr = (u32)res.start; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP4 53162306a36Sopenharmony_ci if (of_machine_is_compatible("ti,omap4")) 53262306a36Sopenharmony_ci data = omap4_clkctrl_data; 53362306a36Sopenharmony_ci#endif 53462306a36Sopenharmony_ci#ifdef CONFIG_SOC_OMAP5 53562306a36Sopenharmony_ci if (of_machine_is_compatible("ti,omap5")) 53662306a36Sopenharmony_ci data = omap5_clkctrl_data; 53762306a36Sopenharmony_ci#endif 53862306a36Sopenharmony_ci#ifdef CONFIG_SOC_DRA7XX 53962306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dra7")) 54062306a36Sopenharmony_ci data = dra7_clkctrl_data; 54162306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dra72")) 54262306a36Sopenharmony_ci soc_mask = CLKF_SOC_DRA72; 54362306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dra74")) 54462306a36Sopenharmony_ci soc_mask = CLKF_SOC_DRA74; 54562306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dra76")) 54662306a36Sopenharmony_ci soc_mask = CLKF_SOC_DRA76; 54762306a36Sopenharmony_ci#endif 54862306a36Sopenharmony_ci#ifdef CONFIG_SOC_AM33XX 54962306a36Sopenharmony_ci if (of_machine_is_compatible("ti,am33xx")) 55062306a36Sopenharmony_ci data = am3_clkctrl_data; 55162306a36Sopenharmony_ci#endif 55262306a36Sopenharmony_ci#ifdef CONFIG_SOC_AM43XX 55362306a36Sopenharmony_ci if (of_machine_is_compatible("ti,am4372")) 55462306a36Sopenharmony_ci data = am4_clkctrl_data; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (of_machine_is_compatible("ti,am438x")) 55762306a36Sopenharmony_ci data = am438x_clkctrl_data; 55862306a36Sopenharmony_ci#endif 55962306a36Sopenharmony_ci#ifdef CONFIG_SOC_TI81XX 56062306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dm814")) 56162306a36Sopenharmony_ci data = dm814_clkctrl_data; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci if (of_machine_is_compatible("ti,dm816")) 56462306a36Sopenharmony_ci data = dm816_clkctrl_data; 56562306a36Sopenharmony_ci#endif 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (ti_clk_get_features()->flags & TI_CLK_DEVICE_TYPE_GP) 56862306a36Sopenharmony_ci soc_mask |= CLKF_SOC_NONSEC; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci while (data->addr) { 57162306a36Sopenharmony_ci if (addr == data->addr) 57262306a36Sopenharmony_ci break; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci data++; 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (!data->addr) { 57862306a36Sopenharmony_ci pr_err("%pOF not found from clkctrl data.\n", node); 57962306a36Sopenharmony_ci return; 58062306a36Sopenharmony_ci } 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci provider = kzalloc(sizeof(*provider), GFP_KERNEL); 58362306a36Sopenharmony_ci if (!provider) 58462306a36Sopenharmony_ci return; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci provider->base = of_iomap(node, 0); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci legacy_naming = ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT; 58962306a36Sopenharmony_ci clkctrl_name = clkctrl_get_name(node); 59062306a36Sopenharmony_ci if (clkctrl_name) { 59162306a36Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, 59262306a36Sopenharmony_ci "%s_clkdm", clkctrl_name); 59362306a36Sopenharmony_ci if (!provider->clkdm_name) { 59462306a36Sopenharmony_ci kfree(provider); 59562306a36Sopenharmony_ci return; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci goto clkdm_found; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* 60162306a36Sopenharmony_ci * The code below can be removed when all clkctrl nodes use domain 60262306a36Sopenharmony_ci * specific compatible property and standard clock node naming 60362306a36Sopenharmony_ci */ 60462306a36Sopenharmony_ci if (legacy_naming) { 60562306a36Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFnxxx", node->parent); 60662306a36Sopenharmony_ci if (!provider->clkdm_name) { 60762306a36Sopenharmony_ci kfree(provider); 60862306a36Sopenharmony_ci return; 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* 61262306a36Sopenharmony_ci * Create default clkdm name, replace _cm from end of parent 61362306a36Sopenharmony_ci * node name with _clkdm 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_ci provider->clkdm_name[strlen(provider->clkdm_name) - 2] = 0; 61662306a36Sopenharmony_ci } else { 61762306a36Sopenharmony_ci provider->clkdm_name = kasprintf(GFP_KERNEL, "%pOFn", node); 61862306a36Sopenharmony_ci if (!provider->clkdm_name) { 61962306a36Sopenharmony_ci kfree(provider); 62062306a36Sopenharmony_ci return; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci /* 62462306a36Sopenharmony_ci * Create default clkdm name, replace _clkctrl from end of 62562306a36Sopenharmony_ci * node name with _clkdm 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_ci provider->clkdm_name[strlen(provider->clkdm_name) - 7] = 0; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci strcat(provider->clkdm_name, "clkdm"); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Replace any dash from the clkdm name with underscore */ 63362306a36Sopenharmony_ci c = provider->clkdm_name; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci while (*c) { 63662306a36Sopenharmony_ci if (*c == '-') 63762306a36Sopenharmony_ci *c = '_'; 63862306a36Sopenharmony_ci c++; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ciclkdm_found: 64162306a36Sopenharmony_ci INIT_LIST_HEAD(&provider->clocks); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci /* Generate clocks */ 64462306a36Sopenharmony_ci reg_data = data->regs; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci while (reg_data->parent) { 64762306a36Sopenharmony_ci if ((reg_data->flags & CLKF_SOC_MASK) && 64862306a36Sopenharmony_ci (reg_data->flags & soc_mask) == 0) { 64962306a36Sopenharmony_ci reg_data++; 65062306a36Sopenharmony_ci continue; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci hw = kzalloc(sizeof(*hw), GFP_KERNEL); 65462306a36Sopenharmony_ci if (!hw) 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci hw->enable_reg.ptr = provider->base + reg_data->offset; 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci _ti_clkctrl_setup_subclks(provider, node, reg_data, 66062306a36Sopenharmony_ci hw->enable_reg.ptr, clkctrl_name); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (reg_data->flags & CLKF_SW_SUP) 66362306a36Sopenharmony_ci hw->enable_bit = MODULEMODE_SWCTRL; 66462306a36Sopenharmony_ci if (reg_data->flags & CLKF_HW_SUP) 66562306a36Sopenharmony_ci hw->enable_bit = MODULEMODE_HWCTRL; 66662306a36Sopenharmony_ci if (reg_data->flags & CLKF_NO_IDLEST) 66762306a36Sopenharmony_ci set_bit(NO_IDLEST, &hw->flags); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci if (reg_data->clkdm_name) 67062306a36Sopenharmony_ci hw->clkdm_name = reg_data->clkdm_name; 67162306a36Sopenharmony_ci else 67262306a36Sopenharmony_ci hw->clkdm_name = provider->clkdm_name; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci init.parent_names = ®_data->parent; 67562306a36Sopenharmony_ci init.num_parents = 1; 67662306a36Sopenharmony_ci init.flags = 0; 67762306a36Sopenharmony_ci if (reg_data->flags & CLKF_SET_RATE_PARENT) 67862306a36Sopenharmony_ci init.flags |= CLK_SET_RATE_PARENT; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci init.name = clkctrl_get_clock_name(node, clkctrl_name, 68162306a36Sopenharmony_ci reg_data->offset, 0, 68262306a36Sopenharmony_ci legacy_naming); 68362306a36Sopenharmony_ci if (!init.name) 68462306a36Sopenharmony_ci goto cleanup; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci clkctrl_clk = kzalloc(sizeof(*clkctrl_clk), GFP_KERNEL); 68762306a36Sopenharmony_ci if (!clkctrl_clk) 68862306a36Sopenharmony_ci goto cleanup; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci init.ops = &omap4_clkctrl_clk_ops; 69162306a36Sopenharmony_ci hw->hw.init = &init; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci clk = of_ti_clk_register_omap_hw(node, &hw->hw, init.name); 69462306a36Sopenharmony_ci if (IS_ERR_OR_NULL(clk)) 69562306a36Sopenharmony_ci goto cleanup; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci clkctrl_clk->reg_offset = reg_data->offset; 69862306a36Sopenharmony_ci clkctrl_clk->clk = &hw->hw; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci list_add(&clkctrl_clk->node, &provider->clocks); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci reg_data++; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ret = of_clk_add_hw_provider(node, _ti_omap4_clkctrl_xlate, provider); 70662306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 70762306a36Sopenharmony_ci ti_clk_retry_init(node, provider, _clkctrl_add_provider); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci kfree(clkctrl_name); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci return; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cicleanup: 71462306a36Sopenharmony_ci kfree(hw); 71562306a36Sopenharmony_ci kfree(init.name); 71662306a36Sopenharmony_ci kfree(clkctrl_name); 71762306a36Sopenharmony_ci kfree(clkctrl_clk); 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ciCLK_OF_DECLARE(ti_omap4_clkctrl_clock, "ti,clkctrl", 72062306a36Sopenharmony_ci _ti_omap4_clkctrl_setup); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci/** 72362306a36Sopenharmony_ci * ti_clk_is_in_standby - Check if clkctrl clock is in standby or not 72462306a36Sopenharmony_ci * @clk: clock to check standby status for 72562306a36Sopenharmony_ci * 72662306a36Sopenharmony_ci * Finds whether the provided clock is in standby mode or not. Returns 72762306a36Sopenharmony_ci * true if the provided clock is a clkctrl type clock and it is in standby, 72862306a36Sopenharmony_ci * false otherwise. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cibool ti_clk_is_in_standby(struct clk *clk) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct clk_hw *hw; 73362306a36Sopenharmony_ci struct clk_hw_omap *hwclk; 73462306a36Sopenharmony_ci u32 val; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci hw = __clk_get_hw(clk); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (!omap2_clk_is_hw_omap(hw)) 73962306a36Sopenharmony_ci return false; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci hwclk = to_clk_hw_omap(hw); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci val = ti_clk_ll_ops->clk_readl(&hwclk->enable_reg); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (val & OMAP4_STBYST_MASK) 74662306a36Sopenharmony_ci return true; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci return false; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(ti_clk_is_in_standby); 751