271 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * TI composite clock support4 *5 * Copyright (C) 2013 Texas Instruments, Inc.6 *7 * Tero Kristo <t-kristo@ti.com>8 */9 10#include <linux/clk-provider.h>11#include <linux/slab.h>12#include <linux/io.h>13#include <linux/of.h>14#include <linux/of_address.h>15#include <linux/clk/ti.h>16#include <linux/list.h>17 18#include "clock.h"19 20#undef pr_fmt21#define pr_fmt(fmt) "%s: " fmt, __func__22 23static unsigned long ti_composite_recalc_rate(struct clk_hw *hw,24 unsigned long parent_rate)25{26 return ti_clk_divider_ops.recalc_rate(hw, parent_rate);27}28 29static long ti_composite_round_rate(struct clk_hw *hw, unsigned long rate,30 unsigned long *prate)31{32 return -EINVAL;33}34 35static int ti_composite_set_rate(struct clk_hw *hw, unsigned long rate,36 unsigned long parent_rate)37{38 return -EINVAL;39}40 41static const struct clk_ops ti_composite_divider_ops = {42 .recalc_rate = &ti_composite_recalc_rate,43 .round_rate = &ti_composite_round_rate,44 .set_rate = &ti_composite_set_rate,45};46 47static const struct clk_ops ti_composite_gate_ops = {48 .enable = &omap2_dflt_clk_enable,49 .disable = &omap2_dflt_clk_disable,50 .is_enabled = &omap2_dflt_clk_is_enabled,51};52 53struct component_clk {54 int num_parents;55 const char **parent_names;56 struct device_node *node;57 int type;58 struct clk_hw *hw;59 struct list_head link;60};61 62static const char * const component_clk_types[] __initconst = {63 "gate", "divider", "mux"64};65 66static LIST_HEAD(component_clks);67 68static struct device_node *_get_component_node(struct device_node *node, int i)69{70 int rc;71 struct of_phandle_args clkspec;72 73 rc = of_parse_phandle_with_args(node, "clocks", "#clock-cells", i,74 &clkspec);75 if (rc)76 return NULL;77 78 return clkspec.np;79}80 81static struct component_clk *_lookup_component(struct device_node *node)82{83 struct component_clk *comp;84 85 list_for_each_entry(comp, &component_clks, link) {86 if (comp->node == node)87 return comp;88 }89 return NULL;90}91 92struct clk_hw_omap_comp {93 struct clk_hw hw;94 struct device_node *comp_nodes[CLK_COMPONENT_TYPE_MAX];95 struct component_clk *comp_clks[CLK_COMPONENT_TYPE_MAX];96};97 98static inline struct clk_hw *_get_hw(struct clk_hw_omap_comp *clk, int idx)99{100 if (!clk)101 return NULL;102 103 if (!clk->comp_clks[idx])104 return NULL;105 106 return clk->comp_clks[idx]->hw;107}108 109#define to_clk_hw_comp(_hw) container_of(_hw, struct clk_hw_omap_comp, hw)110 111static void __init _register_composite(void *user,112 struct device_node *node)113{114 struct clk_hw *hw = user;115 struct clk *clk;116 struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw);117 struct component_clk *comp;118 int num_parents = 0;119 const char **parent_names = NULL;120 const char *name;121 int i;122 int ret;123 124 /* Check for presence of each component clock */125 for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) {126 if (!cclk->comp_nodes[i])127 continue;128 129 comp = _lookup_component(cclk->comp_nodes[i]);130 if (!comp) {131 pr_debug("component %s not ready for %pOFn, retry\n",132 cclk->comp_nodes[i]->name, node);133 if (!ti_clk_retry_init(node, hw,134 _register_composite))135 return;136 137 goto cleanup;138 }139 if (cclk->comp_clks[comp->type] != NULL) {140 pr_err("duplicate component types for %pOFn (%s)!\n",141 node, component_clk_types[comp->type]);142 goto cleanup;143 }144 145 cclk->comp_clks[comp->type] = comp;146 147 /* Mark this node as found */148 cclk->comp_nodes[i] = NULL;149 }150 151 /* All components exists, proceed with registration */152 for (i = CLK_COMPONENT_TYPE_MAX - 1; i >= 0; i--) {153 comp = cclk->comp_clks[i];154 if (!comp)155 continue;156 if (comp->num_parents) {157 num_parents = comp->num_parents;158 parent_names = comp->parent_names;159 break;160 }161 }162 163 if (!num_parents) {164 pr_err("%s: no parents found for %pOFn!\n", __func__, node);165 goto cleanup;166 }167 168 name = ti_dt_clk_name(node);169 clk = clk_register_composite(NULL, name,170 parent_names, num_parents,171 _get_hw(cclk, CLK_COMPONENT_TYPE_MUX),172 &ti_clk_mux_ops,173 _get_hw(cclk, CLK_COMPONENT_TYPE_DIVIDER),174 &ti_composite_divider_ops,175 _get_hw(cclk, CLK_COMPONENT_TYPE_GATE),176 &ti_composite_gate_ops, 0);177 178 if (!IS_ERR(clk)) {179 ret = ti_clk_add_alias(clk, name);180 if (ret) {181 clk_unregister(clk);182 goto cleanup;183 }184 of_clk_add_provider(node, of_clk_src_simple_get, clk);185 }186 187cleanup:188 /* Free component clock list entries */189 for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) {190 if (!cclk->comp_clks[i])191 continue;192 list_del(&cclk->comp_clks[i]->link);193 kfree(cclk->comp_clks[i]->parent_names);194 kfree(cclk->comp_clks[i]);195 }196 197 kfree(cclk);198}199 200static void __init of_ti_composite_clk_setup(struct device_node *node)201{202 unsigned int num_clks;203 int i;204 struct clk_hw_omap_comp *cclk;205 206 /* Number of component clocks to be put inside this clock */207 num_clks = of_clk_get_parent_count(node);208 209 if (!num_clks) {210 pr_err("composite clk %pOFn must have component(s)\n", node);211 return;212 }213 214 cclk = kzalloc(sizeof(*cclk), GFP_KERNEL);215 if (!cclk)216 return;217 218 /* Get device node pointers for each component clock */219 for (i = 0; i < num_clks; i++)220 cclk->comp_nodes[i] = _get_component_node(node, i);221 222 _register_composite(&cclk->hw, node);223}224CLK_OF_DECLARE(ti_composite_clock, "ti,composite-clock",225 of_ti_composite_clk_setup);226 227/**228 * ti_clk_add_component - add a component clock to the pool229 * @node: device node of the component clock230 * @hw: hardware clock definition for the component clock231 * @type: type of the component clock232 *233 * Adds a component clock to the list of available components, so that234 * it can be registered by a composite clock.235 */236int __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw,237 int type)238{239 unsigned int num_parents;240 const char **parent_names;241 struct component_clk *clk;242 243 num_parents = of_clk_get_parent_count(node);244 245 if (!num_parents) {246 pr_err("component-clock %pOFn must have parent(s)\n", node);247 return -EINVAL;248 }249 250 parent_names = kcalloc(num_parents, sizeof(char *), GFP_KERNEL);251 if (!parent_names)252 return -ENOMEM;253 254 of_clk_parent_fill(node, parent_names, num_parents);255 256 clk = kzalloc(sizeof(*clk), GFP_KERNEL);257 if (!clk) {258 kfree(parent_names);259 return -ENOMEM;260 }261 262 clk->num_parents = num_parents;263 clk->parent_names = parent_names;264 clk->hw = hw;265 clk->node = node;266 clk->type = type;267 list_add(&clk->link, &component_clks);268 269 return 0;270}271