xref: /kernel/linux/linux-5.10/drivers/clk/ti/apll.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * OMAP APLL clock support
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2013 Texas Instruments, Inc.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * J Keerthy <j-keerthy@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/module.h>
218c2ecf20Sopenharmony_ci#include <linux/slab.h>
228c2ecf20Sopenharmony_ci#include <linux/io.h>
238c2ecf20Sopenharmony_ci#include <linux/err.h>
248c2ecf20Sopenharmony_ci#include <linux/string.h>
258c2ecf20Sopenharmony_ci#include <linux/log2.h>
268c2ecf20Sopenharmony_ci#include <linux/of.h>
278c2ecf20Sopenharmony_ci#include <linux/of_address.h>
288c2ecf20Sopenharmony_ci#include <linux/clk/ti.h>
298c2ecf20Sopenharmony_ci#include <linux/delay.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#include "clock.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define APLL_FORCE_LOCK 0x1
348c2ecf20Sopenharmony_ci#define APLL_AUTO_IDLE	0x2
358c2ecf20Sopenharmony_ci#define MAX_APLL_WAIT_TRIES		1000000
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#undef pr_fmt
388c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int dra7_apll_enable(struct clk_hw *hw)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
438c2ecf20Sopenharmony_ci	int r = 0, i = 0;
448c2ecf20Sopenharmony_ci	struct dpll_data *ad;
458c2ecf20Sopenharmony_ci	const char *clk_name;
468c2ecf20Sopenharmony_ci	u8 state = 1;
478c2ecf20Sopenharmony_ci	u32 v;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	ad = clk->dpll_data;
508c2ecf20Sopenharmony_ci	if (!ad)
518c2ecf20Sopenharmony_ci		return -EINVAL;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	clk_name = clk_hw_get_name(&clk->hw);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	state <<= __ffs(ad->idlest_mask);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	/* Check is already locked */
588c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if ((v & ad->idlest_mask) == state)
618c2ecf20Sopenharmony_ci		return r;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
648c2ecf20Sopenharmony_ci	v &= ~ad->enable_mask;
658c2ecf20Sopenharmony_ci	v |= APLL_FORCE_LOCK << __ffs(ad->enable_mask);
668c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	state <<= __ffs(ad->idlest_mask);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	while (1) {
718c2ecf20Sopenharmony_ci		v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
728c2ecf20Sopenharmony_ci		if ((v & ad->idlest_mask) == state)
738c2ecf20Sopenharmony_ci			break;
748c2ecf20Sopenharmony_ci		if (i > MAX_APLL_WAIT_TRIES)
758c2ecf20Sopenharmony_ci			break;
768c2ecf20Sopenharmony_ci		i++;
778c2ecf20Sopenharmony_ci		udelay(1);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (i == MAX_APLL_WAIT_TRIES) {
818c2ecf20Sopenharmony_ci		pr_warn("clock: %s failed transition to '%s'\n",
828c2ecf20Sopenharmony_ci			clk_name, (state) ? "locked" : "bypassed");
838c2ecf20Sopenharmony_ci		r = -EBUSY;
848c2ecf20Sopenharmony_ci	} else
858c2ecf20Sopenharmony_ci		pr_debug("clock: %s transition to '%s' in %d loops\n",
868c2ecf20Sopenharmony_ci			 clk_name, (state) ? "locked" : "bypassed", i);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return r;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void dra7_apll_disable(struct clk_hw *hw)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
948c2ecf20Sopenharmony_ci	struct dpll_data *ad;
958c2ecf20Sopenharmony_ci	u8 state = 1;
968c2ecf20Sopenharmony_ci	u32 v;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	ad = clk->dpll_data;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	state <<= __ffs(ad->idlest_mask);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
1038c2ecf20Sopenharmony_ci	v &= ~ad->enable_mask;
1048c2ecf20Sopenharmony_ci	v |= APLL_AUTO_IDLE << __ffs(ad->enable_mask);
1058c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int dra7_apll_is_enabled(struct clk_hw *hw)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
1118c2ecf20Sopenharmony_ci	struct dpll_data *ad;
1128c2ecf20Sopenharmony_ci	u32 v;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	ad = clk->dpll_data;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
1178c2ecf20Sopenharmony_ci	v &= ad->enable_mask;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	v >>= __ffs(ad->enable_mask);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	return v == APLL_AUTO_IDLE ? 0 : 1;
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic u8 dra7_init_apll_parent(struct clk_hw *hw)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	return 0;
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic const struct clk_ops apll_ck_ops = {
1308c2ecf20Sopenharmony_ci	.enable		= &dra7_apll_enable,
1318c2ecf20Sopenharmony_ci	.disable	= &dra7_apll_disable,
1328c2ecf20Sopenharmony_ci	.is_enabled	= &dra7_apll_is_enabled,
1338c2ecf20Sopenharmony_ci	.get_parent	= &dra7_init_apll_parent,
1348c2ecf20Sopenharmony_ci};
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void __init omap_clk_register_apll(void *user,
1378c2ecf20Sopenharmony_ci					  struct device_node *node)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct clk_hw *hw = user;
1408c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk_hw = to_clk_hw_omap(hw);
1418c2ecf20Sopenharmony_ci	struct dpll_data *ad = clk_hw->dpll_data;
1428c2ecf20Sopenharmony_ci	const char *name;
1438c2ecf20Sopenharmony_ci	struct clk *clk;
1448c2ecf20Sopenharmony_ci	const struct clk_init_data *init = clk_hw->hw.init;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	clk = of_clk_get(node, 0);
1478c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1488c2ecf20Sopenharmony_ci		pr_debug("clk-ref for %pOFn not ready, retry\n",
1498c2ecf20Sopenharmony_ci			 node);
1508c2ecf20Sopenharmony_ci		if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))
1518c2ecf20Sopenharmony_ci			return;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		goto cleanup;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	ad->clk_ref = __clk_get_hw(clk);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	clk = of_clk_get(node, 1);
1598c2ecf20Sopenharmony_ci	if (IS_ERR(clk)) {
1608c2ecf20Sopenharmony_ci		pr_debug("clk-bypass for %pOFn not ready, retry\n",
1618c2ecf20Sopenharmony_ci			 node);
1628c2ecf20Sopenharmony_ci		if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))
1638c2ecf20Sopenharmony_ci			return;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		goto cleanup;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ad->clk_bypass = __clk_get_hw(clk);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	name = ti_dt_clk_name(node);
1718c2ecf20Sopenharmony_ci	clk = of_ti_clk_register_omap_hw(node, &clk_hw->hw, name);
1728c2ecf20Sopenharmony_ci	if (!IS_ERR(clk)) {
1738c2ecf20Sopenharmony_ci		of_clk_add_provider(node, of_clk_src_simple_get, clk);
1748c2ecf20Sopenharmony_ci		kfree(init->parent_names);
1758c2ecf20Sopenharmony_ci		kfree(init);
1768c2ecf20Sopenharmony_ci		return;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cicleanup:
1808c2ecf20Sopenharmony_ci	kfree(clk_hw->dpll_data);
1818c2ecf20Sopenharmony_ci	kfree(init->parent_names);
1828c2ecf20Sopenharmony_ci	kfree(init);
1838c2ecf20Sopenharmony_ci	kfree(clk_hw);
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic void __init of_dra7_apll_setup(struct device_node *node)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct dpll_data *ad = NULL;
1898c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk_hw = NULL;
1908c2ecf20Sopenharmony_ci	struct clk_init_data *init = NULL;
1918c2ecf20Sopenharmony_ci	const char **parent_names = NULL;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ad = kzalloc(sizeof(*ad), GFP_KERNEL);
1958c2ecf20Sopenharmony_ci	clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
1968c2ecf20Sopenharmony_ci	init = kzalloc(sizeof(*init), GFP_KERNEL);
1978c2ecf20Sopenharmony_ci	if (!ad || !clk_hw || !init)
1988c2ecf20Sopenharmony_ci		goto cleanup;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	clk_hw->dpll_data = ad;
2018c2ecf20Sopenharmony_ci	clk_hw->hw.init = init;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	init->name = ti_dt_clk_name(node);
2048c2ecf20Sopenharmony_ci	init->ops = &apll_ck_ops;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	init->num_parents = of_clk_get_parent_count(node);
2078c2ecf20Sopenharmony_ci	if (init->num_parents < 1) {
2088c2ecf20Sopenharmony_ci		pr_err("dra7 apll %pOFn must have parent(s)\n", node);
2098c2ecf20Sopenharmony_ci		goto cleanup;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	parent_names = kcalloc(init->num_parents, sizeof(char *), GFP_KERNEL);
2138c2ecf20Sopenharmony_ci	if (!parent_names)
2148c2ecf20Sopenharmony_ci		goto cleanup;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	of_clk_parent_fill(node, parent_names, init->num_parents);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	init->parent_names = parent_names;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);
2218c2ecf20Sopenharmony_ci	ret |= ti_clk_get_reg_addr(node, 1, &ad->idlest_reg);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	if (ret)
2248c2ecf20Sopenharmony_ci		goto cleanup;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	ad->idlest_mask = 0x1;
2278c2ecf20Sopenharmony_ci	ad->enable_mask = 0x3;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	omap_clk_register_apll(&clk_hw->hw, node);
2308c2ecf20Sopenharmony_ci	return;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_cicleanup:
2338c2ecf20Sopenharmony_ci	kfree(parent_names);
2348c2ecf20Sopenharmony_ci	kfree(ad);
2358c2ecf20Sopenharmony_ci	kfree(clk_hw);
2368c2ecf20Sopenharmony_ci	kfree(init);
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciCLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci#define OMAP2_EN_APLL_LOCKED	0x3
2418c2ecf20Sopenharmony_ci#define OMAP2_EN_APLL_STOPPED	0x0
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_cistatic int omap2_apll_is_enabled(struct clk_hw *hw)
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
2468c2ecf20Sopenharmony_ci	struct dpll_data *ad = clk->dpll_data;
2478c2ecf20Sopenharmony_ci	u32 v;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
2508c2ecf20Sopenharmony_ci	v &= ad->enable_mask;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	v >>= __ffs(ad->enable_mask);
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	return v == OMAP2_EN_APLL_LOCKED ? 1 : 0;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic unsigned long omap2_apll_recalc(struct clk_hw *hw,
2588c2ecf20Sopenharmony_ci				       unsigned long parent_rate)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (omap2_apll_is_enabled(hw))
2638c2ecf20Sopenharmony_ci		return clk->fixed_rate;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic int omap2_apll_enable(struct clk_hw *hw)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
2718c2ecf20Sopenharmony_ci	struct dpll_data *ad = clk->dpll_data;
2728c2ecf20Sopenharmony_ci	u32 v;
2738c2ecf20Sopenharmony_ci	int i = 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
2768c2ecf20Sopenharmony_ci	v &= ~ad->enable_mask;
2778c2ecf20Sopenharmony_ci	v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask);
2788c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	while (1) {
2818c2ecf20Sopenharmony_ci		v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);
2828c2ecf20Sopenharmony_ci		if (v & ad->idlest_mask)
2838c2ecf20Sopenharmony_ci			break;
2848c2ecf20Sopenharmony_ci		if (i > MAX_APLL_WAIT_TRIES)
2858c2ecf20Sopenharmony_ci			break;
2868c2ecf20Sopenharmony_ci		i++;
2878c2ecf20Sopenharmony_ci		udelay(1);
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	if (i == MAX_APLL_WAIT_TRIES) {
2918c2ecf20Sopenharmony_ci		pr_warn("%s failed to transition to locked\n",
2928c2ecf20Sopenharmony_ci			clk_hw_get_name(&clk->hw));
2938c2ecf20Sopenharmony_ci		return -EBUSY;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic void omap2_apll_disable(struct clk_hw *hw)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk = to_clk_hw_omap(hw);
3028c2ecf20Sopenharmony_ci	struct dpll_data *ad = clk->dpll_data;
3038c2ecf20Sopenharmony_ci	u32 v;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);
3068c2ecf20Sopenharmony_ci	v &= ~ad->enable_mask;
3078c2ecf20Sopenharmony_ci	v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask);
3088c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
3098c2ecf20Sopenharmony_ci}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_cistatic const struct clk_ops omap2_apll_ops = {
3128c2ecf20Sopenharmony_ci	.enable		= &omap2_apll_enable,
3138c2ecf20Sopenharmony_ci	.disable	= &omap2_apll_disable,
3148c2ecf20Sopenharmony_ci	.is_enabled	= &omap2_apll_is_enabled,
3158c2ecf20Sopenharmony_ci	.recalc_rate	= &omap2_apll_recalc,
3168c2ecf20Sopenharmony_ci};
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_cistatic void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct dpll_data *ad = clk->dpll_data;
3218c2ecf20Sopenharmony_ci	u32 v;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	v = ti_clk_ll_ops->clk_readl(&ad->autoidle_reg);
3248c2ecf20Sopenharmony_ci	v &= ~ad->autoidle_mask;
3258c2ecf20Sopenharmony_ci	v |= val << __ffs(ad->autoidle_mask);
3268c2ecf20Sopenharmony_ci	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci#define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP	0x3
3308c2ecf20Sopenharmony_ci#define OMAP2_APLL_AUTOIDLE_DISABLE		0x0
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic void omap2_apll_allow_idle(struct clk_hw_omap *clk)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic void omap2_apll_deny_idle(struct clk_hw_omap *clk)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic const struct clk_hw_omap_ops omap2_apll_hwops = {
3438c2ecf20Sopenharmony_ci	.allow_idle	= &omap2_apll_allow_idle,
3448c2ecf20Sopenharmony_ci	.deny_idle	= &omap2_apll_deny_idle,
3458c2ecf20Sopenharmony_ci};
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void __init of_omap2_apll_setup(struct device_node *node)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct dpll_data *ad = NULL;
3508c2ecf20Sopenharmony_ci	struct clk_hw_omap *clk_hw = NULL;
3518c2ecf20Sopenharmony_ci	struct clk_init_data *init = NULL;
3528c2ecf20Sopenharmony_ci	const char *name;
3538c2ecf20Sopenharmony_ci	struct clk *clk;
3548c2ecf20Sopenharmony_ci	const char *parent_name;
3558c2ecf20Sopenharmony_ci	u32 val;
3568c2ecf20Sopenharmony_ci	int ret;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ad = kzalloc(sizeof(*ad), GFP_KERNEL);
3598c2ecf20Sopenharmony_ci	clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);
3608c2ecf20Sopenharmony_ci	init = kzalloc(sizeof(*init), GFP_KERNEL);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	if (!ad || !clk_hw || !init)
3638c2ecf20Sopenharmony_ci		goto cleanup;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	clk_hw->dpll_data = ad;
3668c2ecf20Sopenharmony_ci	clk_hw->hw.init = init;
3678c2ecf20Sopenharmony_ci	init->ops = &omap2_apll_ops;
3688c2ecf20Sopenharmony_ci	name = ti_dt_clk_name(node);
3698c2ecf20Sopenharmony_ci	init->name = name;
3708c2ecf20Sopenharmony_ci	clk_hw->ops = &omap2_apll_hwops;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	init->num_parents = of_clk_get_parent_count(node);
3738c2ecf20Sopenharmony_ci	if (init->num_parents != 1) {
3748c2ecf20Sopenharmony_ci		pr_err("%pOFn must have one parent\n", node);
3758c2ecf20Sopenharmony_ci		goto cleanup;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	parent_name = of_clk_get_parent_name(node, 0);
3798c2ecf20Sopenharmony_ci	init->parent_names = &parent_name;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "ti,clock-frequency", &val)) {
3828c2ecf20Sopenharmony_ci		pr_err("%pOFn missing clock-frequency\n", node);
3838c2ecf20Sopenharmony_ci		goto cleanup;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci	clk_hw->fixed_rate = val;
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "ti,bit-shift", &val)) {
3888c2ecf20Sopenharmony_ci		pr_err("%pOFn missing bit-shift\n", node);
3898c2ecf20Sopenharmony_ci		goto cleanup;
3908c2ecf20Sopenharmony_ci	}
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	clk_hw->enable_bit = val;
3938c2ecf20Sopenharmony_ci	ad->enable_mask = 0x3 << val;
3948c2ecf20Sopenharmony_ci	ad->autoidle_mask = 0x3 << val;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	if (of_property_read_u32(node, "ti,idlest-shift", &val)) {
3978c2ecf20Sopenharmony_ci		pr_err("%pOFn missing idlest-shift\n", node);
3988c2ecf20Sopenharmony_ci		goto cleanup;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	ad->idlest_mask = 1 << val;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);
4048c2ecf20Sopenharmony_ci	ret |= ti_clk_get_reg_addr(node, 1, &ad->autoidle_reg);
4058c2ecf20Sopenharmony_ci	ret |= ti_clk_get_reg_addr(node, 2, &ad->idlest_reg);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	if (ret)
4088c2ecf20Sopenharmony_ci		goto cleanup;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	name = ti_dt_clk_name(node);
4118c2ecf20Sopenharmony_ci	clk = of_ti_clk_register_omap_hw(node, &clk_hw->hw, name);
4128c2ecf20Sopenharmony_ci	if (!IS_ERR(clk)) {
4138c2ecf20Sopenharmony_ci		of_clk_add_provider(node, of_clk_src_simple_get, clk);
4148c2ecf20Sopenharmony_ci		kfree(init);
4158c2ecf20Sopenharmony_ci		return;
4168c2ecf20Sopenharmony_ci	}
4178c2ecf20Sopenharmony_cicleanup:
4188c2ecf20Sopenharmony_ci	kfree(ad);
4198c2ecf20Sopenharmony_ci	kfree(clk_hw);
4208c2ecf20Sopenharmony_ci	kfree(init);
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ciCLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock",
4238c2ecf20Sopenharmony_ci	       of_omap2_apll_setup);
424