1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * OMAP clockdomain support 4 * 5 * Copyright (C) 2013 Texas Instruments, Inc. 6 * 7 * Tero Kristo <t-kristo@ti.com> 8 */ 9 10 #include <linux/clk.h> 11 #include <linux/clk-provider.h> 12 #include <linux/slab.h> 13 #include <linux/of.h> 14 #include <linux/of_address.h> 15 #include <linux/clk/ti.h> 16 17 #include "clock.h" 18 19 #undef pr_fmt 20 #define pr_fmt(fmt) "%s: " fmt, __func__ 21 22 /** 23 * omap2_clkops_enable_clkdm - increment usecount on clkdm of @hw 24 * @hw: struct clk_hw * of the clock being enabled 25 * 26 * Increment the usecount of the clockdomain of the clock pointed to 27 * by @hw; if the usecount is 1, the clockdomain will be "enabled." 28 * Only needed for clocks that don't use omap2_dflt_clk_enable() as 29 * their enable function pointer. Passes along the return value of 30 * clkdm_clk_enable(), -EINVAL if @hw is not associated with a 31 * clockdomain, or 0 if clock framework-based clockdomain control is 32 * not implemented. 33 */ 34 int omap2_clkops_enable_clkdm(struct clk_hw *hw) 35 { 36 struct clk_hw_omap *clk; 37 int ret = 0; 38 39 clk = to_clk_hw_omap(hw); 40 41 if (unlikely(!clk->clkdm)) { 42 pr_err("%s: %s: no clkdm set ?!\n", __func__, 43 clk_hw_get_name(hw)); 44 return -EINVAL; 45 } 46 47 if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) { 48 pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n", 49 __func__, clk_hw_get_name(hw)); 50 return 0; 51 } 52 53 ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk); 54 WARN(ret, "%s: could not enable %s's clockdomain %s: %d\n", 55 __func__, clk_hw_get_name(hw), clk->clkdm_name, ret); 56 57 return ret; 58 } 59 60 /** 61 * omap2_clkops_disable_clkdm - decrement usecount on clkdm of @hw 62 * @hw: struct clk_hw * of the clock being disabled 63 * 64 * Decrement the usecount of the clockdomain of the clock pointed to 65 * by @hw; if the usecount is 0, the clockdomain will be "disabled." 66 * Only needed for clocks that don't use omap2_dflt_clk_disable() as their 67 * disable function pointer. No return value. 68 */ 69 void omap2_clkops_disable_clkdm(struct clk_hw *hw) 70 { 71 struct clk_hw_omap *clk; 72 73 clk = to_clk_hw_omap(hw); 74 75 if (unlikely(!clk->clkdm)) { 76 pr_err("%s: %s: no clkdm set ?!\n", __func__, 77 clk_hw_get_name(hw)); 78 return; 79 } 80 81 if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) { 82 pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n", 83 __func__, clk_hw_get_name(hw)); 84 return; 85 } 86 87 ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk); 88 } 89 90 /** 91 * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk 92 * @hw: Pointer to clk_hw_omap used to obtain OMAP clock struct ptr to use 93 * 94 * Convert a clockdomain name stored in a struct clk 'clk' into a 95 * clockdomain pointer, and save it into the struct clk. Intended to be 96 * called during clk_register(). Returns 0 on success, -EERROR otherwise. 97 */ 98 int omap2_init_clk_clkdm(struct clk_hw *hw) 99 { 100 struct clk_hw_omap *clk = to_clk_hw_omap(hw); 101 struct clockdomain *clkdm; 102 const char *clk_name; 103 104 if (!clk->clkdm_name) 105 return 0; 106 107 clk_name = __clk_get_name(hw->clk); 108 109 clkdm = ti_clk_ll_ops->clkdm_lookup(clk->clkdm_name); 110 if (clkdm) { 111 pr_debug("clock: associated clk %s to clkdm %s\n", 112 clk_name, clk->clkdm_name); 113 clk->clkdm = clkdm; 114 } else { 115 pr_debug("clock: could not associate clk %s to clkdm %s\n", 116 clk_name, clk->clkdm_name); 117 } 118 119 return 0; 120 } 121 122 static void __init of_ti_clockdomain_setup(struct device_node *node) 123 { 124 struct clk *clk; 125 struct clk_hw *clk_hw; 126 const char *clkdm_name = ti_dt_clk_name(node); 127 int i; 128 unsigned int num_clks; 129 130 num_clks = of_clk_get_parent_count(node); 131 132 for (i = 0; i < num_clks; i++) { 133 clk = of_clk_get(node, i); 134 if (IS_ERR(clk)) { 135 pr_err("%s: Failed get %pOF' clock nr %d (%ld)\n", 136 __func__, node, i, PTR_ERR(clk)); 137 continue; 138 } 139 clk_hw = __clk_get_hw(clk); 140 if (!omap2_clk_is_hw_omap(clk_hw)) { 141 pr_warn("can't setup clkdm for basic clk %s\n", 142 __clk_get_name(clk)); 143 clk_put(clk); 144 continue; 145 } 146 to_clk_hw_omap(clk_hw)->clkdm_name = clkdm_name; 147 omap2_init_clk_clkdm(clk_hw); 148 clk_put(clk); 149 } 150 } 151 152 static const struct of_device_id ti_clkdm_match_table[] __initconst = { 153 { .compatible = "ti,clockdomain" }, 154 { } 155 }; 156 157 /** 158 * ti_dt_clockdomains_setup - setup device tree clockdomains 159 * 160 * Initializes clockdomain nodes for a SoC. This parses through all the 161 * nodes with compatible = "ti,clockdomain", and add the clockdomain 162 * info for all the clocks listed under these. This function shall be 163 * called after rest of the DT clock init has completed and all 164 * clock nodes have been registered. 165 */ 166 void __init ti_dt_clockdomains_setup(void) 167 { 168 struct device_node *np; 169 for_each_matching_node(np, ti_clkdm_match_table) { 170 of_ti_clockdomain_setup(np); 171 } 172 } 173