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