1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/clk-provider.h> 8 #include <linux/export.h> 9 #include <linux/slab.h> 10 #include <linux/err.h> 11 12 #include "clk.h" 13 14 static u8 clk_periph_get_parent(struct clk_hw *hw) 15 { 16 struct tegra_clk_periph *periph = to_clk_periph(hw); 17 const struct clk_ops *mux_ops = periph->mux_ops; 18 struct clk_hw *mux_hw = &periph->mux.hw; 19 20 __clk_hw_set_clk(mux_hw, hw); 21 22 return mux_ops->get_parent(mux_hw); 23 } 24 25 static int clk_periph_set_parent(struct clk_hw *hw, u8 index) 26 { 27 struct tegra_clk_periph *periph = to_clk_periph(hw); 28 const struct clk_ops *mux_ops = periph->mux_ops; 29 struct clk_hw *mux_hw = &periph->mux.hw; 30 31 __clk_hw_set_clk(mux_hw, hw); 32 33 return mux_ops->set_parent(mux_hw, index); 34 } 35 36 static unsigned long clk_periph_recalc_rate(struct clk_hw *hw, 37 unsigned long parent_rate) 38 { 39 struct tegra_clk_periph *periph = to_clk_periph(hw); 40 const struct clk_ops *div_ops = periph->div_ops; 41 struct clk_hw *div_hw = &periph->divider.hw; 42 43 __clk_hw_set_clk(div_hw, hw); 44 45 return div_ops->recalc_rate(div_hw, parent_rate); 46 } 47 48 static long clk_periph_round_rate(struct clk_hw *hw, unsigned long rate, 49 unsigned long *prate) 50 { 51 struct tegra_clk_periph *periph = to_clk_periph(hw); 52 const struct clk_ops *div_ops = periph->div_ops; 53 struct clk_hw *div_hw = &periph->divider.hw; 54 55 __clk_hw_set_clk(div_hw, hw); 56 57 return div_ops->round_rate(div_hw, rate, prate); 58 } 59 60 static int clk_periph_set_rate(struct clk_hw *hw, unsigned long rate, 61 unsigned long parent_rate) 62 { 63 struct tegra_clk_periph *periph = to_clk_periph(hw); 64 const struct clk_ops *div_ops = periph->div_ops; 65 struct clk_hw *div_hw = &periph->divider.hw; 66 67 __clk_hw_set_clk(div_hw, hw); 68 69 return div_ops->set_rate(div_hw, rate, parent_rate); 70 } 71 72 static int clk_periph_is_enabled(struct clk_hw *hw) 73 { 74 struct tegra_clk_periph *periph = to_clk_periph(hw); 75 const struct clk_ops *gate_ops = periph->gate_ops; 76 struct clk_hw *gate_hw = &periph->gate.hw; 77 78 __clk_hw_set_clk(gate_hw, hw); 79 80 return gate_ops->is_enabled(gate_hw); 81 } 82 83 static int clk_periph_enable(struct clk_hw *hw) 84 { 85 struct tegra_clk_periph *periph = to_clk_periph(hw); 86 const struct clk_ops *gate_ops = periph->gate_ops; 87 struct clk_hw *gate_hw = &periph->gate.hw; 88 89 __clk_hw_set_clk(gate_hw, hw); 90 91 return gate_ops->enable(gate_hw); 92 } 93 94 static void clk_periph_disable(struct clk_hw *hw) 95 { 96 struct tegra_clk_periph *periph = to_clk_periph(hw); 97 const struct clk_ops *gate_ops = periph->gate_ops; 98 struct clk_hw *gate_hw = &periph->gate.hw; 99 100 gate_ops->disable(gate_hw); 101 } 102 103 static void clk_periph_disable_unused(struct clk_hw *hw) 104 { 105 struct tegra_clk_periph *periph = to_clk_periph(hw); 106 const struct clk_ops *gate_ops = periph->gate_ops; 107 struct clk_hw *gate_hw = &periph->gate.hw; 108 109 gate_ops->disable_unused(gate_hw); 110 } 111 112 static void clk_periph_restore_context(struct clk_hw *hw) 113 { 114 struct tegra_clk_periph *periph = to_clk_periph(hw); 115 const struct clk_ops *div_ops = periph->div_ops; 116 struct clk_hw *div_hw = &periph->divider.hw; 117 int parent_id; 118 119 parent_id = clk_hw_get_parent_index(hw); 120 if (WARN_ON(parent_id < 0)) 121 return; 122 123 if (!(periph->gate.flags & TEGRA_PERIPH_NO_DIV)) 124 div_ops->restore_context(div_hw); 125 126 clk_periph_set_parent(hw, parent_id); 127 } 128 129 const struct clk_ops tegra_clk_periph_ops = { 130 .get_parent = clk_periph_get_parent, 131 .set_parent = clk_periph_set_parent, 132 .recalc_rate = clk_periph_recalc_rate, 133 .round_rate = clk_periph_round_rate, 134 .set_rate = clk_periph_set_rate, 135 .is_enabled = clk_periph_is_enabled, 136 .enable = clk_periph_enable, 137 .disable = clk_periph_disable, 138 .disable_unused = clk_periph_disable_unused, 139 .restore_context = clk_periph_restore_context, 140 }; 141 142 static const struct clk_ops tegra_clk_periph_nodiv_ops = { 143 .determine_rate = clk_hw_determine_rate_no_reparent, 144 .get_parent = clk_periph_get_parent, 145 .set_parent = clk_periph_set_parent, 146 .is_enabled = clk_periph_is_enabled, 147 .enable = clk_periph_enable, 148 .disable = clk_periph_disable, 149 .disable_unused = clk_periph_disable_unused, 150 .restore_context = clk_periph_restore_context, 151 }; 152 153 static const struct clk_ops tegra_clk_periph_no_gate_ops = { 154 .get_parent = clk_periph_get_parent, 155 .set_parent = clk_periph_set_parent, 156 .recalc_rate = clk_periph_recalc_rate, 157 .round_rate = clk_periph_round_rate, 158 .set_rate = clk_periph_set_rate, 159 .restore_context = clk_periph_restore_context, 160 }; 161 162 static struct clk *_tegra_clk_register_periph(const char *name, 163 const char * const *parent_names, int num_parents, 164 struct tegra_clk_periph *periph, 165 void __iomem *clk_base, u32 offset, 166 unsigned long flags) 167 { 168 struct clk *clk; 169 struct clk_init_data init; 170 const struct tegra_clk_periph_regs *bank; 171 bool div = !(periph->gate.flags & TEGRA_PERIPH_NO_DIV); 172 173 if (periph->gate.flags & TEGRA_PERIPH_NO_DIV) { 174 flags |= CLK_SET_RATE_PARENT; 175 init.ops = &tegra_clk_periph_nodiv_ops; 176 } else if (periph->gate.flags & TEGRA_PERIPH_NO_GATE) 177 init.ops = &tegra_clk_periph_no_gate_ops; 178 else 179 init.ops = &tegra_clk_periph_ops; 180 181 init.name = name; 182 init.flags = flags; 183 init.parent_names = parent_names; 184 init.num_parents = num_parents; 185 186 bank = get_reg_bank(periph->gate.clk_num); 187 if (!bank) 188 return ERR_PTR(-EINVAL); 189 190 /* Data in .init is copied by clk_register(), so stack variable OK */ 191 periph->hw.init = &init; 192 periph->magic = TEGRA_CLK_PERIPH_MAGIC; 193 periph->mux.reg = clk_base + offset; 194 periph->divider.reg = div ? (clk_base + offset) : NULL; 195 periph->gate.clk_base = clk_base; 196 periph->gate.regs = bank; 197 periph->gate.enable_refcnt = periph_clk_enb_refcnt; 198 199 clk = clk_register(NULL, &periph->hw); 200 if (IS_ERR(clk)) 201 return clk; 202 203 periph->mux.hw.clk = clk; 204 periph->divider.hw.clk = div ? clk : NULL; 205 periph->gate.hw.clk = clk; 206 207 return clk; 208 } 209 210 struct clk *tegra_clk_register_periph(const char *name, 211 const char * const *parent_names, int num_parents, 212 struct tegra_clk_periph *periph, void __iomem *clk_base, 213 u32 offset, unsigned long flags) 214 { 215 return _tegra_clk_register_periph(name, parent_names, num_parents, 216 periph, clk_base, offset, flags); 217 } 218 219 struct clk *tegra_clk_register_periph_nodiv(const char *name, 220 const char * const *parent_names, int num_parents, 221 struct tegra_clk_periph *periph, void __iomem *clk_base, 222 u32 offset) 223 { 224 periph->gate.flags |= TEGRA_PERIPH_NO_DIV; 225 return _tegra_clk_register_periph(name, parent_names, num_parents, 226 periph, clk_base, offset, CLK_SET_RATE_PARENT); 227 } 228 229 struct clk *tegra_clk_register_periph_data(void __iomem *clk_base, 230 struct tegra_periph_init_data *init) 231 { 232 return _tegra_clk_register_periph(init->name, init->p.parent_names, 233 init->num_parents, &init->periph, 234 clk_base, init->offset, init->flags); 235 } 236