18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * OMAP clockdomain 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.h>
198c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci#include <linux/of.h>
228c2ecf20Sopenharmony_ci#include <linux/of_address.h>
238c2ecf20Sopenharmony_ci#include <linux/clk/ti.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include "clock.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#undef pr_fmt
288c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/**
318c2ecf20Sopenharmony_ci * omap2_clkops_enable_clkdm - increment usecount on clkdm of @hw
328c2ecf20Sopenharmony_ci * @hw: struct clk_hw * of the clock being enabled
338c2ecf20Sopenharmony_ci *
348c2ecf20Sopenharmony_ci * Increment the usecount of the clockdomain of the clock pointed to
358c2ecf20Sopenharmony_ci * by @hw; if the usecount is 1, the clockdomain will be "enabled."
368c2ecf20Sopenharmony_ci * Only needed for clocks that don't use omap2_dflt_clk_enable() as
378c2ecf20Sopenharmony_ci * their enable function pointer.  Passes along the return value of
388c2ecf20Sopenharmony_ci * clkdm_clk_enable(), -EINVAL if @hw is not associated with a
398c2ecf20Sopenharmony_ci * clockdomain, or 0 if clock framework-based clockdomain control is
408c2ecf20Sopenharmony_ci * not implemented.
418c2ecf20Sopenharmony_ci */
428c2ecf20Sopenharmony_ciint omap2_clkops_enable_clkdm(struct clk_hw *hw)
438c2ecf20Sopenharmony_ci{
448c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk;
458c2ecf20Sopenharmony_ci	int ret = 0;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	clk = to_clk_hw_omap(hw);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (unlikely(!clk->clkdm)) {
508c2ecf20Sopenharmony_ci		pr_err("%s: %s: no clkdm set ?!\n", __func__,
518c2ecf20Sopenharmony_ci		       clk_hw_get_name(hw));
528c2ecf20Sopenharmony_ci		return -EINVAL;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
568c2ecf20Sopenharmony_ci		pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
578c2ecf20Sopenharmony_ci		       __func__, clk_hw_get_name(hw));
588c2ecf20Sopenharmony_ci		return 0;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	ret = ti_clk_ll_ops->clkdm_clk_enable(clk->clkdm, hw->clk);
628c2ecf20Sopenharmony_ci	WARN(ret, "%s: could not enable %s's clockdomain %s: %d\n",
638c2ecf20Sopenharmony_ci	     __func__, clk_hw_get_name(hw), clk->clkdm_name, ret);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return ret;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/**
698c2ecf20Sopenharmony_ci * omap2_clkops_disable_clkdm - decrement usecount on clkdm of @hw
708c2ecf20Sopenharmony_ci * @hw: struct clk_hw * of the clock being disabled
718c2ecf20Sopenharmony_ci *
728c2ecf20Sopenharmony_ci * Decrement the usecount of the clockdomain of the clock pointed to
738c2ecf20Sopenharmony_ci * by @hw; if the usecount is 0, the clockdomain will be "disabled."
748c2ecf20Sopenharmony_ci * Only needed for clocks that don't use omap2_dflt_clk_disable() as their
758c2ecf20Sopenharmony_ci * disable function pointer.  No return value.
768c2ecf20Sopenharmony_ci */
778c2ecf20Sopenharmony_civoid omap2_clkops_disable_clkdm(struct clk_hw *hw)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	clk = to_clk_hw_omap(hw);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (unlikely(!clk->clkdm)) {
848c2ecf20Sopenharmony_ci		pr_err("%s: %s: no clkdm set ?!\n", __func__,
858c2ecf20Sopenharmony_ci		       clk_hw_get_name(hw));
868c2ecf20Sopenharmony_ci		return;
878c2ecf20Sopenharmony_ci	}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	if (ti_clk_get_features()->flags & TI_CLK_DISABLE_CLKDM_CONTROL) {
908c2ecf20Sopenharmony_ci		pr_err("%s: %s: clkfw-based clockdomain control disabled ?!\n",
918c2ecf20Sopenharmony_ci		       __func__, clk_hw_get_name(hw));
928c2ecf20Sopenharmony_ci		return;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clkdm_clk_disable(clk->clkdm, hw->clk);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/**
998c2ecf20Sopenharmony_ci * omap2_init_clk_clkdm - look up a clockdomain name, store pointer in clk
1008c2ecf20Sopenharmony_ci * @clk: OMAP clock struct ptr to use
1018c2ecf20Sopenharmony_ci *
1028c2ecf20Sopenharmony_ci * Convert a clockdomain name stored in a struct clk 'clk' into a
1038c2ecf20Sopenharmony_ci * clockdomain pointer, and save it into the struct clk.  Intended to be
1048c2ecf20Sopenharmony_ci * called during clk_register(). Returns 0 on success, -EERROR otherwise.
1058c2ecf20Sopenharmony_ci */
1068c2ecf20Sopenharmony_ciint omap2_init_clk_clkdm(struct clk_hw *hw)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
1098c2ecf20Sopenharmony_ci	struct clockdomain *clkdm;
1108c2ecf20Sopenharmony_ci	const char *clk_name;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!clk->clkdm_name)
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	clk_name = __clk_get_name(hw->clk);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	clkdm = ti_clk_ll_ops->clkdm_lookup(clk->clkdm_name);
1188c2ecf20Sopenharmony_ci	if (clkdm) {
1198c2ecf20Sopenharmony_ci		pr_debug("clock: associated clk %s to clkdm %s\n",
1208c2ecf20Sopenharmony_ci			 clk_name, clk->clkdm_name);
1218c2ecf20Sopenharmony_ci		clk->clkdm = clkdm;
1228c2ecf20Sopenharmony_ci	} else {
1238c2ecf20Sopenharmony_ci		pr_debug("clock: could not associate clk %s to clkdm %s\n",
1248c2ecf20Sopenharmony_ci			 clk_name, clk->clkdm_name);
1258c2ecf20Sopenharmony_ci	}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return 0;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic void __init of_ti_clockdomain_setup(struct device_node *node)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct clk *clk;
1338c2ecf20Sopenharmony_ci	struct clk_hw *clk_hw;
1348c2ecf20Sopenharmony_ci	const char *clkdm_name = ti_dt_clk_name(node);
1358c2ecf20Sopenharmony_ci	int i;
1368c2ecf20Sopenharmony_ci	unsigned int num_clks;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	num_clks = of_clk_get_parent_count(node);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	for (i = 0; i < num_clks; i++) {
1418c2ecf20Sopenharmony_ci		clk = of_clk_get(node, i);
1428c2ecf20Sopenharmony_ci		if (IS_ERR(clk)) {
1438c2ecf20Sopenharmony_ci			pr_err("%s: Failed get %pOF' clock nr %d (%ld)\n",
1448c2ecf20Sopenharmony_ci			       __func__, node, i, PTR_ERR(clk));
1458c2ecf20Sopenharmony_ci			continue;
1468c2ecf20Sopenharmony_ci		}
1478c2ecf20Sopenharmony_ci		clk_hw = __clk_get_hw(clk);
1488c2ecf20Sopenharmony_ci		if (!omap2_clk_is_hw_omap(clk_hw)) {
1498c2ecf20Sopenharmony_ci			pr_warn("can't setup clkdm for basic clk %s\n",
1508c2ecf20Sopenharmony_ci				__clk_get_name(clk));
1518c2ecf20Sopenharmony_ci			clk_put(clk);
1528c2ecf20Sopenharmony_ci			continue;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci		to_clk_hw_omap(clk_hw)->clkdm_name = clkdm_name;
1558c2ecf20Sopenharmony_ci		omap2_init_clk_clkdm(clk_hw);
1568c2ecf20Sopenharmony_ci		clk_put(clk);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const struct of_device_id ti_clkdm_match_table[] __initconst = {
1618c2ecf20Sopenharmony_ci	{ .compatible = "ti,clockdomain" },
1628c2ecf20Sopenharmony_ci	{ }
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/**
1668c2ecf20Sopenharmony_ci * ti_dt_clockdomains_setup - setup device tree clockdomains
1678c2ecf20Sopenharmony_ci *
1688c2ecf20Sopenharmony_ci * Initializes clockdomain nodes for a SoC. This parses through all the
1698c2ecf20Sopenharmony_ci * nodes with compatible = "ti,clockdomain", and add the clockdomain
1708c2ecf20Sopenharmony_ci * info for all the clocks listed under these. This function shall be
1718c2ecf20Sopenharmony_ci * called after rest of the DT clock init has completed and all
1728c2ecf20Sopenharmony_ci * clock nodes have been registered.
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_civoid __init ti_dt_clockdomains_setup(void)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct device_node *np;
1778c2ecf20Sopenharmony_ci	for_each_matching_node(np, ti_clkdm_match_table) {
1788c2ecf20Sopenharmony_ci		of_ti_clockdomain_setup(np);
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci}
181