18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * OMAP gate 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
258c2ecf20Sopenharmony_ci#include "clock.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#undef pr_fmt
288c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *clk);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct clk_ops omap_gate_clkdm_clk_ops = {
338c2ecf20Sopenharmony_ci	.init		= &omap2_init_clk_clkdm,
348c2ecf20Sopenharmony_ci	.enable		= &omap2_clkops_enable_clkdm,
358c2ecf20Sopenharmony_ci	.disable	= &omap2_clkops_disable_clkdm,
368c2ecf20Sopenharmony_ci	.restore_context = clk_gate_restore_context,
378c2ecf20Sopenharmony_ci};
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ciconst struct clk_ops omap_gate_clk_ops = {
408c2ecf20Sopenharmony_ci	.init		= &omap2_init_clk_clkdm,
418c2ecf20Sopenharmony_ci	.enable		= &omap2_dflt_clk_enable,
428c2ecf20Sopenharmony_ci	.disable	= &omap2_dflt_clk_disable,
438c2ecf20Sopenharmony_ci	.is_enabled	= &omap2_dflt_clk_is_enabled,
448c2ecf20Sopenharmony_ci	.restore_context = clk_gate_restore_context,
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic const struct clk_ops omap_gate_clk_hsdiv_restore_ops = {
488c2ecf20Sopenharmony_ci	.init		= &omap2_init_clk_clkdm,
498c2ecf20Sopenharmony_ci	.enable		= &omap36xx_gate_clk_enable_with_hsdiv_restore,
508c2ecf20Sopenharmony_ci	.disable	= &omap2_dflt_clk_disable,
518c2ecf20Sopenharmony_ci	.is_enabled	= &omap2_dflt_clk_is_enabled,
528c2ecf20Sopenharmony_ci	.restore_context = clk_gate_restore_context,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/**
568c2ecf20Sopenharmony_ci * omap36xx_gate_clk_enable_with_hsdiv_restore - enable clocks suffering
578c2ecf20Sopenharmony_ci *         from HSDivider PWRDN problem Implements Errata ID: i556.
588c2ecf20Sopenharmony_ci * @clk: DPLL output struct clk
598c2ecf20Sopenharmony_ci *
608c2ecf20Sopenharmony_ci * 3630 only: dpll3_m3_ck, dpll4_m2_ck, dpll4_m3_ck, dpll4_m4_ck,
618c2ecf20Sopenharmony_ci * dpll4_m5_ck & dpll4_m6_ck dividers gets loaded with reset
628c2ecf20Sopenharmony_ci * valueafter their respective PWRDN bits are set.  Any dummy write
638c2ecf20Sopenharmony_ci * (Any other value different from the Read value) to the
648c2ecf20Sopenharmony_ci * corresponding CM_CLKSEL register will refresh the dividers.
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_cistatic int omap36xx_gate_clk_enable_with_hsdiv_restore(struct clk_hw *hw)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	struct clk_omap_divider *parent;
698c2ecf20Sopenharmony_ci	struct clk_hw *parent_hw;
708c2ecf20Sopenharmony_ci	u32 dummy_v, orig_v;
718c2ecf20Sopenharmony_ci	int ret;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/* Clear PWRDN bit of HSDIVIDER */
748c2ecf20Sopenharmony_ci	ret = omap2_dflt_clk_enable(hw);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Parent is the x2 node, get parent of parent for the m2 div */
778c2ecf20Sopenharmony_ci	parent_hw = clk_hw_get_parent(clk_hw_get_parent(hw));
788c2ecf20Sopenharmony_ci	parent = to_clk_omap_divider(parent_hw);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	/* Restore the dividers */
818c2ecf20Sopenharmony_ci	if (!ret) {
828c2ecf20Sopenharmony_ci		orig_v = ti_clk_ll_ops->clk_readl(&parent->reg);
838c2ecf20Sopenharmony_ci		dummy_v = orig_v;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		/* Write any other value different from the Read value */
868c2ecf20Sopenharmony_ci		dummy_v ^= (1 << parent->shift);
878c2ecf20Sopenharmony_ci		ti_clk_ll_ops->clk_writel(dummy_v, &parent->reg);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		/* Write the original divider */
908c2ecf20Sopenharmony_ci		ti_clk_ll_ops->clk_writel(orig_v, &parent->reg);
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic struct clk *_register_gate(struct device_node *node, const char *name,
978c2ecf20Sopenharmony_ci				  const char *parent_name, unsigned long flags,
988c2ecf20Sopenharmony_ci				  struct clk_omap_reg *reg, u8 bit_idx,
998c2ecf20Sopenharmony_ci				  u8 clk_gate_flags, const struct clk_ops *ops,
1008c2ecf20Sopenharmony_ci				  const struct clk_hw_omap_ops *hw_ops)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	struct clk_init_data init = { NULL };
1038c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk_hw;
1048c2ecf20Sopenharmony_ci	struct clk *clk;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
1078c2ecf20Sopenharmony_ci	if (!clk_hw)
1088c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	clk_hw->hw.init = &init;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	init.name = name;
1138c2ecf20Sopenharmony_ci	init.ops = ops;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	memcpy(&clk_hw->enable_reg, reg, sizeof(*reg));
1168c2ecf20Sopenharmony_ci	clk_hw->enable_bit = bit_idx;
1178c2ecf20Sopenharmony_ci	clk_hw->ops = hw_ops;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	clk_hw->flags = clk_gate_flags;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	init.parent_names = &parent_name;
1228c2ecf20Sopenharmony_ci	init.num_parents = 1;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	init.flags = flags;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	clk = of_ti_clk_register_omap_hw(node, &clk_hw->hw, name);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
1298c2ecf20Sopenharmony_ci		kfree(clk_hw);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return clk;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic void __init _of_ti_gate_clk_setup(struct device_node *node,
1358c2ecf20Sopenharmony_ci					 const struct clk_ops *ops,
1368c2ecf20Sopenharmony_ci					 const struct clk_hw_omap_ops *hw_ops)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct clk *clk;
1398c2ecf20Sopenharmony_ci	const char *parent_name;
1408c2ecf20Sopenharmony_ci	struct clk_omap_reg reg;
1418c2ecf20Sopenharmony_ci	const char *name;
1428c2ecf20Sopenharmony_ci	u8 enable_bit = 0;
1438c2ecf20Sopenharmony_ci	u32 val;
1448c2ecf20Sopenharmony_ci	u32 flags = 0;
1458c2ecf20Sopenharmony_ci	u8 clk_gate_flags = 0;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (ops != &omap_gate_clkdm_clk_ops) {
1488c2ecf20Sopenharmony_ci		if (ti_clk_get_reg_addr(node, 0, &reg))
1498c2ecf20Sopenharmony_ci			return;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci		if (!of_property_read_u32(node, "ti,bit-shift", &val))
1528c2ecf20Sopenharmony_ci			enable_bit = val;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	if (of_clk_get_parent_count(node) != 1) {
1568c2ecf20Sopenharmony_ci		pr_err("%pOFn must have 1 parent\n", node);
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "ti,set-rate-parent"))
1638c2ecf20Sopenharmony_ci		flags |= CLK_SET_RATE_PARENT;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	if (of_property_read_bool(node, "ti,set-bit-to-disable"))
1668c2ecf20Sopenharmony_ci		clk_gate_flags |= INVERT_ENABLE;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	name = ti_dt_clk_name(node);
1698c2ecf20Sopenharmony_ci	clk = _register_gate(node, name, parent_name, flags, &reg,
1708c2ecf20Sopenharmony_ci			     enable_bit, clk_gate_flags, ops, hw_ops);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	if (!IS_ERR(clk))
1738c2ecf20Sopenharmony_ci		of_clk_add_provider(node, of_clk_src_simple_get, clk);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void __init
1778c2ecf20Sopenharmony_ci_of_ti_composite_gate_clk_setup(struct device_node *node,
1788c2ecf20Sopenharmony_ci				const struct clk_hw_omap_ops *hw_ops)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct clk_hw_omap *gate;
1818c2ecf20Sopenharmony_ci	u32 val = 0;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
1848c2ecf20Sopenharmony_ci	if (!gate)
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (ti_clk_get_reg_addr(node, 0, &gate->enable_reg))
1888c2ecf20Sopenharmony_ci		goto cleanup;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	of_property_read_u32(node, "ti,bit-shift", &val);
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	gate->enable_bit = val;
1938c2ecf20Sopenharmony_ci	gate->ops = hw_ops;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (!ti_clk_add_component(node, &gate->hw, CLK_COMPONENT_TYPE_GATE))
1968c2ecf20Sopenharmony_ci		return;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cicleanup:
1998c2ecf20Sopenharmony_ci	kfree(gate);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic void __init
2038c2ecf20Sopenharmony_ciof_ti_composite_no_wait_gate_clk_setup(struct device_node *node)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	_of_ti_composite_gate_clk_setup(node, NULL);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_composite_no_wait_gate_clk, "ti,composite-no-wait-gate-clock",
2088c2ecf20Sopenharmony_ci	       of_ti_composite_no_wait_gate_clk_setup);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
2118c2ecf20Sopenharmony_cistatic void __init of_ti_composite_interface_clk_setup(struct device_node *node)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	_of_ti_composite_gate_clk_setup(node, &clkhwops_iclk_wait);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_composite_interface_clk, "ti,composite-interface-clock",
2168c2ecf20Sopenharmony_ci	       of_ti_composite_interface_clk_setup);
2178c2ecf20Sopenharmony_ci#endif
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic void __init of_ti_composite_gate_clk_setup(struct device_node *node)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	_of_ti_composite_gate_clk_setup(node, &clkhwops_wait);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_composite_gate_clk, "ti,composite-gate-clock",
2248c2ecf20Sopenharmony_ci	       of_ti_composite_gate_clk_setup);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void __init of_ti_clkdm_gate_clk_setup(struct device_node *node)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clkdm_clk_ops, NULL);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_clkdm_gate_clk, "ti,clkdm-gate-clock",
2328c2ecf20Sopenharmony_ci	       of_ti_clkdm_gate_clk_setup);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistatic void __init of_ti_hsdiv_gate_clk_setup(struct device_node *node)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clk_hsdiv_restore_ops,
2378c2ecf20Sopenharmony_ci			      &clkhwops_wait);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_hsdiv_gate_clk, "ti,hsdiv-gate-clock",
2408c2ecf20Sopenharmony_ci	       of_ti_hsdiv_gate_clk_setup);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void __init of_ti_gate_clk_setup(struct device_node *node)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clk_ops, NULL);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_gate_clk, "ti,gate-clock", of_ti_gate_clk_setup);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic void __init of_ti_wait_gate_clk_setup(struct device_node *node)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clk_ops, &clkhwops_wait);
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_wait_gate_clk, "ti,wait-gate-clock",
2538c2ecf20Sopenharmony_ci	       of_ti_wait_gate_clk_setup);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci#ifdef CONFIG_ARCH_OMAP3
2568c2ecf20Sopenharmony_cistatic void __init of_ti_am35xx_gate_clk_setup(struct device_node *node)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
2598c2ecf20Sopenharmony_ci			      &clkhwops_am35xx_ipss_module_wait);
2608c2ecf20Sopenharmony_ci}
2618c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_am35xx_gate_clk, "ti,am35xx-gate-clock",
2628c2ecf20Sopenharmony_ci	       of_ti_am35xx_gate_clk_setup);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void __init of_ti_dss_gate_clk_setup(struct device_node *node)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	_of_ti_gate_clk_setup(node, &omap_gate_clk_ops,
2678c2ecf20Sopenharmony_ci			      &clkhwops_omap3430es2_dss_usbhost_wait);
2688c2ecf20Sopenharmony_ci}
2698c2ecf20Sopenharmony_ciCLK_OF_DECLARE(ti_dss_gate_clk, "ti,dss-gate-clock",
2708c2ecf20Sopenharmony_ci	       of_ti_dss_gate_clk_setup);
2718c2ecf20Sopenharmony_ci#endif
272