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