Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
0004  */
0005 
0006 #include <linux/clk.h>
0007 #include <linux/clk-provider.h>
0008 #include <linux/export.h>
0009 #include <linux/slab.h>
0010 #include <linux/err.h>
0011 
0012 #include "clk.h"
0013 
0014 static u8 clk_periph_get_parent(struct clk_hw *hw)
0015 {
0016     struct tegra_clk_periph *periph = to_clk_periph(hw);
0017     const struct clk_ops *mux_ops = periph->mux_ops;
0018     struct clk_hw *mux_hw = &periph->mux.hw;
0019 
0020     __clk_hw_set_clk(mux_hw, hw);
0021 
0022     return mux_ops->get_parent(mux_hw);
0023 }
0024 
0025 static int clk_periph_set_parent(struct clk_hw *hw, u8 index)
0026 {
0027     struct tegra_clk_periph *periph = to_clk_periph(hw);
0028     const struct clk_ops *mux_ops = periph->mux_ops;
0029     struct clk_hw *mux_hw = &periph->mux.hw;
0030 
0031     __clk_hw_set_clk(mux_hw, hw);
0032 
0033     return mux_ops->set_parent(mux_hw, index);
0034 }
0035 
0036 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw,
0037                         unsigned long parent_rate)
0038 {
0039     struct tegra_clk_periph *periph = to_clk_periph(hw);
0040     const struct clk_ops *div_ops = periph->div_ops;
0041     struct clk_hw *div_hw = &periph->divider.hw;
0042 
0043     __clk_hw_set_clk(div_hw, hw);
0044 
0045     return div_ops->recalc_rate(div_hw, parent_rate);
0046 }
0047 
0048 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate,
0049                   unsigned long *prate)
0050 {
0051     struct tegra_clk_periph *periph = to_clk_periph(hw);
0052     const struct clk_ops *div_ops = periph->div_ops;
0053     struct clk_hw *div_hw = &periph->divider.hw;
0054 
0055     __clk_hw_set_clk(div_hw, hw);
0056 
0057     return div_ops->round_rate(div_hw, rate, prate);
0058 }
0059 
0060 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate,
0061                    unsigned long parent_rate)
0062 {
0063     struct tegra_clk_periph *periph = to_clk_periph(hw);
0064     const struct clk_ops *div_ops = periph->div_ops;
0065     struct clk_hw *div_hw = &periph->divider.hw;
0066 
0067     __clk_hw_set_clk(div_hw, hw);
0068 
0069     return div_ops->set_rate(div_hw, rate, parent_rate);
0070 }
0071 
0072 static int clk_periph_is_enabled(struct clk_hw *hw)
0073 {
0074     struct tegra_clk_periph *periph = to_clk_periph(hw);
0075     const struct clk_ops *gate_ops = periph->gate_ops;
0076     struct clk_hw *gate_hw = &periph->gate.hw;
0077 
0078     __clk_hw_set_clk(gate_hw, hw);
0079 
0080     return gate_ops->is_enabled(gate_hw);
0081 }
0082 
0083 static int clk_periph_enable(struct clk_hw *hw)
0084 {
0085     struct tegra_clk_periph *periph = to_clk_periph(hw);
0086     const struct clk_ops *gate_ops = periph->gate_ops;
0087     struct clk_hw *gate_hw = &periph->gate.hw;
0088 
0089     __clk_hw_set_clk(gate_hw, hw);
0090 
0091     return gate_ops->enable(gate_hw);
0092 }
0093 
0094 static void clk_periph_disable(struct clk_hw *hw)
0095 {
0096     struct tegra_clk_periph *periph = to_clk_periph(hw);
0097     const struct clk_ops *gate_ops = periph->gate_ops;
0098     struct clk_hw *gate_hw = &periph->gate.hw;
0099 
0100     gate_ops->disable(gate_hw);
0101 }
0102 
0103 static void clk_periph_disable_unused(struct clk_hw *hw)
0104 {
0105     struct tegra_clk_periph *periph = to_clk_periph(hw);
0106     const struct clk_ops *gate_ops = periph->gate_ops;
0107     struct clk_hw *gate_hw = &periph->gate.hw;
0108 
0109     gate_ops->disable_unused(gate_hw);
0110 }
0111 
0112 static void clk_periph_restore_context(struct clk_hw *hw)
0113 {
0114     struct tegra_clk_periph *periph = to_clk_periph(hw);
0115     const struct clk_ops *div_ops = periph->div_ops;
0116     struct clk_hw *div_hw = &periph->divider.hw;
0117     int parent_id;
0118 
0119     parent_id = clk_hw_get_parent_index(hw);
0120     if (WARN_ON(parent_id < 0))
0121         return;
0122 
0123     if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV))
0124         div_ops->restore_context(div_hw);
0125 
0126     clk_periph_set_parent(hw, parent_id);
0127 }
0128 
0129 const struct clk_ops tegra_clk_periph_ops = {
0130     .get_parent = clk_periph_get_parent,
0131     .set_parent = clk_periph_set_parent,
0132     .recalc_rate = clk_periph_recalc_rate,
0133     .round_rate = clk_periph_round_rate,
0134     .set_rate = clk_periph_set_rate,
0135     .is_enabled = clk_periph_is_enabled,
0136     .enable = clk_periph_enable,
0137     .disable = clk_periph_disable,
0138     .disable_unused = clk_periph_disable_unused,
0139     .restore_context = clk_periph_restore_context,
0140 };
0141 
0142 static const struct clk_ops tegra_clk_periph_nodiv_ops = {
0143     .get_parent = clk_periph_get_parent,
0144     .set_parent = clk_periph_set_parent,
0145     .is_enabled = clk_periph_is_enabled,
0146     .enable = clk_periph_enable,
0147     .disable = clk_periph_disable,
0148     .disable_unused = clk_periph_disable_unused,
0149     .restore_context = clk_periph_restore_context,
0150 };
0151 
0152 static const struct clk_ops tegra_clk_periph_no_gate_ops = {
0153     .get_parent = clk_periph_get_parent,
0154     .set_parent = clk_periph_set_parent,
0155     .recalc_rate = clk_periph_recalc_rate,
0156     .round_rate = clk_periph_round_rate,
0157     .set_rate = clk_periph_set_rate,
0158     .restore_context = clk_periph_restore_context,
0159 };
0160 
0161 static struct clk *_tegra_clk_register_periph(const char *name,
0162             const char * const *parent_names, int num_parents,
0163             struct tegra_clk_periph *periph,
0164             void __iomem *clk_base, u32 offset,
0165             unsigned long flags)
0166 {
0167     struct clk *clk;
0168     struct clk_init_data init;
0169     const struct tegra_clk_periph_regs *bank;
0170     bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV);
0171 
0172     if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) {
0173         flags |= CLK_SET_RATE_PARENT;
0174         init.ops = &tegra_clk_periph_nodiv_ops;
0175     } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE)
0176         init.ops = &tegra_clk_periph_no_gate_ops;
0177     else
0178         init.ops = &tegra_clk_periph_ops;
0179 
0180     init.name = name;
0181     init.flags = flags;
0182     init.parent_names = parent_names;
0183     init.num_parents = num_parents;
0184 
0185     bank = get_reg_bank(periph->gate.clk_num);
0186     if (!bank)
0187         return ERR_PTR(-EINVAL);
0188 
0189     /* Data in .init is copied by clk_register(), so stack variable OK */
0190     periph->hw.init = &init;
0191     periph->magic = TEGRA_CLK_PERIPH_MAGIC;
0192     periph->mux.reg = clk_base + offset;
0193     periph->divider.reg = div ? (clk_base + offset) : NULL;
0194     periph->gate.clk_base = clk_base;
0195     periph->gate.regs = bank;
0196     periph->gate.enable_refcnt = periph_clk_enb_refcnt;
0197 
0198     clk = clk_register(NULL, &periph->hw);
0199     if (IS_ERR(clk))
0200         return clk;
0201 
0202     periph->mux.hw.clk = clk;
0203     periph->divider.hw.clk = div ? clk : NULL;
0204     periph->gate.hw.clk = clk;
0205 
0206     return clk;
0207 }
0208 
0209 struct clk *tegra_clk_register_periph(const char *name,
0210         const char * const *parent_names, int num_parents,
0211         struct tegra_clk_periph *periph, void __iomem *clk_base,
0212         u32 offset, unsigned long flags)
0213 {
0214     return _tegra_clk_register_periph(name, parent_names, num_parents,
0215             periph, clk_base, offset, flags);
0216 }
0217 
0218 struct clk *tegra_clk_register_periph_nodiv(const char *name,
0219         const char * const *parent_names, int num_parents,
0220         struct tegra_clk_periph *periph, void __iomem *clk_base,
0221         u32 offset)
0222 {
0223     periph->gate.flags |= TEGRA_PERIPH_NO_DIV;
0224     return _tegra_clk_register_periph(name, parent_names, num_parents,
0225             periph, clk_base, offset, CLK_SET_RATE_PARENT);
0226 }
0227 
0228 struct clk *tegra_clk_register_periph_data(void __iomem *clk_base,
0229                        struct tegra_periph_init_data *init)
0230 {
0231     return _tegra_clk_register_periph(init->name, init->p.parent_names,
0232                       init->num_parents, &init->periph,
0233                       clk_base, init->offset, init->flags);
0234 }