xref: /openbmc/linux/drivers/clk/tegra/clk-periph.c (revision 2612e3bbc0386368a850140a6c9b990cd496a5ec)
19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28f8f484bSPrashant Gaikwad /*
38f8f484bSPrashant Gaikwad  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
48f8f484bSPrashant Gaikwad  */
58f8f484bSPrashant Gaikwad 
62b8cfd6bSSowjanya Komatineni #include <linux/clk.h>
78f8f484bSPrashant Gaikwad #include <linux/clk-provider.h>
84dd59cddSThierry Reding #include <linux/export.h>
98f8f484bSPrashant Gaikwad #include <linux/slab.h>
108f8f484bSPrashant Gaikwad #include <linux/err.h>
118f8f484bSPrashant Gaikwad 
128f8f484bSPrashant Gaikwad #include "clk.h"
138f8f484bSPrashant Gaikwad 
clk_periph_get_parent(struct clk_hw * hw)148f8f484bSPrashant Gaikwad static u8 clk_periph_get_parent(struct clk_hw *hw)
158f8f484bSPrashant Gaikwad {
168f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
178f8f484bSPrashant Gaikwad 	const struct clk_ops *mux_ops = periph->mux_ops;
188f8f484bSPrashant Gaikwad 	struct clk_hw *mux_hw = &periph->mux.hw;
198f8f484bSPrashant Gaikwad 
204e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
218f8f484bSPrashant Gaikwad 
228f8f484bSPrashant Gaikwad 	return mux_ops->get_parent(mux_hw);
238f8f484bSPrashant Gaikwad }
248f8f484bSPrashant Gaikwad 
clk_periph_set_parent(struct clk_hw * hw,u8 index)258f8f484bSPrashant Gaikwad static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
268f8f484bSPrashant Gaikwad {
278f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
288f8f484bSPrashant Gaikwad 	const struct clk_ops *mux_ops = periph->mux_ops;
298f8f484bSPrashant Gaikwad 	struct clk_hw *mux_hw = &periph->mux.hw;
308f8f484bSPrashant Gaikwad 
314e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(mux_hw, hw);
328f8f484bSPrashant Gaikwad 
338f8f484bSPrashant Gaikwad 	return mux_ops->set_parent(mux_hw, index);
348f8f484bSPrashant Gaikwad }
358f8f484bSPrashant Gaikwad 
clk_periph_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)368f8f484bSPrashant Gaikwad static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
378f8f484bSPrashant Gaikwad 					    unsigned long parent_rate)
388f8f484bSPrashant Gaikwad {
398f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
408f8f484bSPrashant Gaikwad 	const struct clk_ops *div_ops = periph->div_ops;
418f8f484bSPrashant Gaikwad 	struct clk_hw *div_hw = &periph->divider.hw;
428f8f484bSPrashant Gaikwad 
434e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
448f8f484bSPrashant Gaikwad 
458f8f484bSPrashant Gaikwad 	return div_ops->recalc_rate(div_hw, parent_rate);
468f8f484bSPrashant Gaikwad }
478f8f484bSPrashant Gaikwad 
clk_periph_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)48*4d78bd80SMaxime Ripard static int clk_periph_determine_rate(struct clk_hw *hw,
49*4d78bd80SMaxime Ripard 				     struct clk_rate_request *req)
508f8f484bSPrashant Gaikwad {
518f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
528f8f484bSPrashant Gaikwad 	const struct clk_ops *div_ops = periph->div_ops;
538f8f484bSPrashant Gaikwad 	struct clk_hw *div_hw = &periph->divider.hw;
54*4d78bd80SMaxime Ripard 	unsigned long rate;
558f8f484bSPrashant Gaikwad 
564e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
578f8f484bSPrashant Gaikwad 
58*4d78bd80SMaxime Ripard 	rate = div_ops->round_rate(div_hw, req->rate, &req->best_parent_rate);
59*4d78bd80SMaxime Ripard 	if (rate < 0)
60*4d78bd80SMaxime Ripard 		return rate;
61*4d78bd80SMaxime Ripard 
62*4d78bd80SMaxime Ripard 	req->rate = rate;
63*4d78bd80SMaxime Ripard 	return 0;
648f8f484bSPrashant Gaikwad }
658f8f484bSPrashant Gaikwad 
clk_periph_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)668f8f484bSPrashant Gaikwad static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
678f8f484bSPrashant Gaikwad 			       unsigned long parent_rate)
688f8f484bSPrashant Gaikwad {
698f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
708f8f484bSPrashant Gaikwad 	const struct clk_ops *div_ops = periph->div_ops;
718f8f484bSPrashant Gaikwad 	struct clk_hw *div_hw = &periph->divider.hw;
728f8f484bSPrashant Gaikwad 
734e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(div_hw, hw);
748f8f484bSPrashant Gaikwad 
758f8f484bSPrashant Gaikwad 	return div_ops->set_rate(div_hw, rate, parent_rate);
768f8f484bSPrashant Gaikwad }
778f8f484bSPrashant Gaikwad 
clk_periph_is_enabled(struct clk_hw * hw)788f8f484bSPrashant Gaikwad static int clk_periph_is_enabled(struct clk_hw *hw)
798f8f484bSPrashant Gaikwad {
808f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
818f8f484bSPrashant Gaikwad 	const struct clk_ops *gate_ops = periph->gate_ops;
828f8f484bSPrashant Gaikwad 	struct clk_hw *gate_hw = &periph->gate.hw;
838f8f484bSPrashant Gaikwad 
844e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(gate_hw, hw);
858f8f484bSPrashant Gaikwad 
868f8f484bSPrashant Gaikwad 	return gate_ops->is_enabled(gate_hw);
878f8f484bSPrashant Gaikwad }
888f8f484bSPrashant Gaikwad 
clk_periph_enable(struct clk_hw * hw)898f8f484bSPrashant Gaikwad static int clk_periph_enable(struct clk_hw *hw)
908f8f484bSPrashant Gaikwad {
918f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
928f8f484bSPrashant Gaikwad 	const struct clk_ops *gate_ops = periph->gate_ops;
938f8f484bSPrashant Gaikwad 	struct clk_hw *gate_hw = &periph->gate.hw;
948f8f484bSPrashant Gaikwad 
954e907ef6SJavier Martinez Canillas 	__clk_hw_set_clk(gate_hw, hw);
968f8f484bSPrashant Gaikwad 
978f8f484bSPrashant Gaikwad 	return gate_ops->enable(gate_hw);
988f8f484bSPrashant Gaikwad }
998f8f484bSPrashant Gaikwad 
clk_periph_disable(struct clk_hw * hw)1008f8f484bSPrashant Gaikwad static void clk_periph_disable(struct clk_hw *hw)
1018f8f484bSPrashant Gaikwad {
1028f8f484bSPrashant Gaikwad 	struct tegra_clk_periph *periph = to_clk_periph(hw);
1038f8f484bSPrashant Gaikwad 	const struct clk_ops *gate_ops = periph->gate_ops;
1048f8f484bSPrashant Gaikwad 	struct clk_hw *gate_hw = &periph->gate.hw;
1058f8f484bSPrashant Gaikwad 
1068f8f484bSPrashant Gaikwad 	gate_ops->disable(gate_hw);
1078f8f484bSPrashant Gaikwad }
1088f8f484bSPrashant Gaikwad 
clk_periph_disable_unused(struct clk_hw * hw)109c592c8a2SDmitry Osipenko static void clk_periph_disable_unused(struct clk_hw *hw)
110c592c8a2SDmitry Osipenko {
111c592c8a2SDmitry Osipenko 	struct tegra_clk_periph *periph = to_clk_periph(hw);
112c592c8a2SDmitry Osipenko 	const struct clk_ops *gate_ops = periph->gate_ops;
113c592c8a2SDmitry Osipenko 	struct clk_hw *gate_hw = &periph->gate.hw;
114c592c8a2SDmitry Osipenko 
115c592c8a2SDmitry Osipenko 	gate_ops->disable_unused(gate_hw);
116c592c8a2SDmitry Osipenko }
117c592c8a2SDmitry Osipenko 
clk_periph_restore_context(struct clk_hw * hw)1182b8cfd6bSSowjanya Komatineni static void clk_periph_restore_context(struct clk_hw *hw)
1192b8cfd6bSSowjanya Komatineni {
1202b8cfd6bSSowjanya Komatineni 	struct tegra_clk_periph *periph = to_clk_periph(hw);
1212b8cfd6bSSowjanya Komatineni 	const struct clk_ops *div_ops = periph->div_ops;
1222b8cfd6bSSowjanya Komatineni 	struct clk_hw *div_hw = &periph->divider.hw;
1232b8cfd6bSSowjanya Komatineni 	int parent_id;
1242b8cfd6bSSowjanya Komatineni 
1252b8cfd6bSSowjanya Komatineni 	parent_id = clk_hw_get_parent_index(hw);
1262b8cfd6bSSowjanya Komatineni 	if (WARN_ON(parent_id < 0))
1272b8cfd6bSSowjanya Komatineni 		return;
1282b8cfd6bSSowjanya Komatineni 
1292b8cfd6bSSowjanya Komatineni 	if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
1302b8cfd6bSSowjanya Komatineni 		div_ops->restore_context(div_hw);
1312b8cfd6bSSowjanya Komatineni 
1322b8cfd6bSSowjanya Komatineni 	clk_periph_set_parent(hw, parent_id);
1332b8cfd6bSSowjanya Komatineni }
1342b8cfd6bSSowjanya Komatineni 
1358f8f484bSPrashant Gaikwad const struct clk_ops tegra_clk_periph_ops = {
1368f8f484bSPrashant Gaikwad 	.get_parent = clk_periph_get_parent,
1378f8f484bSPrashant Gaikwad 	.set_parent = clk_periph_set_parent,
1388f8f484bSPrashant Gaikwad 	.recalc_rate = clk_periph_recalc_rate,
139*4d78bd80SMaxime Ripard 	.determine_rate = clk_periph_determine_rate,
1408f8f484bSPrashant Gaikwad 	.set_rate = clk_periph_set_rate,
1418f8f484bSPrashant Gaikwad 	.is_enabled = clk_periph_is_enabled,
1428f8f484bSPrashant Gaikwad 	.enable = clk_periph_enable,
1438f8f484bSPrashant Gaikwad 	.disable = clk_periph_disable,
144c592c8a2SDmitry Osipenko 	.disable_unused = clk_periph_disable_unused,
1452b8cfd6bSSowjanya Komatineni 	.restore_context = clk_periph_restore_context,
1468f8f484bSPrashant Gaikwad };
1478f8f484bSPrashant Gaikwad 
1484e100354SSachin Kamat static const struct clk_ops tegra_clk_periph_nodiv_ops = {
149b11fcfa8SMaxime Ripard 	.determine_rate = clk_hw_determine_rate_no_reparent,
1508f8f484bSPrashant Gaikwad 	.get_parent = clk_periph_get_parent,
1518f8f484bSPrashant Gaikwad 	.set_parent = clk_periph_set_parent,
1528f8f484bSPrashant Gaikwad 	.is_enabled = clk_periph_is_enabled,
1538f8f484bSPrashant Gaikwad 	.enable = clk_periph_enable,
1548f8f484bSPrashant Gaikwad 	.disable = clk_periph_disable,
155c592c8a2SDmitry Osipenko 	.disable_unused = clk_periph_disable_unused,
1562b8cfd6bSSowjanya Komatineni 	.restore_context = clk_periph_restore_context,
1578f8f484bSPrashant Gaikwad };
1588f8f484bSPrashant Gaikwad 
15922e5de81SSachin Kamat static const struct clk_ops tegra_clk_periph_no_gate_ops = {
160b29f9e92SPeter De Schrijver 	.get_parent = clk_periph_get_parent,
161b29f9e92SPeter De Schrijver 	.set_parent = clk_periph_set_parent,
162b29f9e92SPeter De Schrijver 	.recalc_rate = clk_periph_recalc_rate,
163*4d78bd80SMaxime Ripard 	.determine_rate = clk_periph_determine_rate,
164b29f9e92SPeter De Schrijver 	.set_rate = clk_periph_set_rate,
1652b8cfd6bSSowjanya Komatineni 	.restore_context = clk_periph_restore_context,
166b29f9e92SPeter De Schrijver };
167b29f9e92SPeter De Schrijver 
_tegra_clk_register_periph(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset,unsigned long flags)1688f8f484bSPrashant Gaikwad static struct clk *_tegra_clk_register_periph(const char *name,
1699e8c93edSPeter De Schrijver 			const char * const *parent_names, int num_parents,
1708f8f484bSPrashant Gaikwad 			struct tegra_clk_periph *periph,
1715bb9d267SPeter De Schrijver 			void __iomem *clk_base, u32 offset,
172a26a0298SPeter De Schrijver 			unsigned long flags)
1738f8f484bSPrashant Gaikwad {
1748f8f484bSPrashant Gaikwad 	struct clk *clk;
1758f8f484bSPrashant Gaikwad 	struct clk_init_data init;
1767e14f223SThierry Reding 	const struct tegra_clk_periph_regs *bank;
1775bb9d267SPeter De Schrijver 	bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
1785bb9d267SPeter De Schrijver 
179b29f9e92SPeter De Schrijver 	if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
180b29f9e92SPeter De Schrijver 		flags |= CLK_SET_RATE_PARENT;
181b29f9e92SPeter De Schrijver 		init.ops = &tegra_clk_periph_nodiv_ops;
182b29f9e92SPeter De Schrijver 	} else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
183b29f9e92SPeter De Schrijver 		init.ops = &tegra_clk_periph_no_gate_ops;
184b29f9e92SPeter De Schrijver 	else
185b29f9e92SPeter De Schrijver 		init.ops = &tegra_clk_periph_ops;
1868f8f484bSPrashant Gaikwad 
1878f8f484bSPrashant Gaikwad 	init.name = name;
188a26a0298SPeter De Schrijver 	init.flags = flags;
1898f8f484bSPrashant Gaikwad 	init.parent_names = parent_names;
1908f8f484bSPrashant Gaikwad 	init.num_parents = num_parents;
1918f8f484bSPrashant Gaikwad 
192d5ff89a8SPeter De Schrijver 	bank = get_reg_bank(periph->gate.clk_num);
193d5ff89a8SPeter De Schrijver 	if (!bank)
194d5ff89a8SPeter De Schrijver 		return ERR_PTR(-EINVAL);
195d5ff89a8SPeter De Schrijver 
1968f8f484bSPrashant Gaikwad 	/* Data in .init is copied by clk_register(), so stack variable OK */
1978f8f484bSPrashant Gaikwad 	periph->hw.init = &init;
1988f8f484bSPrashant Gaikwad 	periph->magic = TEGRA_CLK_PERIPH_MAGIC;
1998f8f484bSPrashant Gaikwad 	periph->mux.reg = clk_base + offset;
2008f8f484bSPrashant Gaikwad 	periph->divider.reg = div ? (clk_base + offset) : NULL;
2018f8f484bSPrashant Gaikwad 	periph->gate.clk_base = clk_base;
202d5ff89a8SPeter De Schrijver 	periph->gate.regs = bank;
203343a607cSPeter De Schrijver 	periph->gate.enable_refcnt = periph_clk_enb_refcnt;
2048f8f484bSPrashant Gaikwad 
2058f8f484bSPrashant Gaikwad 	clk = clk_register(NULL, &periph->hw);
2068f8f484bSPrashant Gaikwad 	if (IS_ERR(clk))
2078f8f484bSPrashant Gaikwad 		return clk;
2088f8f484bSPrashant Gaikwad 
2098f8f484bSPrashant Gaikwad 	periph->mux.hw.clk = clk;
2108f8f484bSPrashant Gaikwad 	periph->divider.hw.clk = div ? clk : NULL;
2118f8f484bSPrashant Gaikwad 	periph->gate.hw.clk = clk;
2128f8f484bSPrashant Gaikwad 
2138f8f484bSPrashant Gaikwad 	return clk;
2148f8f484bSPrashant Gaikwad }
2158f8f484bSPrashant Gaikwad 
tegra_clk_register_periph(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset,unsigned long flags)2168f8f484bSPrashant Gaikwad struct clk *tegra_clk_register_periph(const char *name,
2179e8c93edSPeter De Schrijver 		const char * const *parent_names, int num_parents,
2188f8f484bSPrashant Gaikwad 		struct tegra_clk_periph *periph, void __iomem *clk_base,
219a26a0298SPeter De Schrijver 		u32 offset, unsigned long flags)
2208f8f484bSPrashant Gaikwad {
2218f8f484bSPrashant Gaikwad 	return _tegra_clk_register_periph(name, parent_names, num_parents,
2225bb9d267SPeter De Schrijver 			periph, clk_base, offset, flags);
2238f8f484bSPrashant Gaikwad }
2248f8f484bSPrashant Gaikwad 
tegra_clk_register_periph_nodiv(const char * name,const char * const * parent_names,int num_parents,struct tegra_clk_periph * periph,void __iomem * clk_base,u32 offset)2258f8f484bSPrashant Gaikwad struct clk *tegra_clk_register_periph_nodiv(const char *name,
22639133505SThierry Reding 		const char * const *parent_names, int num_parents,
2278f8f484bSPrashant Gaikwad 		struct tegra_clk_periph *periph, void __iomem *clk_base,
2288f8f484bSPrashant Gaikwad 		u32 offset)
2298f8f484bSPrashant Gaikwad {
2305bb9d267SPeter De Schrijver 	periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
2318f8f484bSPrashant Gaikwad 	return _tegra_clk_register_periph(name, parent_names, num_parents,
2325bb9d267SPeter De Schrijver 			periph, clk_base, offset, CLK_SET_RATE_PARENT);
2338f8f484bSPrashant Gaikwad }
2348be95190SThierry Reding 
tegra_clk_register_periph_data(void __iomem * clk_base,struct tegra_periph_init_data * init)2358be95190SThierry Reding struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
2368be95190SThierry Reding 					   struct tegra_periph_init_data *init)
2378be95190SThierry Reding {
2388be95190SThierry Reding 	return _tegra_clk_register_periph(init->name, init->p.parent_names,
2398be95190SThierry Reding 					  init->num_parents, &init->periph,
2408be95190SThierry Reding 					  clk_base, init->offset, init->flags);
2418be95190SThierry Reding }
242