brintos

brintos / linux-shallow public Read only

0
0
Text · 9.2 KiB · 43514e6 Raw
411 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * OMAP APLL clock support4 *5 * Copyright (C) 2013 Texas Instruments, Inc.6 *7 * J Keerthy <j-keerthy@ti.com>8 */9 10#include <linux/clk.h>11#include <linux/clk-provider.h>12#include <linux/module.h>13#include <linux/slab.h>14#include <linux/io.h>15#include <linux/err.h>16#include <linux/string.h>17#include <linux/log2.h>18#include <linux/of.h>19#include <linux/of_address.h>20#include <linux/clk/ti.h>21#include <linux/delay.h>22 23#include "clock.h"24 25#define APLL_FORCE_LOCK 0x126#define APLL_AUTO_IDLE	0x227#define MAX_APLL_WAIT_TRIES		100000028 29#undef pr_fmt30#define pr_fmt(fmt) "%s: " fmt, __func__31 32static int dra7_apll_enable(struct clk_hw *hw)33{34	struct clk_hw_omap *clk = to_clk_hw_omap(hw);35	int r = 0, i = 0;36	struct dpll_data *ad;37	const char *clk_name;38	u8 state = 1;39	u32 v;40 41	ad = clk->dpll_data;42	if (!ad)43		return -EINVAL;44 45	clk_name = clk_hw_get_name(&clk->hw);46 47	state <<= __ffs(ad->idlest_mask);48 49	/* Check is already locked */50	v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);51 52	if ((v & ad->idlest_mask) == state)53		return r;54 55	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);56	v &= ~ad->enable_mask;57	v |= APLL_FORCE_LOCK << __ffs(ad->enable_mask);58	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);59 60	state <<= __ffs(ad->idlest_mask);61 62	while (1) {63		v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);64		if ((v & ad->idlest_mask) == state)65			break;66		if (i > MAX_APLL_WAIT_TRIES)67			break;68		i++;69		udelay(1);70	}71 72	if (i == MAX_APLL_WAIT_TRIES) {73		pr_warn("clock: %s failed transition to '%s'\n",74			clk_name, (state) ? "locked" : "bypassed");75		r = -EBUSY;76	} else77		pr_debug("clock: %s transition to '%s' in %d loops\n",78			 clk_name, (state) ? "locked" : "bypassed", i);79 80	return r;81}82 83static void dra7_apll_disable(struct clk_hw *hw)84{85	struct clk_hw_omap *clk = to_clk_hw_omap(hw);86	struct dpll_data *ad;87	u8 state = 1;88	u32 v;89 90	ad = clk->dpll_data;91 92	state <<= __ffs(ad->idlest_mask);93 94	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);95	v &= ~ad->enable_mask;96	v |= APLL_AUTO_IDLE << __ffs(ad->enable_mask);97	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);98}99 100static int dra7_apll_is_enabled(struct clk_hw *hw)101{102	struct clk_hw_omap *clk = to_clk_hw_omap(hw);103	struct dpll_data *ad;104	u32 v;105 106	ad = clk->dpll_data;107 108	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);109	v &= ad->enable_mask;110 111	v >>= __ffs(ad->enable_mask);112 113	return v == APLL_AUTO_IDLE ? 0 : 1;114}115 116static u8 dra7_init_apll_parent(struct clk_hw *hw)117{118	return 0;119}120 121static const struct clk_ops apll_ck_ops = {122	.enable		= &dra7_apll_enable,123	.disable	= &dra7_apll_disable,124	.is_enabled	= &dra7_apll_is_enabled,125	.get_parent	= &dra7_init_apll_parent,126};127 128static void __init omap_clk_register_apll(void *user,129					  struct device_node *node)130{131	struct clk_hw *hw = user;132	struct clk_hw_omap *clk_hw = to_clk_hw_omap(hw);133	struct dpll_data *ad = clk_hw->dpll_data;134	const char *name;135	struct clk *clk;136	const struct clk_init_data *init = clk_hw->hw.init;137 138	clk = of_clk_get(node, 0);139	if (IS_ERR(clk)) {140		pr_debug("clk-ref for %pOFn not ready, retry\n",141			 node);142		if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))143			return;144 145		goto cleanup;146	}147 148	ad->clk_ref = __clk_get_hw(clk);149 150	clk = of_clk_get(node, 1);151	if (IS_ERR(clk)) {152		pr_debug("clk-bypass for %pOFn not ready, retry\n",153			 node);154		if (!ti_clk_retry_init(node, hw, omap_clk_register_apll))155			return;156 157		goto cleanup;158	}159 160	ad->clk_bypass = __clk_get_hw(clk);161 162	name = ti_dt_clk_name(node);163	clk = of_ti_clk_register_omap_hw(node, &clk_hw->hw, name);164	if (!IS_ERR(clk)) {165		of_clk_add_provider(node, of_clk_src_simple_get, clk);166		kfree(init->parent_names);167		kfree(init);168		return;169	}170 171cleanup:172	kfree(clk_hw->dpll_data);173	kfree(init->parent_names);174	kfree(init);175	kfree(clk_hw);176}177 178static void __init of_dra7_apll_setup(struct device_node *node)179{180	struct dpll_data *ad = NULL;181	struct clk_hw_omap *clk_hw = NULL;182	struct clk_init_data *init = NULL;183	const char **parent_names = NULL;184	int ret;185 186	ad = kzalloc(sizeof(*ad), GFP_KERNEL);187	clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);188	init = kzalloc(sizeof(*init), GFP_KERNEL);189	if (!ad || !clk_hw || !init)190		goto cleanup;191 192	clk_hw->dpll_data = ad;193	clk_hw->hw.init = init;194 195	init->name = ti_dt_clk_name(node);196	init->ops = &apll_ck_ops;197 198	init->num_parents = of_clk_get_parent_count(node);199	if (init->num_parents < 1) {200		pr_err("dra7 apll %pOFn must have parent(s)\n", node);201		goto cleanup;202	}203 204	parent_names = kcalloc(init->num_parents, sizeof(char *), GFP_KERNEL);205	if (!parent_names)206		goto cleanup;207 208	of_clk_parent_fill(node, parent_names, init->num_parents);209 210	init->parent_names = parent_names;211 212	ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);213	ret |= ti_clk_get_reg_addr(node, 1, &ad->idlest_reg);214 215	if (ret)216		goto cleanup;217 218	ad->idlest_mask = 0x1;219	ad->enable_mask = 0x3;220 221	omap_clk_register_apll(&clk_hw->hw, node);222	return;223 224cleanup:225	kfree(parent_names);226	kfree(ad);227	kfree(clk_hw);228	kfree(init);229}230CLK_OF_DECLARE(dra7_apll_clock, "ti,dra7-apll-clock", of_dra7_apll_setup);231 232#define OMAP2_EN_APLL_LOCKED	0x3233#define OMAP2_EN_APLL_STOPPED	0x0234 235static int omap2_apll_is_enabled(struct clk_hw *hw)236{237	struct clk_hw_omap *clk = to_clk_hw_omap(hw);238	struct dpll_data *ad = clk->dpll_data;239	u32 v;240 241	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);242	v &= ad->enable_mask;243 244	v >>= __ffs(ad->enable_mask);245 246	return v == OMAP2_EN_APLL_LOCKED ? 1 : 0;247}248 249static unsigned long omap2_apll_recalc(struct clk_hw *hw,250				       unsigned long parent_rate)251{252	struct clk_hw_omap *clk = to_clk_hw_omap(hw);253 254	if (omap2_apll_is_enabled(hw))255		return clk->fixed_rate;256 257	return 0;258}259 260static int omap2_apll_enable(struct clk_hw *hw)261{262	struct clk_hw_omap *clk = to_clk_hw_omap(hw);263	struct dpll_data *ad = clk->dpll_data;264	u32 v;265	int i = 0;266 267	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);268	v &= ~ad->enable_mask;269	v |= OMAP2_EN_APLL_LOCKED << __ffs(ad->enable_mask);270	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);271 272	while (1) {273		v = ti_clk_ll_ops->clk_readl(&ad->idlest_reg);274		if (v & ad->idlest_mask)275			break;276		if (i > MAX_APLL_WAIT_TRIES)277			break;278		i++;279		udelay(1);280	}281 282	if (i == MAX_APLL_WAIT_TRIES) {283		pr_warn("%s failed to transition to locked\n",284			clk_hw_get_name(&clk->hw));285		return -EBUSY;286	}287 288	return 0;289}290 291static void omap2_apll_disable(struct clk_hw *hw)292{293	struct clk_hw_omap *clk = to_clk_hw_omap(hw);294	struct dpll_data *ad = clk->dpll_data;295	u32 v;296 297	v = ti_clk_ll_ops->clk_readl(&ad->control_reg);298	v &= ~ad->enable_mask;299	v |= OMAP2_EN_APLL_STOPPED << __ffs(ad->enable_mask);300	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);301}302 303static const struct clk_ops omap2_apll_ops = {304	.enable		= &omap2_apll_enable,305	.disable	= &omap2_apll_disable,306	.is_enabled	= &omap2_apll_is_enabled,307	.recalc_rate	= &omap2_apll_recalc,308};309 310static void omap2_apll_set_autoidle(struct clk_hw_omap *clk, u32 val)311{312	struct dpll_data *ad = clk->dpll_data;313	u32 v;314 315	v = ti_clk_ll_ops->clk_readl(&ad->autoidle_reg);316	v &= ~ad->autoidle_mask;317	v |= val << __ffs(ad->autoidle_mask);318	ti_clk_ll_ops->clk_writel(v, &ad->control_reg);319}320 321#define OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP	0x3322#define OMAP2_APLL_AUTOIDLE_DISABLE		0x0323 324static void omap2_apll_allow_idle(struct clk_hw_omap *clk)325{326	omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_LOW_POWER_STOP);327}328 329static void omap2_apll_deny_idle(struct clk_hw_omap *clk)330{331	omap2_apll_set_autoidle(clk, OMAP2_APLL_AUTOIDLE_DISABLE);332}333 334static const struct clk_hw_omap_ops omap2_apll_hwops = {335	.allow_idle	= &omap2_apll_allow_idle,336	.deny_idle	= &omap2_apll_deny_idle,337};338 339static void __init of_omap2_apll_setup(struct device_node *node)340{341	struct dpll_data *ad = NULL;342	struct clk_hw_omap *clk_hw = NULL;343	struct clk_init_data *init = NULL;344	const char *name;345	struct clk *clk;346	const char *parent_name;347	u32 val;348	int ret;349 350	ad = kzalloc(sizeof(*ad), GFP_KERNEL);351	clk_hw = kzalloc(sizeof(*clk_hw), GFP_KERNEL);352	init = kzalloc(sizeof(*init), GFP_KERNEL);353 354	if (!ad || !clk_hw || !init)355		goto cleanup;356 357	clk_hw->dpll_data = ad;358	clk_hw->hw.init = init;359	init->ops = &omap2_apll_ops;360	name = ti_dt_clk_name(node);361	init->name = name;362	clk_hw->ops = &omap2_apll_hwops;363 364	init->num_parents = of_clk_get_parent_count(node);365	if (init->num_parents != 1) {366		pr_err("%pOFn must have one parent\n", node);367		goto cleanup;368	}369 370	parent_name = of_clk_get_parent_name(node, 0);371	init->parent_names = &parent_name;372 373	if (of_property_read_u32(node, "ti,clock-frequency", &val)) {374		pr_err("%pOFn missing clock-frequency\n", node);375		goto cleanup;376	}377	clk_hw->fixed_rate = val;378 379	clk_hw->enable_bit = ti_clk_get_legacy_bit_shift(node);380	ad->enable_mask = 0x3 << clk_hw->enable_bit;381	ad->autoidle_mask = 0x3 << clk_hw->enable_bit;382 383	if (of_property_read_u32(node, "ti,idlest-shift", &val)) {384		pr_err("%pOFn missing idlest-shift\n", node);385		goto cleanup;386	}387 388	ad->idlest_mask = 1 << val;389 390	ret = ti_clk_get_reg_addr(node, 0, &ad->control_reg);391	ret |= ti_clk_get_reg_addr(node, 1, &ad->autoidle_reg);392	ret |= ti_clk_get_reg_addr(node, 2, &ad->idlest_reg);393 394	if (ret)395		goto cleanup;396 397	name = ti_dt_clk_name(node);398	clk = of_ti_clk_register_omap_hw(node, &clk_hw->hw, name);399	if (!IS_ERR(clk)) {400		of_clk_add_provider(node, of_clk_src_simple_get, clk);401		kfree(init);402		return;403	}404cleanup:405	kfree(ad);406	kfree(clk_hw);407	kfree(init);408}409CLK_OF_DECLARE(omap2_apll_clock, "ti,omap2-apll-clock",410	       of_omap2_apll_setup);411