18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * TI composite clock support 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2013 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/io.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_address.h> 238c2ecf20Sopenharmony_ci#include <linux/clk/ti.h> 248c2ecf20Sopenharmony_ci#include <linux/list.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "clock.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#undef pr_fmt 298c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic unsigned long ti_composite_recalc_rate(struct clk_hw *hw, 328c2ecf20Sopenharmony_ci unsigned long parent_rate) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci return ti_clk_divider_ops.recalc_rate(hw, parent_rate); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic long ti_composite_round_rate(struct clk_hw *hw, unsigned long rate, 388c2ecf20Sopenharmony_ci unsigned long *prate) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci return -EINVAL; 418c2ecf20Sopenharmony_ci} 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int ti_composite_set_rate(struct clk_hw *hw, unsigned long rate, 448c2ecf20Sopenharmony_ci unsigned long parent_rate) 458c2ecf20Sopenharmony_ci{ 468c2ecf20Sopenharmony_ci return -EINVAL; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic const struct clk_ops ti_composite_divider_ops = { 508c2ecf20Sopenharmony_ci .recalc_rate = &ti_composite_recalc_rate, 518c2ecf20Sopenharmony_ci .round_rate = &ti_composite_round_rate, 528c2ecf20Sopenharmony_ci .set_rate = &ti_composite_set_rate, 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic const struct clk_ops ti_composite_gate_ops = { 568c2ecf20Sopenharmony_ci .enable = &omap2_dflt_clk_enable, 578c2ecf20Sopenharmony_ci .disable = &omap2_dflt_clk_disable, 588c2ecf20Sopenharmony_ci .is_enabled = &omap2_dflt_clk_is_enabled, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct component_clk { 628c2ecf20Sopenharmony_ci int num_parents; 638c2ecf20Sopenharmony_ci const char **parent_names; 648c2ecf20Sopenharmony_ci struct device_node *node; 658c2ecf20Sopenharmony_ci int type; 668c2ecf20Sopenharmony_ci struct clk_hw *hw; 678c2ecf20Sopenharmony_ci struct list_head link; 688c2ecf20Sopenharmony_ci}; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic const char * const component_clk_types[] __initconst = { 718c2ecf20Sopenharmony_ci "gate", "divider", "mux" 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic LIST_HEAD(component_clks); 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic struct device_node *_get_component_node(struct device_node *node, int i) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci int rc; 798c2ecf20Sopenharmony_ci struct of_phandle_args clkspec; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci rc = of_parse_phandle_with_args(node, "clocks", "#clock-cells", i, 828c2ecf20Sopenharmony_ci &clkspec); 838c2ecf20Sopenharmony_ci if (rc) 848c2ecf20Sopenharmony_ci return NULL; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci return clkspec.np; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic struct component_clk *_lookup_component(struct device_node *node) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct component_clk *comp; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci list_for_each_entry(comp, &component_clks, link) { 948c2ecf20Sopenharmony_ci if (comp->node == node) 958c2ecf20Sopenharmony_ci return comp; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci return NULL; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistruct clk_hw_omap_comp { 1018c2ecf20Sopenharmony_ci struct clk_hw hw; 1028c2ecf20Sopenharmony_ci struct device_node *comp_nodes[CLK_COMPONENT_TYPE_MAX]; 1038c2ecf20Sopenharmony_ci struct component_clk *comp_clks[CLK_COMPONENT_TYPE_MAX]; 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic inline struct clk_hw *_get_hw(struct clk_hw_omap_comp *clk, int idx) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci if (!clk) 1098c2ecf20Sopenharmony_ci return NULL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!clk->comp_clks[idx]) 1128c2ecf20Sopenharmony_ci return NULL; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return clk->comp_clks[idx]->hw; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#define to_clk_hw_comp(_hw) container_of(_hw, struct clk_hw_omap_comp, hw) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic void __init _register_composite(void *user, 1208c2ecf20Sopenharmony_ci struct device_node *node) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct clk_hw *hw = user; 1238c2ecf20Sopenharmony_ci struct clk *clk; 1248c2ecf20Sopenharmony_ci struct clk_hw_omap_comp *cclk = to_clk_hw_comp(hw); 1258c2ecf20Sopenharmony_ci struct component_clk *comp; 1268c2ecf20Sopenharmony_ci int num_parents = 0; 1278c2ecf20Sopenharmony_ci const char **parent_names = NULL; 1288c2ecf20Sopenharmony_ci const char *name; 1298c2ecf20Sopenharmony_ci int i; 1308c2ecf20Sopenharmony_ci int ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Check for presence of each component clock */ 1338c2ecf20Sopenharmony_ci for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) { 1348c2ecf20Sopenharmony_ci if (!cclk->comp_nodes[i]) 1358c2ecf20Sopenharmony_ci continue; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci comp = _lookup_component(cclk->comp_nodes[i]); 1388c2ecf20Sopenharmony_ci if (!comp) { 1398c2ecf20Sopenharmony_ci pr_debug("component %s not ready for %pOFn, retry\n", 1408c2ecf20Sopenharmony_ci cclk->comp_nodes[i]->name, node); 1418c2ecf20Sopenharmony_ci if (!ti_clk_retry_init(node, hw, 1428c2ecf20Sopenharmony_ci _register_composite)) 1438c2ecf20Sopenharmony_ci return; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci goto cleanup; 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci if (cclk->comp_clks[comp->type] != NULL) { 1488c2ecf20Sopenharmony_ci pr_err("duplicate component types for %pOFn (%s)!\n", 1498c2ecf20Sopenharmony_ci node, component_clk_types[comp->type]); 1508c2ecf20Sopenharmony_ci goto cleanup; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci cclk->comp_clks[comp->type] = comp; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Mark this node as found */ 1568c2ecf20Sopenharmony_ci cclk->comp_nodes[i] = NULL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* All components exists, proceed with registration */ 1608c2ecf20Sopenharmony_ci for (i = CLK_COMPONENT_TYPE_MAX - 1; i >= 0; i--) { 1618c2ecf20Sopenharmony_ci comp = cclk->comp_clks[i]; 1628c2ecf20Sopenharmony_ci if (!comp) 1638c2ecf20Sopenharmony_ci continue; 1648c2ecf20Sopenharmony_ci if (comp->num_parents) { 1658c2ecf20Sopenharmony_ci num_parents = comp->num_parents; 1668c2ecf20Sopenharmony_ci parent_names = comp->parent_names; 1678c2ecf20Sopenharmony_ci break; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (!num_parents) { 1728c2ecf20Sopenharmony_ci pr_err("%s: no parents found for %pOFn!\n", __func__, node); 1738c2ecf20Sopenharmony_ci goto cleanup; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci name = ti_dt_clk_name(node); 1778c2ecf20Sopenharmony_ci clk = clk_register_composite(NULL, name, 1788c2ecf20Sopenharmony_ci parent_names, num_parents, 1798c2ecf20Sopenharmony_ci _get_hw(cclk, CLK_COMPONENT_TYPE_MUX), 1808c2ecf20Sopenharmony_ci &ti_clk_mux_ops, 1818c2ecf20Sopenharmony_ci _get_hw(cclk, CLK_COMPONENT_TYPE_DIVIDER), 1828c2ecf20Sopenharmony_ci &ti_composite_divider_ops, 1838c2ecf20Sopenharmony_ci _get_hw(cclk, CLK_COMPONENT_TYPE_GATE), 1848c2ecf20Sopenharmony_ci &ti_composite_gate_ops, 0); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 1878c2ecf20Sopenharmony_ci ret = ti_clk_add_alias(clk, name); 1888c2ecf20Sopenharmony_ci if (ret) { 1898c2ecf20Sopenharmony_ci clk_unregister(clk); 1908c2ecf20Sopenharmony_ci goto cleanup; 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci of_clk_add_provider(node, of_clk_src_simple_get, clk); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cicleanup: 1968c2ecf20Sopenharmony_ci /* Free component clock list entries */ 1978c2ecf20Sopenharmony_ci for (i = 0; i < CLK_COMPONENT_TYPE_MAX; i++) { 1988c2ecf20Sopenharmony_ci if (!cclk->comp_clks[i]) 1998c2ecf20Sopenharmony_ci continue; 2008c2ecf20Sopenharmony_ci list_del(&cclk->comp_clks[i]->link); 2018c2ecf20Sopenharmony_ci kfree(cclk->comp_clks[i]->parent_names); 2028c2ecf20Sopenharmony_ci kfree(cclk->comp_clks[i]); 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci kfree(cclk); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void __init of_ti_composite_clk_setup(struct device_node *node) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci unsigned int num_clks; 2118c2ecf20Sopenharmony_ci int i; 2128c2ecf20Sopenharmony_ci struct clk_hw_omap_comp *cclk; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci /* Number of component clocks to be put inside this clock */ 2158c2ecf20Sopenharmony_ci num_clks = of_clk_get_parent_count(node); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci if (!num_clks) { 2188c2ecf20Sopenharmony_ci pr_err("composite clk %pOFn must have component(s)\n", node); 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci cclk = kzalloc(sizeof(*cclk), GFP_KERNEL); 2238c2ecf20Sopenharmony_ci if (!cclk) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci /* Get device node pointers for each component clock */ 2278c2ecf20Sopenharmony_ci for (i = 0; i < num_clks; i++) 2288c2ecf20Sopenharmony_ci cclk->comp_nodes[i] = _get_component_node(node, i); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci _register_composite(&cclk->hw, node); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_composite_clock, "ti,composite-clock", 2338c2ecf20Sopenharmony_ci of_ti_composite_clk_setup); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci/** 2368c2ecf20Sopenharmony_ci * ti_clk_add_component - add a component clock to the pool 2378c2ecf20Sopenharmony_ci * @node: device node of the component clock 2388c2ecf20Sopenharmony_ci * @hw: hardware clock definition for the component clock 2398c2ecf20Sopenharmony_ci * @type: type of the component clock 2408c2ecf20Sopenharmony_ci * 2418c2ecf20Sopenharmony_ci * Adds a component clock to the list of available components, so that 2428c2ecf20Sopenharmony_ci * it can be registered by a composite clock. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ciint __init ti_clk_add_component(struct device_node *node, struct clk_hw *hw, 2458c2ecf20Sopenharmony_ci int type) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci unsigned int num_parents; 2488c2ecf20Sopenharmony_ci const char **parent_names; 2498c2ecf20Sopenharmony_ci struct component_clk *clk; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci num_parents = of_clk_get_parent_count(node); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (!num_parents) { 2548c2ecf20Sopenharmony_ci pr_err("component-clock %pOFn must have parent(s)\n", node); 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci parent_names = kzalloc((sizeof(char *) * num_parents), GFP_KERNEL); 2598c2ecf20Sopenharmony_ci if (!parent_names) 2608c2ecf20Sopenharmony_ci return -ENOMEM; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci of_clk_parent_fill(node, parent_names, num_parents); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci clk = kzalloc(sizeof(*clk), GFP_KERNEL); 2658c2ecf20Sopenharmony_ci if (!clk) { 2668c2ecf20Sopenharmony_ci kfree(parent_names); 2678c2ecf20Sopenharmony_ci return -ENOMEM; 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci clk->num_parents = num_parents; 2718c2ecf20Sopenharmony_ci clk->parent_names = parent_names; 2728c2ecf20Sopenharmony_ci clk->hw = hw; 2738c2ecf20Sopenharmony_ci clk->node = node; 2748c2ecf20Sopenharmony_ci clk->type = type; 2758c2ecf20Sopenharmony_ci list_add(&clk->link, &component_clks); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return 0; 2788c2ecf20Sopenharmony_ci} 279